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.widget.AppCheckBoxPreference; 48 import com.android.settingslib.core.AbstractPreferenceController; 49 import com.android.settingslib.utils.StringUtil; 50 import com.android.settingslib.widget.FooterPreferenceMixinCompat; 51 52 import java.util.List; 53 54 /** 55 * Fragment to show a list of anomaly apps, where user could handle these anomalies 56 */ 57 public class RestrictedAppDetails extends DashboardFragment implements 58 BatteryTipPreferenceController.BatteryTipListener { 59 60 public static final String TAG = "RestrictedAppDetails"; 61 62 @VisibleForTesting 63 static final String EXTRA_APP_INFO_LIST = "app_info_list"; 64 private static final String KEY_PREF_RESTRICTED_APP_LIST = "restrict_app_list"; 65 private static final long TIME_NULL = -1; 66 67 @VisibleForTesting 68 List<AppInfo> mAppInfos; 69 @VisibleForTesting 70 IconDrawableFactory mIconDrawableFactory; 71 @VisibleForTesting 72 PreferenceGroup mRestrictedAppListGroup; 73 @VisibleForTesting 74 BatteryUtils mBatteryUtils; 75 @VisibleForTesting 76 PackageManager mPackageManager; 77 @VisibleForTesting 78 BatteryDatabaseManager mBatteryDatabaseManager; 79 private final FooterPreferenceMixinCompat mFooterPreferenceMixin = 80 new FooterPreferenceMixinCompat(this, getSettingsLifecycle()); 81 startRestrictedAppDetails(InstrumentedPreferenceFragment fragment, List<AppInfo> appInfos)82 public static void startRestrictedAppDetails(InstrumentedPreferenceFragment fragment, 83 List<AppInfo> appInfos) { 84 final Bundle args = new Bundle(); 85 args.putParcelableList(EXTRA_APP_INFO_LIST, appInfos); 86 87 new SubSettingLauncher(fragment.getContext()) 88 .setDestination(RestrictedAppDetails.class.getName()) 89 .setArguments(args) 90 .setTitleRes(R.string.restricted_app_title) 91 .setSourceMetricsCategory(fragment.getMetricsCategory()) 92 .launch(); 93 } 94 95 @Override onCreate(Bundle icicle)96 public void onCreate(Bundle icicle) { 97 super.onCreate(icicle); 98 final Context context = getContext(); 99 100 mFooterPreferenceMixin.createFooterPreference().setTitle( 101 R.string.restricted_app_detail_footer); 102 mRestrictedAppListGroup = (PreferenceGroup) findPreference(KEY_PREF_RESTRICTED_APP_LIST); 103 mAppInfos = getArguments().getParcelableArrayList(EXTRA_APP_INFO_LIST); 104 mPackageManager = context.getPackageManager(); 105 mIconDrawableFactory = IconDrawableFactory.newInstance(context); 106 mBatteryUtils = BatteryUtils.getInstance(context); 107 mBatteryDatabaseManager = BatteryDatabaseManager.getInstance(context); 108 109 refreshUi(); 110 } 111 112 @Override onPreferenceTreeClick(Preference preference)113 public boolean onPreferenceTreeClick(Preference preference) { 114 115 return super.onPreferenceTreeClick(preference); 116 } 117 118 @Override getLogTag()119 protected String getLogTag() { 120 return TAG; 121 } 122 123 @Override getPreferenceScreenResId()124 protected int getPreferenceScreenResId() { 125 return R.xml.restricted_apps_detail; 126 } 127 128 @Override createPreferenceControllers(Context context)129 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 130 return null; 131 } 132 133 @Override getMetricsCategory()134 public int getMetricsCategory() { 135 return SettingsEnums.FUELGAUGE_RESTRICTED_APP_DETAILS; 136 } 137 138 @Override getHelpResource()139 public int getHelpResource() { 140 return R.string.help_uri_restricted_apps; 141 } 142 143 @VisibleForTesting refreshUi()144 void refreshUi() { 145 mRestrictedAppListGroup.removeAll(); 146 final Context context = getPrefContext(); 147 final SparseLongArray timestampArray = mBatteryDatabaseManager 148 .queryActionTime(AnomalyDatabaseHelper.ActionType.RESTRICTION); 149 final long now = System.currentTimeMillis(); 150 151 for (int i = 0, size = mAppInfos.size(); i < size; i++) { 152 final CheckBoxPreference checkBoxPreference = new AppCheckBoxPreference(context); 153 final AppInfo appInfo = mAppInfos.get(i); 154 try { 155 final ApplicationInfo applicationInfo = mPackageManager.getApplicationInfoAsUser( 156 appInfo.packageName, 0 /* flags */, UserHandle.getUserId(appInfo.uid)); 157 checkBoxPreference.setChecked( 158 mBatteryUtils.isForceAppStandbyEnabled(appInfo.uid, appInfo.packageName)); 159 checkBoxPreference.setTitle(mPackageManager.getApplicationLabel(applicationInfo)); 160 checkBoxPreference.setIcon( 161 Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, 162 appInfo.packageName, 163 UserHandle.getUserId(appInfo.uid))); 164 checkBoxPreference.setKey(getKeyFromAppInfo(appInfo)); 165 checkBoxPreference.setOnPreferenceChangeListener((pref, value) -> { 166 final BatteryTipDialogFragment fragment = createDialogFragment(appInfo, 167 (Boolean) value); 168 fragment.setTargetFragment(this, 0 /* requestCode */); 169 fragment.show(getFragmentManager(), TAG); 170 171 return false; 172 }); 173 174 final long timestamp = timestampArray.get(appInfo.uid, TIME_NULL); 175 if (timestamp != TIME_NULL) { 176 checkBoxPreference.setSummary(getString(R.string.restricted_app_time_summary, 177 StringUtil.formatRelativeTime(context, now - timestamp, false))); 178 } 179 final CharSequence test = checkBoxPreference.getSummaryOn(); 180 mRestrictedAppListGroup.addPreference(checkBoxPreference); 181 } catch (PackageManager.NameNotFoundException e) { 182 Log.e(TAG, "Can't find package: " + appInfo.packageName); 183 } 184 } 185 } 186 187 @Override onBatteryTipHandled(BatteryTip batteryTip)188 public void onBatteryTipHandled(BatteryTip batteryTip) { 189 final AppInfo appInfo; 190 final boolean isRestricted = batteryTip instanceof RestrictAppTip; 191 if (isRestricted) { 192 appInfo = ((RestrictAppTip) batteryTip).getRestrictAppList().get(0); 193 } else { 194 appInfo = ((UnrestrictAppTip) batteryTip).getUnrestrictAppInfo(); 195 } 196 197 CheckBoxPreference preference = (CheckBoxPreference) mRestrictedAppListGroup 198 .findPreference(getKeyFromAppInfo(appInfo)); 199 if (preference != null) { 200 preference.setChecked(isRestricted); 201 } 202 } 203 204 @VisibleForTesting createDialogFragment(AppInfo appInfo, boolean toRestrict)205 BatteryTipDialogFragment createDialogFragment(AppInfo appInfo, boolean toRestrict) { 206 final BatteryTip batteryTip = toRestrict 207 ? new RestrictAppTip(BatteryTip.StateType.NEW, appInfo) 208 : new UnrestrictAppTip(BatteryTip.StateType.NEW, appInfo); 209 210 return BatteryTipDialogFragment.newInstance( 211 batteryTip, getMetricsCategory()); 212 } 213 214 @VisibleForTesting getKeyFromAppInfo(AppInfo appInfo)215 String getKeyFromAppInfo(AppInfo appInfo) { 216 return appInfo.uid + "," + appInfo.packageName; 217 } 218 } 219