From 6f8048b0e36ab83f8f1ce96618db506c37e6aa56 Mon Sep 17 00:00:00 2001 From: Amin Shaikh Date: Thu, 3 Jan 2019 14:52:59 -0500 Subject: [PATCH] Add developer settings for theme overlays. - Extracted a OverlayCategoryPreferenceController from EmulateDisplayCutoutCategoryPreferenceController - Add 3 developer options for toggling categories of android theme customization Change-Id: I36fcebca6d8a6f109833a2bbca984c31e643e912 Fixes: 122308197 Test: m RunSettingsRoboTests; manually toggling settings --- res/values/strings.xml | 15 +- res/xml/development_settings.xml | 15 ++ .../DevelopmentSettingsDashboardFragment.java | 6 + ...lateDisplayCutoutPreferenceController.java | 140 +------------ .../OverlayCategoryPreferenceController.java | 188 ++++++++++++++++++ ...DisplayCutoutPreferenceControllerTest.java | 126 +----------- ...erlayCategoryPreferenceControllerTest.java | 184 +++++++++++++++++ 7 files changed, 415 insertions(+), 259 deletions(-) create mode 100644 src/com/android/settings/development/OverlayCategoryPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/development/OverlayCategoryPreferenceControllerTest.java diff --git a/res/values/strings.xml b/res/values/strings.xml index ea3979f3175..e6a496c3531 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9507,14 +9507,25 @@ Ranking object doesn\'t contain this key. + + Theming + + Accent color + + Headline / Body font + + Icon shape + + Device default + Display cutout display cutout, notch - - Device default + + Device default Special app access diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml index 4cdab33b158..b23a9cb3a03 100644 --- a/res/xml/development_settings.xml +++ b/res/xml/development_settings.xml @@ -559,4 +559,19 @@ + + + + + + diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 2a9919ab39f..b1e118a9345 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -492,6 +492,12 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new DefaultLaunchPreferenceController(context, "inactive_apps")); controllers.add(new AutofillLoggingLevelPreferenceController(context, lifecycle)); controllers.add(new AutofillResetOptionsPreferenceController(context)); + controllers.add(new OverlayCategoryPreferenceController(context, + "android.theme.customization.accent_color")); + controllers.add(new OverlayCategoryPreferenceController(context, + "android.theme.customization.font")); + controllers.add(new OverlayCategoryPreferenceController(context, + "android.theme.customization.adaptive_icon_shape")); return controllers; } diff --git a/src/com/android/settings/development/EmulateDisplayCutoutPreferenceController.java b/src/com/android/settings/development/EmulateDisplayCutoutPreferenceController.java index e3c16f3dc5d..ef88baa134b 100644 --- a/src/com/android/settings/development/EmulateDisplayCutoutPreferenceController.java +++ b/src/com/android/settings/development/EmulateDisplayCutoutPreferenceController.java @@ -16,50 +16,22 @@ package com.android.settings.development; -import static android.os.UserHandle.USER_SYSTEM; - import android.content.Context; import android.content.om.IOverlayManager; -import android.content.om.OverlayInfo; import android.content.pm.PackageManager; -import android.os.RemoteException; import android.os.ServiceManager; -import android.text.TextUtils; import android.view.DisplayCutout; import androidx.annotation.VisibleForTesting; -import androidx.preference.ListPreference; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; -import com.android.settings.R; -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.development.DeveloperOptionsPreferenceController; - -import java.util.Comparator; -import java.util.List; - -public class EmulateDisplayCutoutPreferenceController extends - DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener, - PreferenceControllerMixin { +public class EmulateDisplayCutoutPreferenceController extends OverlayCategoryPreferenceController { private static final String KEY = "display_cutout_emulation"; - private static final Comparator OVERLAY_INFO_COMPARATOR = - Comparator.comparingInt(a -> a.priority); - - private final IOverlayManager mOverlayManager; - private final boolean mAvailable; - - private ListPreference mPreference; - private PackageManager mPackageManager; @VisibleForTesting EmulateDisplayCutoutPreferenceController(Context context, PackageManager packageManager, IOverlayManager overlayManager) { - super(context); - mOverlayManager = overlayManager; - mPackageManager = packageManager; - mAvailable = overlayManager != null && getOverlayInfos().length > 0; + super(context, packageManager, overlayManager, DisplayCutout.EMULATION_OVERLAY_CATEGORY); } public EmulateDisplayCutoutPreferenceController(Context context) { @@ -67,116 +39,8 @@ public class EmulateDisplayCutoutPreferenceController extends .asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE))); } - @Override - public boolean isAvailable() { - return mAvailable; - } - @Override public String getPreferenceKey() { return KEY; } - - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - setPreference((ListPreference) screen.findPreference(getPreferenceKey())); - } - - @VisibleForTesting - void setPreference(ListPreference preference) { - mPreference = preference; - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - return setEmulationOverlay((String) newValue); - } - - private boolean setEmulationOverlay(String packageName) { - OverlayInfo[] overlays = getOverlayInfos(); - String currentPackageName = null; - for (OverlayInfo o : overlays) { - if (o.isEnabled()) { - currentPackageName = o.packageName; - } - } - - if (TextUtils.isEmpty(packageName) && TextUtils.isEmpty(currentPackageName) - || TextUtils.equals(packageName, currentPackageName)) { - // Already set. - return true; - } - - final boolean result; - try { - if (TextUtils.isEmpty(packageName)) { - result = mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM); - } else { - result = mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM); - } - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - updateState(mPreference); - return result; - } - - @Override - public void updateState(Preference preference) { - OverlayInfo[] overlays = getOverlayInfos(); - - CharSequence[] pkgs = new CharSequence[overlays.length + 1]; - CharSequence[] labels = new CharSequence[pkgs.length]; - - int current = 0; - pkgs[0] = ""; - labels[0] = mContext.getString(R.string.display_cutout_emulation_device_default); - - for (int i = 0; i < overlays.length; i++) { - OverlayInfo o = overlays[i]; - pkgs[i+1] = o.packageName; - if (o.isEnabled()) { - current = i+1; - } - } - for (int i = 1; i < pkgs.length; i++) { - try { - labels[i] = mPackageManager.getApplicationInfo(pkgs[i].toString(), 0) - .loadLabel(mPackageManager); - } catch (PackageManager.NameNotFoundException e) { - labels[i] = pkgs[i]; - } - } - - mPreference.setEntries(labels); - mPreference.setEntryValues(pkgs); - mPreference.setValueIndex(current); - mPreference.setSummary(labels[current]); - } - - private OverlayInfo[] getOverlayInfos() { - List overlayInfos; - try { - overlayInfos = mOverlayManager.getOverlayInfosForTarget("android", USER_SYSTEM); - for (int i = overlayInfos.size() - 1; i >= 0; i--) { - if (!DisplayCutout.EMULATION_OVERLAY_CATEGORY.equals( - overlayInfos.get(i).category)) { - overlayInfos.remove(i); - } - } - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - overlayInfos.sort(OVERLAY_INFO_COMPARATOR); - return overlayInfos.toArray(new OverlayInfo[overlayInfos.size()]); - } - - @Override - protected void onDeveloperOptionsSwitchDisabled() { - super.onDeveloperOptionsSwitchDisabled(); - setEmulationOverlay(""); - updateState(mPreference); - } - } diff --git a/src/com/android/settings/development/OverlayCategoryPreferenceController.java b/src/com/android/settings/development/OverlayCategoryPreferenceController.java new file mode 100644 index 00000000000..276049f2a50 --- /dev/null +++ b/src/com/android/settings/development/OverlayCategoryPreferenceController.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 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 android.os.UserHandle.USER_SYSTEM; + +import android.content.Context; +import android.content.om.IOverlayManager; +import android.content.om.OverlayInfo; +import android.content.pm.PackageManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.text.TextUtils; + +import com.android.settings.R; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.development.DeveloperOptionsPreferenceController; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import androidx.annotation.VisibleForTesting; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +/** + * Preference controller to allow users to choose an overlay from a list for a given category. + * The chosen overlay is enabled exclusively within its category. A default option is also + * exposed that disables all overlays in the given category. + */ +public class OverlayCategoryPreferenceController extends DeveloperOptionsPreferenceController + implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin { + @VisibleForTesting + static final String PACKAGE_DEVICE_DEFAULT = "package_device_default"; + private static final String OVERLAY_TARGET_PACKAGE = "android"; + private static final Comparator OVERLAY_INFO_COMPARATOR = + Comparator.comparingInt(a -> a.priority); + private final IOverlayManager mOverlayManager; + private final boolean mAvailable; + private final String mCategory; + private final PackageManager mPackageManager; + + private ListPreference mPreference; + + @VisibleForTesting + OverlayCategoryPreferenceController(Context context, PackageManager packageManager, + IOverlayManager overlayManager, String category) { + super(context); + mOverlayManager = overlayManager; + mPackageManager = packageManager; + mCategory = category; + mAvailable = overlayManager != null && !getOverlayInfos().isEmpty(); + } + + public OverlayCategoryPreferenceController(Context context, String category) { + this(context, context.getPackageManager(), IOverlayManager.Stub + .asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE)), category); + } + + @Override + public boolean isAvailable() { + return mAvailable; + } + + @Override + public String getPreferenceKey() { + return mCategory; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + setPreference((ListPreference) screen.findPreference(getPreferenceKey())); + } + + @VisibleForTesting + void setPreference(ListPreference preference) { + mPreference = preference; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + return setOverlay((String) newValue); + } + + private boolean setOverlay(String packageName) { + String currentPackageName = null; + for (OverlayInfo o : getOverlayInfos()) { + if (o.isEnabled()) { + currentPackageName = o.packageName; + } + } + + if (PACKAGE_DEVICE_DEFAULT.equals(packageName) && TextUtils.isEmpty(currentPackageName) + || TextUtils.equals(packageName, currentPackageName)) { + // Already set. + return true; + } + + final boolean result; + try { + if (PACKAGE_DEVICE_DEFAULT.equals(packageName)) { + result = mOverlayManager.setEnabled(currentPackageName, false, USER_SYSTEM); + } else { + result = mOverlayManager.setEnabledExclusiveInCategory(packageName, USER_SYSTEM); + } + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + updateState(mPreference); + return result; + } + + @Override + public void updateState(Preference preference) { + final List pkgs = new ArrayList<>(); + final List labels = new ArrayList<>(); + + String selectedPkg = PACKAGE_DEVICE_DEFAULT; + String selectedLabel = mContext.getString(R.string.overlay_option_device_default); + + // Add the default package / label before all of the overlays + pkgs.add(selectedPkg); + labels.add(selectedLabel); + + for (OverlayInfo overlayInfo : getOverlayInfos()) { + pkgs.add(overlayInfo.packageName); + try { + labels.add(mPackageManager.getApplicationInfo(overlayInfo.packageName, 0) + .loadLabel(mPackageManager).toString()); + } catch (PackageManager.NameNotFoundException e) { + labels.add(overlayInfo.packageName); + } + if (overlayInfo.isEnabled()) { + selectedPkg = pkgs.get(pkgs.size() - 1); + selectedLabel = labels.get(labels.size() - 1); + } + } + + mPreference.setEntries(labels.toArray(new String[labels.size()])); + mPreference.setEntryValues(pkgs.toArray(new String[pkgs.size()])); + mPreference.setValue(selectedPkg); + mPreference.setSummary(selectedLabel); + } + + private List getOverlayInfos() { + final List filteredInfos = new ArrayList<>(); + try { + List overlayInfos = mOverlayManager + .getOverlayInfosForTarget(OVERLAY_TARGET_PACKAGE, USER_SYSTEM); + for (OverlayInfo overlayInfo : overlayInfos) { + if (mCategory.equals(overlayInfo.category)) { + filteredInfos.add(overlayInfo); + } + } + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + filteredInfos.sort(OVERLAY_INFO_COMPARATOR); + return filteredInfos; + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + super.onDeveloperOptionsSwitchDisabled(); + // STOPSHIP b/122308197: reset the overlays to the set in + // Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES + setOverlay(PACKAGE_DEVICE_DEFAULT); + updateState(mPreference); + } + +} diff --git a/tests/robotests/src/com/android/settings/development/EmulateDisplayCutoutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/EmulateDisplayCutoutPreferenceControllerTest.java index 34112fd6a18..99509bba45d 100644 --- a/tests/robotests/src/com/android/settings/development/EmulateDisplayCutoutPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/development/EmulateDisplayCutoutPreferenceControllerTest.java @@ -18,44 +18,25 @@ package com.android.settings.development; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.AdditionalMatchers.aryEq; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.Context; import android.content.om.IOverlayManager; -import android.content.om.OverlayInfo; import android.content.pm.PackageManager; -import android.os.RemoteException; -import android.view.DisplayCutout; - -import androidx.preference.ListPreference; -import androidx.preference.PreferenceScreen; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Arrays; import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import androidx.preference.ListPreference; @RunWith(RobolectricTestRunner.class) public class EmulateDisplayCutoutPreferenceControllerTest { - - private static final OverlayInfo ONE_DISABLED = createFakeOverlay("emulation.one", false, 1); - private static final OverlayInfo ONE_ENABLED = createFakeOverlay("emulation.one", true, 1); - private static final OverlayInfo TWO_DISABLED = createFakeOverlay("emulation.two", false, 2); - private static final OverlayInfo TWO_ENABLED = createFakeOverlay("emulation.two", true, 2); - - @Mock - private Context mContext; @Mock private IOverlayManager mOverlayManager; @Mock @@ -67,112 +48,19 @@ public class EmulateDisplayCutoutPreferenceControllerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - when(mContext.getSystemService(Context.OVERLAY_SERVICE)).thenReturn(mOverlayManager); - mockCurrentOverlays(); when(mPackageManager.getApplicationInfo(any(), anyInt())) .thenThrow(PackageManager.NameNotFoundException.class); mController = createController(); mController.setPreference(mPreference); } - Object mockCurrentOverlays(OverlayInfo... overlays) { - try { - return when(mOverlayManager.getOverlayInfosForTarget(eq("android"), anyInt())) - .thenReturn(Arrays.asList(overlays)); - } catch (RemoteException re) { - return new ArrayList(); - } - } - @Test - public void isAvailable_true() { - mockCurrentOverlays(ONE_DISABLED, TWO_DISABLED); - - assertThat(createController().isAvailable()).isTrue(); - } - - @Test - public void isAvailable_false() { - mockCurrentOverlays(); - - assertThat(createController().isAvailable()).isFalse(); - } - - @Test - public void onPreferenceChange_enable() throws Exception { - mockCurrentOverlays(ONE_DISABLED, TWO_DISABLED); - - mController.onPreferenceChange(null, TWO_DISABLED.packageName); - - verify(mOverlayManager) - .setEnabledExclusiveInCategory(eq(TWO_DISABLED.packageName), anyInt()); - } - - @Test - public void onPreferenceChange_disable() throws Exception { - mockCurrentOverlays(ONE_DISABLED, TWO_ENABLED); - - mController.onPreferenceChange(null, ""); - - verify(mOverlayManager).setEnabled(eq(TWO_ENABLED.packageName), eq(false), anyInt()); - } - - @Test - public void updateState_enabled() { - mockCurrentOverlays(ONE_DISABLED, TWO_ENABLED); - - mController.updateState(null); - - verify(mPreference).setValueIndex(2); - } - - @Test - public void updateState_disabled() { - mockCurrentOverlays(ONE_DISABLED, TWO_DISABLED); - - mController.updateState(null); - - verify(mPreference).setValueIndex(0); - } - - @Test - public void ordered_by_priority() { - mockCurrentOverlays(TWO_DISABLED, ONE_DISABLED); - - mController.updateState(null); - - verify(mPreference).setEntryValues( - aryEq(new String[]{"", ONE_DISABLED.packageName, TWO_DISABLED.packageName})); - } - - @Test - public void onDeveloperOptionsSwitchDisabled() throws Exception { - mockCurrentOverlays(ONE_ENABLED, TWO_DISABLED); - final PreferenceScreen screen = mock(PreferenceScreen.class); - when(screen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); - mController.displayPreference(screen); - - mController.onDeveloperOptionsSwitchDisabled(); - - verify(mPreference).setEnabled(false); - verify(mOverlayManager).setEnabled(eq(ONE_ENABLED.packageName), eq(false), anyInt()); + public void getKey_returnsDisplayCutoutEmulation() { + assertThat(createController().getPreferenceKey()).isEqualTo("display_cutout_emulation"); } private EmulateDisplayCutoutPreferenceController createController() { - return new EmulateDisplayCutoutPreferenceController(mContext, mPackageManager, - mOverlayManager); - } - - private static OverlayInfo createFakeOverlay(String pkg, boolean enabled, int priority) { - final int state = (enabled) ? OverlayInfo.STATE_ENABLED : OverlayInfo.STATE_DISABLED; - - return new OverlayInfo(pkg /* packageName */, - "android" /* targetPackageName */, - DisplayCutout.EMULATION_OVERLAY_CATEGORY /* category */, - pkg + ".baseCodePath" /* baseCodePath */, - state /* state */, - 0 /* userId */, - priority, - true /* isStatic */); + return new EmulateDisplayCutoutPreferenceController(RuntimeEnvironment.application, + mPackageManager, mOverlayManager); } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/development/OverlayCategoryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/OverlayCategoryPreferenceControllerTest.java new file mode 100644 index 00000000000..3938b8993d7 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/OverlayCategoryPreferenceControllerTest.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 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.google.common.truth.Truth.assertThat; + +import static org.mockito.AdditionalMatchers.aryEq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.om.IOverlayManager; +import android.content.om.OverlayInfo; +import android.content.pm.PackageManager; +import android.os.RemoteException; + +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; + +import java.util.ArrayList; +import java.util.Arrays; + +import androidx.preference.ListPreference; +import androidx.preference.PreferenceScreen; + +@RunWith(RobolectricTestRunner.class) +public class OverlayCategoryPreferenceControllerTest { + + private static final OverlayInfo ONE_DISABLED = createFakeOverlay("overlay.one", false, 1); + private static final OverlayInfo ONE_ENABLED = createFakeOverlay("overlay.one", true, 1); + private static final OverlayInfo TWO_DISABLED = createFakeOverlay("overlay.two", false, 2); + private static final OverlayInfo TWO_ENABLED = createFakeOverlay("overlay.two", true, 2); + private static final String TEST_CATEGORY = "android.test.category"; + + @Mock + private IOverlayManager mOverlayManager; + @Mock + private PackageManager mPackageManager; + @Mock + private ListPreference mPreference; + private OverlayCategoryPreferenceController mController; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mockCurrentOverlays(); + when(mPackageManager.getApplicationInfo(any(), anyInt())) + .thenThrow(PackageManager.NameNotFoundException.class); + mController = createController(); + mController.setPreference(mPreference); + } + + Object mockCurrentOverlays(OverlayInfo... overlays) { + try { + return when(mOverlayManager.getOverlayInfosForTarget(eq("android"), anyInt())) + .thenReturn(Arrays.asList(overlays)); + } catch (RemoteException re) { + return new ArrayList(); + } + } + + @Test + public void getKey_returnsCategory() { + assertThat(createController().getPreferenceKey()).isEqualTo(TEST_CATEGORY); + } + + @Test + public void isAvailable_true() { + mockCurrentOverlays(ONE_DISABLED, TWO_DISABLED); + + assertThat(createController().isAvailable()).isTrue(); + } + + @Test + public void isAvailable_false() { + mockCurrentOverlays(); + + assertThat(createController().isAvailable()).isFalse(); + } + + @Test + public void onPreferenceChange_enable() throws Exception { + mockCurrentOverlays(ONE_DISABLED, TWO_DISABLED); + + mController.onPreferenceChange(null, TWO_DISABLED.packageName); + + verify(mOverlayManager) + .setEnabledExclusiveInCategory(eq(TWO_DISABLED.packageName), anyInt()); + } + + @Test + public void onPreferenceChange_disable() throws Exception { + mockCurrentOverlays(ONE_DISABLED, TWO_ENABLED); + + mController.onPreferenceChange( + null, OverlayCategoryPreferenceController.PACKAGE_DEVICE_DEFAULT); + + verify(mOverlayManager).setEnabled(eq(TWO_ENABLED.packageName), eq(false), anyInt()); + } + + @Test + public void updateState_enabled() { + mockCurrentOverlays(ONE_DISABLED, TWO_ENABLED); + + mController.updateState(null); + + verify(mPreference).setValue(TWO_ENABLED.packageName); + } + + @Test + public void updateState_disabled() { + mockCurrentOverlays(ONE_DISABLED, TWO_DISABLED); + + mController.updateState(null); + + verify(mPreference).setValue(OverlayCategoryPreferenceController.PACKAGE_DEVICE_DEFAULT); + } + + @Test + public void ordered_by_priority() { + mockCurrentOverlays(TWO_DISABLED, ONE_DISABLED); + + mController.updateState(null); + + verify(mPreference).setEntryValues( + aryEq(new String[]{ + OverlayCategoryPreferenceController.PACKAGE_DEVICE_DEFAULT, + ONE_DISABLED.packageName, + TWO_DISABLED.packageName})); + } + + @Test + public void onDeveloperOptionsSwitchDisabled() throws Exception { + mockCurrentOverlays(ONE_ENABLED, TWO_DISABLED); + final PreferenceScreen screen = mock(PreferenceScreen.class); + when(screen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); + mController.displayPreference(screen); + + mController.onDeveloperOptionsSwitchDisabled(); + + verify(mPreference).setEnabled(false); + verify(mOverlayManager).setEnabled(eq(ONE_ENABLED.packageName), eq(false), anyInt()); + } + + private OverlayCategoryPreferenceController createController() { + return new OverlayCategoryPreferenceController(RuntimeEnvironment.application, + mPackageManager, mOverlayManager, TEST_CATEGORY); + } + + private static OverlayInfo createFakeOverlay(String pkg, boolean enabled, int priority) { + final int state = (enabled) ? OverlayInfo.STATE_ENABLED : OverlayInfo.STATE_DISABLED; + + return new OverlayInfo(pkg /* packageName */, + "android" /* targetPackageName */, + TEST_CATEGORY/* category */, + pkg + ".baseCodePath" /* baseCodePath */, + state /* state */, + 0 /* userId */, + priority, + true /* isStatic */); + } +} \ No newline at end of file