1 /* 2 * Copyright (C) 2015 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 static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; 20 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED; 21 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; 22 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; 23 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; 24 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED; 25 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; 26 27 import android.app.settings.SettingsEnums; 28 import android.app.usage.UsageStatsManager; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.pm.PackageManager; 32 import android.content.pm.ResolveInfo; 33 import android.content.res.Resources; 34 import android.os.Bundle; 35 import android.text.TextUtils; 36 37 import androidx.preference.ListPreference; 38 import androidx.preference.Preference; 39 import androidx.preference.PreferenceGroup; 40 41 import com.android.settings.R; 42 import com.android.settings.SettingsPreferenceFragment; 43 import com.android.settings.development.DeveloperOptionAwareMixin; 44 45 import java.util.Arrays; 46 import java.util.List; 47 48 public class InactiveApps extends SettingsPreferenceFragment 49 implements Preference.OnPreferenceChangeListener, DeveloperOptionAwareMixin { 50 51 private static final CharSequence[] FULL_SETTABLE_BUCKETS_NAMES = { 52 "ACTIVE", "WORKING_SET", "FREQUENT", "RARE", "RESTRICTED" 53 }; 54 55 private static final CharSequence[] FULL_SETTABLE_BUCKETS_VALUES = { 56 Integer.toString(STANDBY_BUCKET_ACTIVE), 57 Integer.toString(STANDBY_BUCKET_WORKING_SET), 58 Integer.toString(STANDBY_BUCKET_FREQUENT), 59 Integer.toString(STANDBY_BUCKET_RARE), 60 Integer.toString(STANDBY_BUCKET_RESTRICTED) 61 }; 62 63 private UsageStatsManager mUsageStats; 64 65 @Override getMetricsCategory()66 public int getMetricsCategory() { 67 return SettingsEnums.FUELGAUGE_INACTIVE_APPS; 68 } 69 70 @Override onCreate(Bundle icicle)71 public void onCreate(Bundle icicle) { 72 super.onCreate(icicle); 73 74 mUsageStats = getActivity().getSystemService(UsageStatsManager.class); 75 addPreferencesFromResource(R.xml.placeholder_preference_screen); 76 getActivity().setTitle(com.android.settingslib.R.string.inactive_apps_title); 77 } 78 79 @Override onResume()80 public void onResume() { 81 super.onResume(); 82 init(); 83 } 84 init()85 private void init() { 86 PreferenceGroup screen = getPreferenceScreen(); 87 screen.removeAll(); 88 screen.setOrderingAsAdded(false); 89 final Context context = getActivity(); 90 final PackageManager pm = context.getPackageManager(); 91 final String settingsPackage = context.getPackageName(); 92 final CharSequence[] bucketNames = FULL_SETTABLE_BUCKETS_NAMES; 93 final CharSequence[] bucketValues = FULL_SETTABLE_BUCKETS_VALUES; 94 95 Intent launcherIntent = new Intent(Intent.ACTION_MAIN); 96 launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER); 97 List<ResolveInfo> apps = pm.queryIntentActivities(launcherIntent, 0); 98 for (ResolveInfo app : apps) { 99 String packageName = app.activityInfo.applicationInfo.packageName; 100 ListPreference p = new ListPreference(getPrefContext()); 101 p.setTitle(app.loadLabel(pm)); 102 p.setIcon(app.loadIcon(pm)); 103 p.setKey(packageName); 104 p.setEntries(getAllowableBuckets(packageName, bucketNames)); 105 p.setEntryValues(getAllowableBuckets(packageName, bucketValues)); 106 updateSummary(p); 107 // Don't allow Settings to change its own standby bucket. 108 if (TextUtils.equals(packageName, settingsPackage)) { 109 p.setEnabled(false); 110 } 111 p.setOnPreferenceChangeListener(this); 112 113 screen.addPreference(p); 114 } 115 } 116 getAllowableBuckets(String packageName, CharSequence[] possibleBuckets)117 private CharSequence[] getAllowableBuckets(String packageName, CharSequence[] possibleBuckets) { 118 final int minBucket = mUsageStats.getAppMinStandbyBucket(packageName); 119 if (minBucket > STANDBY_BUCKET_RESTRICTED) { 120 return possibleBuckets; 121 } 122 if (minBucket < STANDBY_BUCKET_ACTIVE) { 123 return new CharSequence[] {}; 124 } 125 // Use FULL_SETTABLE_BUCKETS_VALUES since we're searching using the int value. The index 126 // should apply no matter which array we're going to copy from. 127 final int idx = 128 Arrays.binarySearch(FULL_SETTABLE_BUCKETS_VALUES, Integer.toString(minBucket)); 129 if (idx < 0) { 130 // Include everything 131 return possibleBuckets; 132 } 133 return Arrays.copyOfRange(possibleBuckets, 0, idx + 1); 134 } 135 bucketToName(int bucket)136 static String bucketToName(int bucket) { 137 switch (bucket) { 138 case STANDBY_BUCKET_EXEMPTED: 139 return "EXEMPTED"; 140 case STANDBY_BUCKET_ACTIVE: 141 return "ACTIVE"; 142 case STANDBY_BUCKET_WORKING_SET: 143 return "WORKING_SET"; 144 case STANDBY_BUCKET_FREQUENT: 145 return "FREQUENT"; 146 case STANDBY_BUCKET_RARE: 147 return "RARE"; 148 case STANDBY_BUCKET_RESTRICTED: 149 return "RESTRICTED"; 150 case STANDBY_BUCKET_NEVER: 151 return "NEVER"; 152 } 153 return ""; 154 } 155 updateSummary(ListPreference p)156 private void updateSummary(ListPreference p) { 157 final Resources res = getActivity().getResources(); 158 final int appBucket = mUsageStats.getAppStandbyBucket(p.getKey()); 159 final String bucketName = bucketToName(appBucket); 160 p.setSummary( 161 res.getString(com.android.settingslib.R.string.standby_bucket_summary, bucketName)); 162 // Buckets outside of the range of the dynamic ones are only used for special 163 // purposes and can either not be changed out of, or might have undesirable 164 // side-effects in combination with other assumptions. 165 final boolean changeable = 166 appBucket >= STANDBY_BUCKET_ACTIVE && appBucket <= STANDBY_BUCKET_RESTRICTED; 167 if (changeable) { 168 p.setValue(Integer.toString(appBucket)); 169 } 170 p.setEnabled(changeable); 171 } 172 173 @Override onPreferenceChange(Preference preference, Object newValue)174 public boolean onPreferenceChange(Preference preference, Object newValue) { 175 mUsageStats.setAppStandbyBucket(preference.getKey(), Integer.parseInt((String) newValue)); 176 updateSummary((ListPreference) preference); 177 return false; 178 } 179 } 180