1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.fuelgauge;
18 
19 import android.app.settings.SettingsEnums;
20 import android.content.Context;
21 import android.content.pm.ApplicationInfo;
22 import android.content.pm.PackageManager;
23 import android.os.Bundle;
24 import android.os.UserHandle;
25 import android.util.IconDrawableFactory;
26 import android.util.Log;
27 import android.util.SparseLongArray;
28 
29 import androidx.annotation.VisibleForTesting;
30 import androidx.preference.CheckBoxPreference;
31 import androidx.preference.Preference;
32 import androidx.preference.PreferenceGroup;
33 
34 import com.android.settings.R;
35 import com.android.settings.Utils;
36 import com.android.settings.core.InstrumentedPreferenceFragment;
37 import com.android.settings.core.SubSettingLauncher;
38 import com.android.settings.dashboard.DashboardFragment;
39 import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
40 import com.android.settings.fuelgauge.batterytip.AppInfo;
41 import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager;
42 import com.android.settings.fuelgauge.batterytip.BatteryTipDialogFragment;
43 import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
44 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
45 import com.android.settings.fuelgauge.batterytip.tips.RestrictAppTip;
46 import com.android.settings.fuelgauge.batterytip.tips.UnrestrictAppTip;
47 import com.android.settings.overlay.FeatureFactory;
48 import com.android.settings.widget.AppCheckBoxPreference;
49 import com.android.settingslib.core.AbstractPreferenceController;
50 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
51 import com.android.settingslib.utils.StringUtil;
52 
53 import java.util.List;
54 
55 /** Fragment to show a list of anomaly apps, where user could handle these anomalies */
56 public class RestrictedAppDetails extends DashboardFragment
57         implements BatteryTipPreferenceController.BatteryTipListener {
58 
59     public static final String TAG = "RestrictedAppDetails";
60 
61     @VisibleForTesting static final String EXTRA_APP_INFO_LIST = "app_info_list";
62     private static final String KEY_PREF_RESTRICTED_APP_LIST = "restrict_app_list";
63     private static final long TIME_NULL = -1;
64 
65     @VisibleForTesting List<AppInfo> mAppInfos;
66     @VisibleForTesting IconDrawableFactory mIconDrawableFactory;
67     @VisibleForTesting PreferenceGroup mRestrictedAppListGroup;
68     @VisibleForTesting BatteryUtils mBatteryUtils;
69     @VisibleForTesting PackageManager mPackageManager;
70     @VisibleForTesting BatteryDatabaseManager mBatteryDatabaseManager;
71 
72     private MetricsFeatureProvider mMetricsFeatureProvider;
73 
74     /** Starts restricted app details page */
startRestrictedAppDetails( InstrumentedPreferenceFragment fragment, List<AppInfo> appInfos)75     public static void startRestrictedAppDetails(
76             InstrumentedPreferenceFragment fragment, List<AppInfo> appInfos) {
77         final Bundle args = new Bundle();
78         args.putParcelableList(EXTRA_APP_INFO_LIST, appInfos);
79 
80         new SubSettingLauncher(fragment.getContext())
81                 .setDestination(RestrictedAppDetails.class.getName())
82                 .setArguments(args)
83                 .setTitleRes(R.string.restricted_app_title)
84                 .setSourceMetricsCategory(fragment.getMetricsCategory())
85                 .launch();
86     }
87 
88     @Override
onCreate(Bundle icicle)89     public void onCreate(Bundle icicle) {
90         super.onCreate(icicle);
91         final Context context = getContext();
92 
93         mRestrictedAppListGroup = (PreferenceGroup) findPreference(KEY_PREF_RESTRICTED_APP_LIST);
94         mAppInfos = getArguments().getParcelableArrayList(EXTRA_APP_INFO_LIST);
95         mPackageManager = context.getPackageManager();
96         mIconDrawableFactory = IconDrawableFactory.newInstance(context);
97         mBatteryUtils = BatteryUtils.getInstance(context);
98         mBatteryDatabaseManager = BatteryDatabaseManager.getInstance(context);
99         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
100         refreshUi();
101     }
102 
103     @Override
onPreferenceTreeClick(Preference preference)104     public boolean onPreferenceTreeClick(Preference preference) {
105 
106         return super.onPreferenceTreeClick(preference);
107     }
108 
109     @Override
getLogTag()110     protected String getLogTag() {
111         return TAG;
112     }
113 
114     @Override
getPreferenceScreenResId()115     protected int getPreferenceScreenResId() {
116         return R.xml.restricted_apps_detail;
117     }
118 
119     @Override
createPreferenceControllers(Context context)120     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
121         return null;
122     }
123 
124     @Override
getMetricsCategory()125     public int getMetricsCategory() {
126         return SettingsEnums.FUELGAUGE_RESTRICTED_APP_DETAILS;
127     }
128 
129     @Override
getHelpResource()130     public int getHelpResource() {
131         return R.string.help_uri_restricted_apps;
132     }
133 
134     @VisibleForTesting
refreshUi()135     void refreshUi() {
136         mRestrictedAppListGroup.removeAll();
137         final Context context = getPrefContext();
138         final SparseLongArray timestampArray =
139                 mBatteryDatabaseManager.queryActionTime(
140                         AnomalyDatabaseHelper.ActionType.RESTRICTION);
141         final long now = System.currentTimeMillis();
142 
143         for (int i = 0, size = mAppInfos.size(); i < size; i++) {
144             final CheckBoxPreference checkBoxPreference = new AppCheckBoxPreference(context);
145             final AppInfo appInfo = mAppInfos.get(i);
146             try {
147                 final ApplicationInfo applicationInfo =
148                         mPackageManager.getApplicationInfoAsUser(
149                                 appInfo.packageName,
150                                 0 /* flags */,
151                                 UserHandle.getUserId(appInfo.uid));
152                 checkBoxPreference.setChecked(
153                         mBatteryUtils.isForceAppStandbyEnabled(appInfo.uid, appInfo.packageName));
154                 checkBoxPreference.setTitle(mPackageManager.getApplicationLabel(applicationInfo));
155                 checkBoxPreference.setIcon(
156                         Utils.getBadgedIcon(
157                                 mIconDrawableFactory,
158                                 mPackageManager,
159                                 appInfo.packageName,
160                                 UserHandle.getUserId(appInfo.uid)));
161                 checkBoxPreference.setKey(getKeyFromAppInfo(appInfo));
162                 checkBoxPreference.setOnPreferenceChangeListener(
163                         (pref, value) -> {
164                             final BatteryTipDialogFragment fragment =
165                                     createDialogFragment(appInfo, (Boolean) value);
166                             fragment.setTargetFragment(this, 0 /* requestCode */);
167                             fragment.show(getFragmentManager(), TAG);
168                             mMetricsFeatureProvider.action(
169                                     getContext(),
170                                     SettingsEnums.ACTION_APP_RESTRICTED_LIST_UNCHECKED,
171                                     appInfo.packageName);
172                             return false;
173                         });
174 
175                 final long timestamp = timestampArray.get(appInfo.uid, TIME_NULL);
176                 if (timestamp != TIME_NULL) {
177                     checkBoxPreference.setSummary(
178                             getString(
179                                     R.string.restricted_app_time_summary,
180                                     StringUtil.formatRelativeTime(
181                                             context, now - timestamp, false)));
182                 }
183                 final CharSequence test = checkBoxPreference.getSummaryOn();
184                 mRestrictedAppListGroup.addPreference(checkBoxPreference);
185             } catch (PackageManager.NameNotFoundException e) {
186                 Log.e(TAG, "Can't find package: " + appInfo.packageName);
187             }
188         }
189     }
190 
191     @Override
onBatteryTipHandled(BatteryTip batteryTip)192     public void onBatteryTipHandled(BatteryTip batteryTip) {
193         final AppInfo appInfo;
194         final boolean isRestricted = batteryTip instanceof RestrictAppTip;
195         if (isRestricted) {
196             appInfo = ((RestrictAppTip) batteryTip).getRestrictAppList().get(0);
197         } else {
198             appInfo = ((UnrestrictAppTip) batteryTip).getUnrestrictAppInfo();
199         }
200 
201         CheckBoxPreference preference =
202                 (CheckBoxPreference)
203                         mRestrictedAppListGroup.findPreference(getKeyFromAppInfo(appInfo));
204         if (preference != null) {
205             preference.setChecked(isRestricted);
206         }
207     }
208 
209     @VisibleForTesting
createDialogFragment(AppInfo appInfo, boolean toRestrict)210     BatteryTipDialogFragment createDialogFragment(AppInfo appInfo, boolean toRestrict) {
211         final BatteryTip batteryTip =
212                 toRestrict
213                         ? new RestrictAppTip(BatteryTip.StateType.NEW, appInfo)
214                         : new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo);
215 
216         return BatteryTipDialogFragment.newInstance(batteryTip, getMetricsCategory());
217     }
218 
219     @VisibleForTesting
getKeyFromAppInfo(AppInfo appInfo)220     String getKeyFromAppInfo(AppInfo appInfo) {
221         return appInfo.uid + "," + appInfo.packageName;
222     }
223 }
224