• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.car.developeroptions.development.gamedriver;
18 
19 import static com.android.car.developeroptions.development.gamedriver.GameDriverEnableForAllAppsPreferenceController.GAME_DRIVER_DEFAULT;
20 import static com.android.car.developeroptions.development.gamedriver.GameDriverEnableForAllAppsPreferenceController.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.car.developeroptions.R;
38 import com.android.car.developeroptions.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 GameDriverAppPreferenceController extends BasePreferenceController
57         implements Preference.OnPreferenceChangeListener,
58         GameDriverContentObserver.OnGameDriverContentChangedListener, LifecycleObserver,
59         OnStart, OnStop {
60 
61     private final Context mContext;
62     private final ContentResolver mContentResolver;
63     private final CharSequence[] mEntryList;
64     private final String mPreferenceTitle;
65     private final String mPreferenceDefault;
66     private final String mPreferenceGameDriver;
67     private final String mPreferenceSystem;
68     @VisibleForTesting
69     GameDriverContentObserver mGameDriverContentObserver;
70 
71     private final List<AppInfo> mAppInfos;
72     private final Set<String> mDevOptInApps;
73     private final Set<String> mDevOptOutApps;
74 
75     private PreferenceGroup mPreferenceGroup;
76 
GameDriverAppPreferenceController(Context context, String key)77     public GameDriverAppPreferenceController(Context context, String key) {
78         super(context, key);
79 
80         mContext = context;
81         mContentResolver = context.getContentResolver();
82         mGameDriverContentObserver =
83                 new GameDriverContentObserver(new Handler(Looper.getMainLooper()), this);
84 
85         final Resources resources = context.getResources();
86         mEntryList = resources.getStringArray(R.array.game_driver_app_preference_values);
87         mPreferenceTitle = resources.getString(R.string.game_driver_app_preference_title);
88         mPreferenceDefault = resources.getString(R.string.game_driver_app_preference_default);
89         mPreferenceGameDriver =
90                 resources.getString(R.string.game_driver_app_preference_game_driver);
91         mPreferenceSystem = resources.getString(R.string.game_driver_app_preference_system);
92 
93         // TODO: Move this task to background if there's potential ANR/Jank.
94         // Update the UI when all the app infos are ready.
95         mAppInfos = getAppInfos(context);
96 
97         mDevOptInApps =
98                 getGlobalSettingsString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_IN_APPS);
99         mDevOptOutApps =
100                 getGlobalSettingsString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_OUT_APPS);
101     }
102 
103     @Override
getAvailabilityStatus()104     public int getAvailabilityStatus() {
105         return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)
106                 && (Settings.Global.getInt(mContentResolver,
107                 Settings.Global.GAME_DRIVER_ALL_APPS, GAME_DRIVER_DEFAULT)
108                 != GAME_DRIVER_OFF)
109                 ? AVAILABLE
110                 : CONDITIONALLY_UNAVAILABLE;
111     }
112 
113     @Override
displayPreference(PreferenceScreen screen)114     public void displayPreference(PreferenceScreen screen) {
115         super.displayPreference(screen);
116         mPreferenceGroup = screen.findPreference(getPreferenceKey());
117 
118         final Context context = mPreferenceGroup.getContext();
119         for (AppInfo appInfo : mAppInfos) {
120             mPreferenceGroup.addPreference(
121                     createListPreference(context, appInfo.info.packageName, appInfo.label));
122         }
123     }
124 
125     @Override
onStart()126     public void onStart() {
127         mGameDriverContentObserver.register(mContentResolver);
128     }
129 
130     @Override
onStop()131     public void onStop() {
132         mGameDriverContentObserver.unregister(mContentResolver);
133     }
134 
135     @Override
updateState(Preference preference)136     public void updateState(Preference preference) {
137         preference.setVisible(isAvailable());
138     }
139 
140     @Override
onPreferenceChange(Preference preference, Object newValue)141     public boolean onPreferenceChange(Preference preference, Object newValue) {
142         final ListPreference listPref = (ListPreference) preference;
143         final String value = newValue.toString();
144         final String packageName = preference.getKey();
145 
146         // When user choose a new preference, update both Sets for
147         // opt-in and opt-out apps. Then set the new summary text.
148         if (value.equals(mPreferenceSystem)) {
149             mDevOptInApps.remove(packageName);
150             mDevOptOutApps.add(packageName);
151         } else if (value.equals(mPreferenceGameDriver)) {
152             mDevOptInApps.add(packageName);
153             mDevOptOutApps.remove(packageName);
154         } else {
155             mDevOptInApps.remove(packageName);
156             mDevOptOutApps.remove(packageName);
157         }
158         listPref.setValue(value);
159         listPref.setSummary(value);
160 
161         // Push the updated Sets for opt-in and opt-out apps to
162         // corresponding Settings.Global.GAME_DRIVER_OPT_(IN|OUT)_APPS
163         Settings.Global.putString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_IN_APPS,
164                 String.join(",", mDevOptInApps));
165         Settings.Global.putString(mContentResolver, Settings.Global.GAME_DRIVER_OPT_OUT_APPS,
166                 String.join(",", mDevOptOutApps));
167 
168         return true;
169     }
170 
171     @Override
onGameDriverContentChanged()172     public void onGameDriverContentChanged() {
173         updateState(mPreferenceGroup);
174     }
175 
176     // AppInfo class to achieve loading the application label only once
177     class AppInfo {
AppInfo(PackageManager packageManager, ApplicationInfo applicationInfo)178         AppInfo(PackageManager packageManager, ApplicationInfo applicationInfo) {
179             info = applicationInfo;
180             label = packageManager.getApplicationLabel(applicationInfo).toString();
181         }
182 
183         final ApplicationInfo info;
184         final String label;
185     }
186 
187     // List of non-system packages that are installed for the current user.
getAppInfos(Context context)188     private List<AppInfo> getAppInfos(Context context) {
189         final PackageManager packageManager = context.getPackageManager();
190         final List<ApplicationInfo> applicationInfos =
191                 packageManager.getInstalledApplications(0 /* flags */);
192 
193         final List<AppInfo> appInfos = new ArrayList<>();
194         for (ApplicationInfo applicationInfo : applicationInfos) {
195             if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
196                 appInfos.add(new AppInfo(packageManager, applicationInfo));
197             }
198         }
199 
200         Collections.sort(appInfos, appInfoComparator);
201 
202         return appInfos;
203     }
204 
205     // Parse the raw comma separated package names into a String Set
getGlobalSettingsString(ContentResolver contentResolver, String name)206     private Set<String> getGlobalSettingsString(ContentResolver contentResolver, String name) {
207         final String settingsValue = Settings.Global.getString(contentResolver, name);
208         if (settingsValue == null) {
209             return new HashSet<>();
210         }
211 
212         final Set<String> valueSet = new HashSet<>(Arrays.asList(settingsValue.split(",")));
213         valueSet.remove("");
214 
215         return valueSet;
216     }
217 
218     private final Comparator<AppInfo> appInfoComparator = new Comparator<AppInfo>() {
219         public final int compare(AppInfo a, AppInfo b) {
220             return Collator.getInstance().compare(a.label, b.label);
221         }
222     };
223 
224     @VisibleForTesting
createListPreference( Context context, String packageName, String appName)225     protected ListPreference createListPreference(
226             Context context, String packageName, String appName) {
227         final ListPreference listPreference = new ListPreference(context);
228 
229         listPreference.setKey(packageName);
230         listPreference.setTitle(appName);
231         listPreference.setDialogTitle(mPreferenceTitle);
232         listPreference.setEntries(mEntryList);
233         listPreference.setEntryValues(mEntryList);
234 
235         // Initialize preference default and summary with the opt in/out choices
236         // from Settings.Global.GAME_DRIVER_OPT_(IN|OUT)_APPS
237         if (mDevOptOutApps.contains(packageName)) {
238             listPreference.setValue(mPreferenceSystem);
239             listPreference.setSummary(mPreferenceSystem);
240         } else if (mDevOptInApps.contains(packageName)) {
241             listPreference.setValue(mPreferenceGameDriver);
242             listPreference.setSummary(mPreferenceGameDriver);
243         } else {
244             listPreference.setValue(mPreferenceDefault);
245             listPreference.setSummary(mPreferenceDefault);
246         }
247 
248         listPreference.setOnPreferenceChangeListener(this);
249 
250         return listPreference;
251     }
252 }
253