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