From 83c030f1cd50b3377d04bcdd8fdae6783224b4ab Mon Sep 17 00:00:00 2001 From: Yiwei Zhang Date: Sat, 29 Dec 2018 08:52:31 +0800 Subject: [PATCH] GUP: Display a list of Apps and dialogs 1) Add preference controller for gup dashboard and add list preferences for each entry of the apps 2) Add gup dashboard page to search index 3) Add comprehensive tests for GupPreferenceController Bug: 119221883 Test: make RunSettingsRoboTests Change-Id: Ide4934c0dd3901532723e77e74663e5a7b639026 --- res/values/strings.xml | 21 +- res/xml/development_settings.xml | 8 +- res/xml/gup_settings.xml | 12 +- ...evelopmentOptionsActivityRequestCodes.java | 2 - .../DevelopmentSettingsDashboardFragment.java | 1 - ...tePackageDevOptInPreferenceController.java | 121 --------- .../development/gup/GupDashboard.java | 26 ++ .../gup/GupPreferenceController.java | 206 ++++++++++++++++ ...randfather_not_implementing_index_provider | 1 - ...ckageDevOptInPreferenceControllerTest.java | 133 ---------- .../gup/GupPreferenceControllerTest.java | 231 ++++++++++++++++++ 11 files changed, 490 insertions(+), 272 deletions(-) delete mode 100644 src/com/android/settings/development/GameUpdatePackageDevOptInPreferenceController.java create mode 100644 src/com/android/settings/development/gup/GupPreferenceController.java delete mode 100644 tests/robotests/src/com/android/settings/development/GameUpdatePackageDevOptInPreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/development/gup/GupPreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index f0a57abf8de..8868b4b5eab 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10042,17 +10042,24 @@ ANGLE enabled application: %1$s - - Use Game Update Package - - No selected app - - %1$s - Game Update Packages Preferences Modify Game Update Packages settings + + Select Graphics Driver + + Default + + Game Update Packages + + Native Graphics Driver + + + @string/gup_app_preference_default + @string/gup_app_preference_gup + @string/gup_app_preference_native + diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index 2eedca55729..a5e26f654f8 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -195,7 +195,8 @@ android:key="gup_dashboard" android:title="@string/gup_dashboard_title" android:summary="@string/gup_dashboard_summary" - android:fragment="com.android.settings.development.gup.GupDashboard" /> + android:fragment="com.android.settings.development.gup.GupDashboard" + settings:searchable="false" /> @@ -430,11 +431,6 @@ android:summary="%s" android:title="@string/simulate_color_space" /> - - + xmlns:settings="http://schemas.android.com/apk/res-auto" + android:key="gup_settings" + android:title="@string/gup_dashboard_title"> + + + + + diff --git a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java index a67aac4d3a5..564f2c35c47 100644 --- a/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java +++ b/src/com/android/settings/development/DevelopmentOptionsActivityRequestCodes.java @@ -31,6 +31,4 @@ public interface DevelopmentOptionsActivityRequestCodes { int REQUEST_CODE_ANGLE_DRIVER_PKGS = 4; int REQUEST_CODE_ANGLE_DRIVER_VALUES = 5; - - int REQUEST_CODE_GUP_DEV_OPT_IN_APPS = 6; } diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 5990320321c..725a1953429 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -423,7 +423,6 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new SelectDebugAppPreferenceController(context, fragment)); controllers.add(new WaitForDebuggerPreferenceController(context)); controllers.add(new EnableGpuDebugLayersPreferenceController(context)); - controllers.add(new GameUpdatePackageDevOptInPreferenceController(context, fragment)); controllers.add(new VerifyAppsOverUsbPreferenceController(context)); controllers.add(new LogdSizePreferenceController(context)); controllers.add(new LogPersistPreferenceController(context, fragment, lifecycle)); diff --git a/src/com/android/settings/development/GameUpdatePackageDevOptInPreferenceController.java b/src/com/android/settings/development/GameUpdatePackageDevOptInPreferenceController.java deleted file mode 100644 index 2d295055ea0..00000000000 --- a/src/com/android/settings/development/GameUpdatePackageDevOptInPreferenceController.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.development; - -import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes - .REQUEST_CODE_GUP_DEV_OPT_IN_APPS; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.provider.Settings; -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; - -import com.android.settings.R; -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.development.DeveloperOptionsPreferenceController; - -// TODO(b/119221883): Need to override isAvailable() to return false when updatable graphics driver is not supported. -public class GameUpdatePackageDevOptInPreferenceController - extends DeveloperOptionsPreferenceController - implements PreferenceControllerMixin, OnActivityResultListener { - - private static final String GUP_DEV_OPT_IN_APP_KEY = "gup_dev_opt_in_app"; - - private final DevelopmentSettingsDashboardFragment mFragment; - private final PackageManager mPackageManager; - - public GameUpdatePackageDevOptInPreferenceController(Context context, - DevelopmentSettingsDashboardFragment fragment) { - super(context); - mFragment = fragment; - mPackageManager = mContext.getPackageManager(); - } - - @Override - public String getPreferenceKey() { - return GUP_DEV_OPT_IN_APP_KEY; - } - - @Override - public boolean handlePreferenceTreeClick(Preference preference) { - if (GUP_DEV_OPT_IN_APP_KEY.equals(preference.getKey())) { - // pass it on to settings - final Intent intent = getActivityStartIntent(); - mFragment.startActivityForResult(intent, REQUEST_CODE_GUP_DEV_OPT_IN_APPS); - return true; - } - return false; - } - - @Override - public void updateState(Preference preference) { - updatePreferenceSummary(); - } - - @Override - public boolean onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode != REQUEST_CODE_GUP_DEV_OPT_IN_APPS - || resultCode != Activity.RESULT_OK) { - return false; - } - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.GUP_DEV_OPT_IN_APPS, data.getAction()); - updatePreferenceSummary(); - return true; - } - - @Override - protected void onDeveloperOptionsSwitchDisabled() { - super.onDeveloperOptionsSwitchDisabled(); - mPreference.setSummary(mContext.getResources().getString( - R.string.gup_dev_opt_in_app_not_set)); - } - - @VisibleForTesting - Intent getActivityStartIntent() { - Intent intent = new Intent(mContext, AppPicker.class); - intent.putExtra(AppPicker.EXTRA_NON_SYSTEM, true /* value */); - return intent; - } - - private void updatePreferenceSummary() { - final String optInApp = Settings.Global.getString( - mContext.getContentResolver(), Settings.Global.GUP_DEV_OPT_IN_APPS); - if (optInApp != null && !optInApp.isEmpty()) { - mPreference.setSummary(mContext.getResources().getString( - R.string.gup_dev_opt_in_app_set, getAppLabel(optInApp))); - } else { - mPreference.setSummary(mContext.getResources().getString( - R.string.gup_dev_opt_in_app_not_set)); - } - } - - private String getAppLabel(String applicationPackageName) { - try { - final ApplicationInfo ai = mPackageManager.getApplicationInfo(applicationPackageName, - PackageManager.GET_DISABLED_COMPONENTS); - final CharSequence lab = mPackageManager.getApplicationLabel(ai); - return lab != null ? lab.toString() : applicationPackageName; - } catch (PackageManager.NameNotFoundException e) { - return applicationPackageName; - } - } -} diff --git a/src/com/android/settings/development/gup/GupDashboard.java b/src/com/android/settings/development/gup/GupDashboard.java index 674a0a90d0e..31f01dd08eb 100644 --- a/src/com/android/settings/development/gup/GupDashboard.java +++ b/src/com/android/settings/development/gup/GupDashboard.java @@ -17,14 +17,22 @@ package com.android.settings.development.gup; import android.content.Context; +import android.provider.SearchIndexableResource; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.search.Indexable; import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.development.DevelopmentSettingsEnabler; +import com.android.settingslib.search.SearchIndexable; +import java.util.ArrayList; import java.util.List; +@SearchIndexable public class GupDashboard extends DashboardFragment { private static final String TAG = "GupDashboard"; @@ -47,4 +55,22 @@ public class GupDashboard extends DashboardFragment { public int getHelpResource() { return 0; } + + public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() { + @Override + public List getXmlResourcesToIndex( + Context context, boolean enabled) { + final List result = new ArrayList<>(); + final SearchIndexableResource sir = new SearchIndexableResource(context); + sir.xmlResId = R.xml.gup_settings; + result.add(sir); + return result; + } + + @Override + protected boolean isPageSearchEnabled(Context context) { + return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context); + } + }; } diff --git a/src/com/android/settings/development/gup/GupPreferenceController.java b/src/com/android/settings/development/gup/GupPreferenceController.java new file mode 100644 index 00000000000..762314474ef --- /dev/null +++ b/src/com/android/settings/development/gup/GupPreferenceController.java @@ -0,0 +1,206 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development.gup; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.provider.Settings; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.development.DevelopmentSettingsEnabler; + +import java.text.Collator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class GupPreferenceController + extends BasePreferenceController implements Preference.OnPreferenceChangeListener { + private final CharSequence[] mEntryList; + private final String mPreferenceTitle; + private final String mPreferenceDefault; + private final String mPreferenceGup; + private final String mPreferenceNative; + + private final List mAppInfos; + private final Set mDevOptInApps; + private final Set mDevOptOutApps; + + public GupPreferenceController(Context context, String key) { + super(context, key); + + final Resources resources = context.getResources(); + mEntryList = resources.getStringArray(R.array.gup_app_preference_values); + mPreferenceTitle = resources.getString(R.string.gup_app_preference_title); + mPreferenceDefault = resources.getString(R.string.gup_app_preference_default); + mPreferenceGup = resources.getString(R.string.gup_app_preference_gup); + mPreferenceNative = resources.getString(R.string.gup_app_preference_native); + + // TODO: Move this task to background if there's potential ANR/Jank. + // Update the UI when all the app infos are ready. + mAppInfos = getAppInfos(context); + + final ContentResolver contentResolver = context.getContentResolver(); + mDevOptInApps = + getGlobalSettingsString(contentResolver, Settings.Global.GUP_DEV_OPT_IN_APPS); + mDevOptOutApps = + getGlobalSettingsString(contentResolver, Settings.Global.GUP_DEV_OPT_OUT_APPS); + } + + @Override + public int getAvailabilityStatus() { + return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext) + ? AVAILABLE + : DISABLED_DEPENDENT_SETTING; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + final PreferenceGroup preferenceGroup = + (PreferenceGroup) screen.findPreference(getPreferenceKey()); + if (preferenceGroup == null) { + return; + } + + for (AppInfo appInfo : mAppInfos) { + preferenceGroup.addPreference( + createListPreference(appInfo.info.packageName, appInfo.label)); + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final ListPreference listPref = (ListPreference) preference; + final String value = newValue.toString(); + final String packageName = preference.getKey(); + + // When user choose a new preference, update both Sets for + // opt-in and opt-out apps. Then set the new summary text. + if (value.equals(mPreferenceNative)) { + mDevOptInApps.remove(packageName); + mDevOptOutApps.add(packageName); + listPref.setSummary(mPreferenceNative); + } else if (value.equals(mPreferenceGup)) { + mDevOptInApps.add(packageName); + mDevOptOutApps.remove(packageName); + listPref.setSummary(mPreferenceGup); + } else { + mDevOptInApps.remove(packageName); + mDevOptOutApps.remove(packageName); + listPref.setSummary(mPreferenceDefault); + } + + // Push the updated Sets for opt-in and opt-out apps to + // corresponding Settings.Global.GUP_DEV_OPT_(IN|OUT)_APPS + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.GUP_DEV_OPT_IN_APPS, String.join(",", mDevOptInApps)); + Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.GUP_DEV_OPT_OUT_APPS, String.join(",", mDevOptOutApps)); + + return true; + } + + // AppInfo class to achieve loading the application label only once + class AppInfo { + AppInfo(PackageManager packageManager, ApplicationInfo applicationInfo) { + info = applicationInfo; + label = packageManager.getApplicationLabel(applicationInfo).toString(); + } + final ApplicationInfo info; + final String label; + } + + // List of non-system packages that are installed for the current user. + private List getAppInfos(Context context) { + final PackageManager packageManager = context.getPackageManager(); + final List applicationInfos = + packageManager.getInstalledApplications(0 /* flags */); + + final List appInfos = new ArrayList<>(); + for (ApplicationInfo applicationInfo : applicationInfos) { + if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + appInfos.add(new AppInfo(packageManager, applicationInfo)); + } + } + + Collections.sort(appInfos, appInfoComparator); + + return appInfos; + } + + // Parse the raw comma separated package names into a String Set + private Set getGlobalSettingsString(ContentResolver contentResolver, String name) { + final String settingsValue = Settings.Global.getString(contentResolver, name); + if (settingsValue == null) { + return new HashSet<>(); + } + + final Set valueSet = new HashSet<>(Arrays.asList(settingsValue.split(","))); + valueSet.remove(""); + + return valueSet; + } + + private final Comparator appInfoComparator = new Comparator() { + public final int compare(AppInfo a, AppInfo b) { + return Collator.getInstance().compare(a.label, b.label); + } + }; + + @VisibleForTesting + protected ListPreference createListPreference(String packageName, String appName) { + final ListPreference listPreference = new ListPreference(mContext); + + listPreference.setKey(packageName); + listPreference.setTitle(appName); + listPreference.setDialogTitle(mPreferenceTitle); + listPreference.setEntries(mEntryList); + listPreference.setEntryValues(mEntryList); + + // Initialize preference default and summary with the opt in/out choices + // from Settings.Global.GUP_DEV_OPT_(IN|OUT)_APPS + if (mDevOptOutApps.contains(packageName)) { + listPreference.setValue(mPreferenceNative); + listPreference.setSummary(mPreferenceNative); + } else if (mDevOptInApps.contains(packageName)) { + listPreference.setValue(mPreferenceGup); + listPreference.setSummary(mPreferenceGup); + } else { + listPreference.setValue(mPreferenceDefault); + listPreference.setSummary(mPreferenceDefault); + } + + listPreference.setOnPreferenceChangeListener(this); + + return listPreference; + } +} diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider index 2622eb320e7..447de0049df 100644 --- a/tests/robotests/assets/grandfather_not_implementing_index_provider +++ b/tests/robotests/assets/grandfather_not_implementing_index_provider @@ -28,7 +28,6 @@ com.android.settings.bluetooth.DevicePickerFragment com.android.settings.datausage.AppDataUsage com.android.settings.datausage.DataUsageList com.android.settings.datetime.timezone.TimeZoneSettings -com.android.settings.development.gup.GupDashboard com.android.settings.deviceinfo.PrivateVolumeSettings com.android.settings.deviceinfo.PublicVolumeSettings com.android.settings.deviceinfo.StorageProfileFragment diff --git a/tests/robotests/src/com/android/settings/development/GameUpdatePackageDevOptInPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/GameUpdatePackageDevOptInPreferenceControllerTest.java deleted file mode 100644 index 199cad6e191..00000000000 --- a/tests/robotests/src/com/android/settings/development/GameUpdatePackageDevOptInPreferenceControllerTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.development; - -import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_CODE_GUP_DEV_OPT_IN_APPS; -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.Activity; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.provider.Settings; - -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; - -import com.android.settings.R; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -@RunWith(RobolectricTestRunner.class) -public class GameUpdatePackageDevOptInPreferenceControllerTest { - - @Mock - private PreferenceScreen mPreferenceScreen; - @Mock - private DevelopmentSettingsDashboardFragment mFragment; - - private Context mContext; - private Preference mPreference; - private GameUpdatePackageDevOptInPreferenceController mController; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; - mController = spy(new GameUpdatePackageDevOptInPreferenceController(mContext, mFragment)); - mPreference = new Preference(mContext); - mPreference.setKey(mController.getPreferenceKey()); - - when(mPreferenceScreen.findPreference(mController.getPreferenceKey())) - .thenReturn(mPreference); - mController.displayPreference(mPreferenceScreen); - } - - @Test - public void handlePreferenceTreeClick_preferenceClicked_launchActivity() { - final Intent activityStartIntent = new Intent(mContext, AppPicker.class); - doReturn(activityStartIntent).when(mController).getActivityStartIntent(); - mController.handlePreferenceTreeClick(mPreference); - - verify(mFragment).startActivityForResult(activityStartIntent, - REQUEST_CODE_GUP_DEV_OPT_IN_APPS); - } - - @Test - public void updateState_foobarAppSelected_shouldUpdateSummaryWithGUPDevOptInAppLabel() { - final String selectedApp = "foobar"; - final ContentResolver contentResolver = mContext.getContentResolver(); - Settings.Global.putString(contentResolver, - Settings.Global.GUP_DEV_OPT_IN_APPS, selectedApp); - mController.updateState(mPreference); - - assertThat(mPreference.getSummary()).isEqualTo( - mContext.getString(R.string.gup_dev_opt_in_app_set, selectedApp)); - } - - @Test - public void updateState_noAppSelected_shouldUpdateSummaryWithNoAppSelected() { - final String selectedApp = null; - final ContentResolver contentResolver = mContext.getContentResolver(); - Settings.Global.putString(contentResolver, - Settings.Global.GUP_DEV_OPT_IN_APPS, selectedApp); - mController.updateState(mPreference); - - assertThat(mPreference.getSummary()).isEqualTo( - mContext.getString(R.string.gup_dev_opt_in_app_not_set)); - } - - @Test - public void onActivityResult_foobarAppSelected_shouldUpdateSummaryWithGUPDevOptInLabel() { - Intent activityResultIntent = new Intent(mContext, AppPicker.class); - final String appLabel = "foobar"; - activityResultIntent.setAction(appLabel); - final boolean result = mController - .onActivityResult(REQUEST_CODE_GUP_DEV_OPT_IN_APPS, Activity.RESULT_OK, - activityResultIntent); - - assertThat(result).isTrue(); - assertThat(mPreference.getSummary()).isEqualTo( - mContext.getString(R.string.gup_dev_opt_in_app_set, appLabel)); - } - - @Test - public void onActivityResult_badRequestCode_shouldReturnFalse() { - assertThat(mController.onActivityResult( - -1 /* requestCode */, -1 /* resultCode */, null /* intent */)).isFalse(); - } - - @Test - public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference() { - mController.onDeveloperOptionsSwitchDisabled(); - - assertThat(mPreference.isEnabled()).isFalse(); - assertThat(mPreference.getSummary()).isEqualTo( - mContext.getString(R.string.gup_dev_opt_in_app_not_set)); - } -} diff --git a/tests/robotests/src/com/android/settings/development/gup/GupPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/gup/GupPreferenceControllerTest.java new file mode 100644 index 00000000000..62e3475bd25 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/gup/GupPreferenceControllerTest.java @@ -0,0 +1,231 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.development.gup; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING; +import static com.android.settings.testutils.ApplicationTestUtils.buildInfo; +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.provider.Settings; + +import androidx.preference.ListPreference; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceGroup; +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; + +import java.util.Arrays; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class GupPreferenceControllerTest { + private static final int DEFAULT = 0; + private static final int GUP = 1; + private static final int NATIVE = 2; + private static final String TEST_APP_NAME = "testApp"; + private static final String TEST_PKG_NAME = "testPkg"; + + // Pre-installed Apps in the Mock PackageManager + private static final String APP_1 = "app1"; + private static final String APP_2 = "app2"; + private static final String APP_3 = "app3"; + + @Mock + private PackageManager mPackageManager; + @Mock + private PreferenceScreen mScreen; + + private Context mContext; + private PreferenceGroup mGroup; + private PreferenceManager mPreferenceManager; + private ContentResolver mResolver; + private GupPreferenceController mController; + private CharSequence[] mValueList; + private String mDialogTitle; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + mResolver = mContext.getContentResolver(); + mValueList = mContext.getResources().getStringArray(R.array.gup_app_preference_values); + mDialogTitle = mContext.getResources().getString(R.string.gup_app_preference_title); + } + + @Test + public void getAvailability_developmentSettingsEnabled_available() { + loadDefaultConfig(); + Settings.Global.putInt(mResolver, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + public void getAvailability_developmentSettingsDisabled_disabledDependentSetting() { + loadDefaultConfig(); + Settings.Global.putInt(mResolver, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0); + + assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING); + } + + @Test + public void displayPreference_shouldAddTwoPreferencesAndSortAscendingly() { + mockPackageManager(); + loadDefaultConfig(); + + // Only non-system app has preference + assertThat(mGroup.getPreferenceCount()).isEqualTo(2); + assertThat(mGroup.getPreference(0).getKey()).isEqualTo(APP_1); + assertThat(mGroup.getPreference(1).getKey()).isEqualTo(APP_3); + } + + @Test + public void createPreference_configDefault_shouldSetDefaultAttributes() { + loadDefaultConfig(); + final ListPreference preference = + mController.createListPreference(TEST_PKG_NAME, TEST_APP_NAME); + + assertThat(preference.getKey()).isEqualTo(TEST_PKG_NAME); + assertThat(preference.getTitle()).isEqualTo(TEST_APP_NAME); + assertThat(preference.getDialogTitle()).isEqualTo(mDialogTitle); + assertThat(preference.getEntries()).isEqualTo(mValueList); + assertThat(preference.getEntryValues()).isEqualTo(mValueList); + assertThat(preference.getEntry()).isEqualTo(mValueList[DEFAULT]); + assertThat(preference.getValue()).isEqualTo(mValueList[DEFAULT]); + assertThat(preference.getSummary()).isEqualTo(mValueList[DEFAULT]); + } + + @Test + public void createPreference_configGup_shouldSetGupAttributes() { + loadConfig(TEST_PKG_NAME, ""); + final ListPreference preference = + mController.createListPreference(TEST_PKG_NAME, TEST_APP_NAME); + + assertThat(preference.getKey()).isEqualTo(TEST_PKG_NAME); + assertThat(preference.getTitle()).isEqualTo(TEST_APP_NAME); + assertThat(preference.getDialogTitle()).isEqualTo(mDialogTitle); + assertThat(preference.getEntries()).isEqualTo(mValueList); + assertThat(preference.getEntryValues()).isEqualTo(mValueList); + assertThat(preference.getEntry()).isEqualTo(mValueList[GUP]); + assertThat(preference.getValue()).isEqualTo(mValueList[GUP]); + assertThat(preference.getSummary()).isEqualTo(mValueList[GUP]); + } + + @Test + public void createPreference_configNative_shouldSetNativeAttributes() { + loadConfig("", TEST_PKG_NAME); + final ListPreference preference = + mController.createListPreference(TEST_PKG_NAME, TEST_APP_NAME); + + assertThat(preference.getKey()).isEqualTo(TEST_PKG_NAME); + assertThat(preference.getTitle()).isEqualTo(TEST_APP_NAME); + assertThat(preference.getDialogTitle()).isEqualTo(mDialogTitle); + assertThat(preference.getEntries()).isEqualTo(mValueList); + assertThat(preference.getEntryValues()).isEqualTo(mValueList); + assertThat(preference.getEntry()).isEqualTo(mValueList[NATIVE]); + assertThat(preference.getValue()).isEqualTo(mValueList[NATIVE]); + assertThat(preference.getSummary()).isEqualTo(mValueList[NATIVE]); + } + + @Test + public void onPreferenceChange_selectDefault_shouldUpdateAttributesAndSettingsGlobal() { + loadDefaultConfig(); + final ListPreference preference = + mController.createListPreference(TEST_PKG_NAME, TEST_APP_NAME); + mController.onPreferenceChange(preference, mValueList[DEFAULT]); + + assertThat(preference.getSummary()).isEqualTo(mValueList[DEFAULT]); + assertThat(Settings.Global.getString(mResolver, Settings.Global.GUP_DEV_OPT_IN_APPS)) + .isEqualTo(""); + assertThat(Settings.Global.getString(mResolver, Settings.Global.GUP_DEV_OPT_OUT_APPS)) + .isEqualTo(""); + } + + @Test + public void onPreferenceChange_selectGup_shouldUpdateAttributesAndSettingsGlobal() { + loadDefaultConfig(); + final ListPreference preference = + mController.createListPreference(TEST_PKG_NAME, TEST_APP_NAME); + mController.onPreferenceChange(preference, mValueList[GUP]); + + assertThat(preference.getSummary()).isEqualTo(mValueList[GUP]); + assertThat(Settings.Global.getString(mResolver, Settings.Global.GUP_DEV_OPT_IN_APPS)) + .isEqualTo(TEST_PKG_NAME); + assertThat(Settings.Global.getString(mResolver, Settings.Global.GUP_DEV_OPT_OUT_APPS)) + .isEqualTo(""); + } + + @Test + public void onPreferenceChange_selectNative_shouldUpdateAttributesAndSettingsGlobal() { + loadDefaultConfig(); + final ListPreference preference = + mController.createListPreference(TEST_PKG_NAME, TEST_APP_NAME); + mController.onPreferenceChange(preference, mValueList[NATIVE]); + + assertThat(preference.getSummary()).isEqualTo(mValueList[NATIVE]); + assertThat(Settings.Global.getString(mResolver, Settings.Global.GUP_DEV_OPT_IN_APPS)) + .isEqualTo(""); + assertThat(Settings.Global.getString(mResolver, Settings.Global.GUP_DEV_OPT_OUT_APPS)) + .isEqualTo(TEST_PKG_NAME); + } + + private void mockPackageManager() { + final int uid = mContext.getUserId(); + final ApplicationInfo app1 = buildInfo(uid, APP_1, 0 /* flags */, 0 /* targetSdkVersion */); + final ApplicationInfo app2 = + buildInfo(uid, APP_2, ApplicationInfo.FLAG_SYSTEM, 0 /* targetSdkVersion */); + final ApplicationInfo app3 = buildInfo(uid, APP_3, 0 /* flags */, 0 /* targetSdkVersion */); + + when(mPackageManager.getInstalledApplications(0 /* flags */)) + .thenReturn(Arrays.asList(app3, app2, app1)); + when(mPackageManager.getApplicationLabel(app1)).thenReturn(APP_1); + when(mPackageManager.getApplicationLabel(app2)).thenReturn(APP_2); + when(mPackageManager.getApplicationLabel(app3)).thenReturn(APP_3); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + } + + private void loadDefaultConfig() { loadConfig("", ""); } + + private void loadConfig(String optIn, String optOut) { + Settings.Global.putString(mResolver, Settings.Global.GUP_DEV_OPT_IN_APPS, optIn); + Settings.Global.putString(mResolver, Settings.Global.GUP_DEV_OPT_OUT_APPS, optOut); + + mController = new GupPreferenceController(mContext, "testKey"); + mGroup = spy(new PreferenceCategory(mContext)); + final PreferenceManager preferenceManager = new PreferenceManager(mContext); + when(mGroup.getPreferenceManager()).thenReturn(preferenceManager); + when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mGroup); + mController.displayPreference(mScreen); + } +}