1 /* 2 * Copyright 2019 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.development.graphicsdriver; 18 19 import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.GAME_DRIVER_DEFAULT; 20 import static com.android.settings.development.graphicsdriver.GraphicsDriverEnableForAllAppsPreferenceController.GAME_DRIVER_OFF; 21 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageManager; 26 import android.content.res.Resources; 27 import android.os.Handler; 28 import android.os.Looper; 29 import android.provider.Settings; 30 31 import androidx.annotation.VisibleForTesting; 32 import androidx.preference.ListPreference; 33 import androidx.preference.Preference; 34 import androidx.preference.PreferenceGroup; 35 import androidx.preference.PreferenceScreen; 36 37 import com.android.settings.R; 38 import com.android.settings.core.BasePreferenceController; 39 import com.android.settingslib.core.lifecycle.LifecycleObserver; 40 import com.android.settingslib.core.lifecycle.events.OnStart; 41 import com.android.settingslib.core.lifecycle.events.OnStop; 42 import com.android.settingslib.development.DevelopmentSettingsEnabler; 43 44 import java.text.Collator; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Collections; 48 import java.util.Comparator; 49 import java.util.HashSet; 50 import java.util.List; 51 import java.util.Set; 52 53 /** 54 * Controller of all the per App based list preferences. 55 */ 56 public class GraphicsDriverAppPreferenceController extends BasePreferenceController 57 implements Preference.OnPreferenceChangeListener, 58 GraphicsDriverContentObserver.OnGraphicsDriverContentChangedListener, LifecycleObserver, 59 OnStart, OnStop { 60 61 private final Context mContext; 62 private final ContentResolver mContentResolver; 63 private final String mPreferenceTitle; 64 private final String mPreferenceDefault; 65 private final String mPreferenceGameDriver; 66 private final String mPreferencePrereleaseDriver; 67 private final String mPreferenceSystem; 68 @VisibleForTesting 69 CharSequence[] mEntryList; 70 @VisibleForTesting 71 GraphicsDriverContentObserver mGraphicsDriverContentObserver; 72 73 private final List<AppInfo> mAppInfos; 74 private final Set<String> mDevOptInApps; 75 private final Set<String> mDevPrereleaseOptInApps; 76 private final Set<String> mDevOptOutApps; 77 78 private PreferenceGroup mPreferenceGroup; 79 GraphicsDriverAppPreferenceController(Context context, String key)80 public GraphicsDriverAppPreferenceController(Context context, String key) { 81 super(context, key); 82 83 mContext = context; 84 mContentResolver = context.getContentResolver(); 85 mGraphicsDriverContentObserver = 86 new GraphicsDriverContentObserver(new Handler(Looper.getMainLooper()), this); 87 88 final Resources resources = context.getResources(); 89 mPreferenceTitle = resources.getString(R.string.graphics_driver_app_preference_title); 90 mPreferenceDefault = resources.getString(R.string.graphics_driver_app_preference_default); 91 mPreferenceGameDriver = 92 resources.getString(R.string.graphics_driver_app_preference_game_driver); 93 mPreferencePrereleaseDriver = 94 resources.getString(R.string.graphics_driver_app_preference_prerelease_driver); 95 mPreferenceSystem = resources.getString(R.string.graphics_driver_app_preference_system); 96 mEntryList = GraphicsDriverEnableForAllAppsPreferenceController.constructEntryList( 97 mContext, true); 98 99 // TODO: Move this task to background if there's potential ANR/Jank. 100 // Update the UI when all the app infos are ready. 101 mAppInfos = getAppInfos(context); 102 103 mDevOptInApps = 104 getGlobalSettingsString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_IN_APPS); 105 mDevPrereleaseOptInApps = getGlobalSettingsString( 106 mContentResolver, Settings.Global.GAME_DRIVER_PRERELEASE_OPT_IN_APPS); 107 mDevOptOutApps = 108 getGlobalSettingsString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_OUT_APPS); 109 } 110 111 @Override getAvailabilityStatus()112 public int getAvailabilityStatus() { 113 return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext) 114 && (Settings.Global.getInt(mContentResolver, 115 Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT) 116 != GAME_DRIVER_OFF) 117 ? AVAILABLE 118 : CONDITIONALLY_UNAVAILABLE; 119 } 120 121 @Override displayPreference(PreferenceScreen screen)122 public void displayPreference(PreferenceScreen screen) { 123 super.displayPreference(screen); 124 mPreferenceGroup = screen.findPreference(getPreferenceKey()); 125 126 final Context context = mPreferenceGroup.getContext(); 127 for (AppInfo appInfo : mAppInfos) { 128 mPreferenceGroup.addPreference( 129 createListPreference(context, appInfo.info.packageName, appInfo.label)); 130 } 131 } 132 133 @Override onStart()134 public void onStart() { 135 mGraphicsDriverContentObserver.register(mContentResolver); 136 } 137 138 @Override onStop()139 public void onStop() { 140 mGraphicsDriverContentObserver.unregister(mContentResolver); 141 } 142 143 @Override updateState(Preference preference)144 public void updateState(Preference preference) { 145 preference.setVisible(isAvailable()); 146 } 147 148 @Override onPreferenceChange(Preference preference, Object newValue)149 public boolean onPreferenceChange(Preference preference, Object newValue) { 150 final ListPreference listPref = (ListPreference) preference; 151 final String value = newValue.toString(); 152 final String packageName = preference.getKey(); 153 154 // When user choose a new preference, update both Sets for 155 // opt-in and opt-out apps. Then set the new summary text. 156 if (value.equals(mPreferenceSystem)) { 157 mDevOptInApps.remove(packageName); 158 mDevPrereleaseOptInApps.remove(packageName); 159 mDevOptOutApps.add(packageName); 160 } else if (value.equals(mPreferenceGameDriver)) { 161 mDevOptInApps.add(packageName); 162 mDevPrereleaseOptInApps.remove(packageName); 163 mDevOptOutApps.remove(packageName); 164 } else if (value.equals(mPreferencePrereleaseDriver)) { 165 mDevOptInApps.remove(packageName); 166 mDevPrereleaseOptInApps.add(packageName); 167 mDevOptOutApps.remove(packageName); 168 } else { 169 mDevOptInApps.remove(packageName); 170 mDevPrereleaseOptInApps.remove(packageName); 171 mDevOptOutApps.remove(packageName); 172 } 173 listPref.setValue(value); 174 listPref.setSummary(value); 175 176 // Push the updated Sets for stable/prerelease opt-in and opt-out apps to 177 // corresponding Settings.Global.GAME_DRIVER(_PRERELEASE)?_OPT_(IN|OUT)_APPS 178 Settings.Global.putString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_IN_APPS, 179 String.join(",", mDevOptInApps)); 180 Settings.Global.putString(mContentResolver, 181 Settings.Global.GAME_DRIVER_PRERELEASE_OPT_IN_APPS, 182 String.join(",", mDevPrereleaseOptInApps)); 183 Settings.Global.putString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_OUT_APPS, 184 String.join(",", mDevOptOutApps)); 185 186 return true; 187 } 188 189 @Override onGraphicsDriverContentChanged()190 public void onGraphicsDriverContentChanged() { 191 updateState(mPreferenceGroup); 192 } 193 194 // AppInfo class to achieve loading the application label only once 195 class AppInfo { AppInfo(PackageManager packageManager, ApplicationInfo applicationInfo)196 AppInfo(PackageManager packageManager, ApplicationInfo applicationInfo) { 197 info = applicationInfo; 198 label = packageManager.getApplicationLabel(applicationInfo).toString(); 199 } 200 201 public final ApplicationInfo info; 202 public final String label; 203 } 204 205 // List of non-system packages that are installed for the current user. getAppInfos(Context context)206 private List<AppInfo> getAppInfos(Context context) { 207 final PackageManager packageManager = context.getPackageManager(); 208 final List<ApplicationInfo> applicationInfos = 209 packageManager.getInstalledApplications(0 /* flags */); 210 211 final List<AppInfo> appInfos = new ArrayList<>(); 212 for (ApplicationInfo applicationInfo : applicationInfos) { 213 if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { 214 appInfos.add(new AppInfo(packageManager, applicationInfo)); 215 } 216 } 217 218 Collections.sort(appInfos, mAppInfoComparator); 219 220 return appInfos; 221 } 222 223 // Parse the raw comma separated package names into a String Set getGlobalSettingsString(ContentResolver contentResolver, String name)224 private Set<String> getGlobalSettingsString(ContentResolver contentResolver, String name) { 225 final String settingsValue = Settings.Global.getString(contentResolver, name); 226 if (settingsValue == null) { 227 return new HashSet<>(); 228 } 229 230 final Set<String> valueSet = new HashSet<>(Arrays.asList(settingsValue.split(","))); 231 valueSet.remove(""); 232 233 return valueSet; 234 } 235 236 private final Comparator<AppInfo> mAppInfoComparator = new Comparator<AppInfo>() { 237 public int compare(AppInfo a, AppInfo b) { 238 return Collator.getInstance().compare(a.label, b.label); 239 } 240 }; 241 242 @VisibleForTesting createListPreference( Context context, String packageName, String appName)243 protected ListPreference createListPreference( 244 Context context, String packageName, String appName) { 245 final ListPreference listPreference = new ListPreference(context); 246 247 listPreference.setKey(packageName); 248 listPreference.setTitle(appName); 249 listPreference.setDialogTitle(mPreferenceTitle); 250 listPreference.setEntries(mEntryList); 251 listPreference.setEntryValues(mEntryList); 252 253 // Initialize preference default and summary with the opt in/out choices 254 // from Settings.Global.GAME_DRIVER(_PRERELEASE)?_OPT_(IN|OUT)_APPS 255 if (mDevOptOutApps.contains(packageName)) { 256 listPreference.setValue(mPreferenceSystem); 257 listPreference.setSummary(mPreferenceSystem); 258 } else if (mDevPrereleaseOptInApps.contains(packageName)) { 259 listPreference.setValue(mPreferencePrereleaseDriver); 260 listPreference.setSummary(mPreferencePrereleaseDriver); 261 } else if (mDevOptInApps.contains(packageName)) { 262 listPreference.setValue(mPreferenceGameDriver); 263 listPreference.setSummary(mPreferenceGameDriver); 264 } else { 265 listPreference.setValue(mPreferenceDefault); 266 listPreference.setSummary(mPreferenceDefault); 267 } 268 269 listPreference.setOnPreferenceChangeListener(this); 270 271 return listPreference; 272 } 273 } 274