diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index ce04638a51e59..a067cd2022479 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -316,6 +316,7 @@ com.android.systemui.SliceBroadcastRelayHandler com.android.systemui.SizeCompatModeActivityController com.android.systemui.statusbar.notification.InstantAppNotifier + com.android.systemui.theme.ThemeOverlayController @@ -486,4 +487,7 @@ 5 + + com.android.launcher3 + diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java new file mode 100644 index 0000000000000..f318f8f94e50a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 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.systemui.theme; + +import android.app.ActivityManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.om.OverlayManager; +import android.content.pm.UserInfo; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.SystemUI; + +import com.google.android.collect.Sets; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Map; +import java.util.Set; + +/** + * Controls the application of theme overlays across the system for all users. + * This service is responsible for: + * - Observing changes to Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES and applying the + * corresponding overlays across the system + * - Observing user switches, applying the overlays for the current user to user 0 (for systemui) + * - Observing work profile changes and applying overlays from the primary user to their + * associated work profiles + */ +public class ThemeOverlayController extends SystemUI { + private static final String TAG = "ThemeOverlayController"; + private static final boolean DEBUG = false; + + private ThemeOverlayManager mThemeManager; + private UserManager mUserManager; + + @Override + public void start() { + if (DEBUG) Log.d(TAG, "Start"); + mUserManager = mContext.getSystemService(UserManager.class); + mThemeManager = new ThemeOverlayManager( + mContext.getSystemService(OverlayManager.class), + mContext.getString(R.string.launcher_overlayable_package)); + final Handler bgHandler = Dependency.get(Dependency.BG_HANDLER); + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_SWITCHED); + filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); + mContext.registerReceiverAsUser(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) Log.d(TAG, "Updating overlays for user switch / profile added."); + updateThemeOverlays(); + } + }, UserHandle.ALL, filter, null, bgHandler); + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), + false, + new ContentObserver(bgHandler) { + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + if (DEBUG) Log.d(TAG, "Overlay changed for user: " + userId); + if (ActivityManager.getCurrentUser() == userId) { + updateThemeOverlays(); + } + } + }, + UserHandle.USER_ALL); + } + + private void updateThemeOverlays() { + final int currentUser = ActivityManager.getCurrentUser(); + final String overlayPackageJson = Settings.Secure.getStringForUser( + mContext.getContentResolver(), Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, + currentUser); + if (DEBUG) Log.d(TAG, "updateThemeOverlays: " + overlayPackageJson); + final Map categoryToPackage = new ArrayMap<>(); + if (!TextUtils.isEmpty(overlayPackageJson)) { + try { + JSONObject object = new JSONObject(overlayPackageJson); + for (String category : ThemeOverlayManager.THEME_CATEGORIES) { + if (object.has(category)) { + categoryToPackage.put(category, object.getString(category)); + } + } + } catch (JSONException e) { + Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e); + } + } + Set userHandles = Sets.newHashSet(UserHandle.of(currentUser)); + for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) { + if (userInfo.isManagedProfile()) { + userHandles.add(userInfo.getUserHandle()); + } + } + mThemeManager.applyCurrentUserOverlays(categoryToPackage, userHandles); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java new file mode 100644 index 0000000000000..1a9fd5315c32a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayManager.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 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.systemui.theme; + +import android.content.om.OverlayInfo; +import android.content.om.OverlayManager; +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; + +import com.google.android.collect.Sets; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +class ThemeOverlayManager { + private static final String TAG = "ThemeOverlayManager"; + private static final boolean DEBUG = false; + + @VisibleForTesting + static final String ANDROID_PACKAGE = "android"; + @VisibleForTesting + static final String SETTINGS_PACKAGE = "com.android.settings"; + @VisibleForTesting + static final String SYSUI_PACKAGE = "com.android.systemui"; + + @VisibleForTesting + static final String OVERLAY_CATEGORY_COLOR = "android.theme.customization.accent_color"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_FONT = "android.theme.customization.font"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_SHAPE = + "android.theme.customization.adaptive_icon_shape"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_ICON_ANDROID = + "android.theme.customization.icon_pack.android"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_ICON_SYSUI = + "android.theme.customization.icon_pack.systemui"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_ICON_SETTINGS = + "android.theme.customization.icon_pack.settings"; + @VisibleForTesting + static final String OVERLAY_CATEGORY_ICON_LAUNCHER = + "android.theme.customization.icon_pack.launcher"; + + /* All theme customization categories used by the system. */ + static final Set THEME_CATEGORIES = Sets.newHashSet( + OVERLAY_CATEGORY_COLOR, + OVERLAY_CATEGORY_FONT, + OVERLAY_CATEGORY_SHAPE, + OVERLAY_CATEGORY_ICON_ANDROID, + OVERLAY_CATEGORY_ICON_SYSUI, + OVERLAY_CATEGORY_ICON_SETTINGS, + OVERLAY_CATEGORY_ICON_LAUNCHER); + + /* Categories that need to applied to the current user as well as the system user. */ + @VisibleForTesting + static final Set SYSTEM_USER_CATEGORIES = Sets.newHashSet( + OVERLAY_CATEGORY_COLOR, + OVERLAY_CATEGORY_FONT, + OVERLAY_CATEGORY_SHAPE, + OVERLAY_CATEGORY_ICON_ANDROID, + OVERLAY_CATEGORY_ICON_SYSUI); + + /* Allowed overlay categories for each target package. */ + private final Map> mTargetPackageToCategories = new ArrayMap<>(); + /* Target package for each overlay category. */ + private final Map mCategoryToTargetPackage = new ArrayMap<>(); + private final OverlayManager mOverlayManager; + private final String mLauncherPackage; + + ThemeOverlayManager(OverlayManager overlayManager, String launcherPackage) { + mOverlayManager = overlayManager; + mLauncherPackage = launcherPackage; + mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet( + OVERLAY_CATEGORY_COLOR, OVERLAY_CATEGORY_FONT, + OVERLAY_CATEGORY_SHAPE, OVERLAY_CATEGORY_ICON_ANDROID)); + mTargetPackageToCategories.put(SYSUI_PACKAGE, + Sets.newHashSet(OVERLAY_CATEGORY_ICON_SYSUI)); + mTargetPackageToCategories.put(SETTINGS_PACKAGE, + Sets.newHashSet(OVERLAY_CATEGORY_ICON_SETTINGS)); + mTargetPackageToCategories.put(mLauncherPackage, + Sets.newHashSet(OVERLAY_CATEGORY_ICON_LAUNCHER)); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_COLOR, ANDROID_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_SHAPE, ANDROID_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_ANDROID, ANDROID_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SYSUI, SYSUI_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_SETTINGS, SETTINGS_PACKAGE); + mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_LAUNCHER, mLauncherPackage); + } + + /** + * Apply the set of overlay packages to the set of {@code UserHandle}s provided. Overlays that + * affect sysui will also be applied to the system user. + */ + void applyCurrentUserOverlays( + Map categoryToPackage, Set userHandles) { + final Map> categorySplit = THEME_CATEGORIES.stream().collect( + Collectors.partitioningBy((category) -> categoryToPackage.containsKey(category))); + final List overlayCategoriesToEnable = categorySplit.get(true); + final List overlayCategoriesToDisable = categorySplit.get(false); + + // Disable all overlays that have not been specified in the user setting. + final List overlays = new ArrayList<>(); + overlayCategoriesToDisable.stream() + .map(category -> mCategoryToTargetPackage.get(category)) + .collect(Collectors.toSet()) + .forEach(targetPackage -> overlays.addAll(mOverlayManager + .getOverlayInfosForTarget(targetPackage, UserHandle.SYSTEM))); + overlays.stream() + .filter(o -> + mTargetPackageToCategories.get(o.targetPackageName).contains(o.category)) + .filter(o -> overlayCategoriesToDisable.contains(o.category)) + .filter(o -> o.isEnabled()) + .forEach(o -> setEnabled(o.packageName, o.category, userHandles, false)); + + + // Enable all overlays specified in the user setting. + overlayCategoriesToEnable.forEach((category) -> + setEnabled(categoryToPackage.get(category), category, userHandles, true)); + } + + private void setEnabled( + String packageName, String category, Set handles, boolean enabled) { + for (UserHandle userHandle : handles) { + setEnabled(packageName, userHandle, enabled); + } + if (!handles.contains(UserHandle.SYSTEM) && SYSTEM_USER_CATEGORIES.contains(category)) { + setEnabled(packageName, UserHandle.SYSTEM, enabled); + } + } + + private void setEnabled(String pkg, UserHandle userHandle, boolean enabled) { + if (DEBUG) Log.d(TAG, String.format("setEnabled: %s %s %b", pkg, userHandle, enabled)); + if (enabled) { + mOverlayManager.setEnabledExclusiveInCategory(pkg, userHandle); + } else { + mOverlayManager.setEnabled(pkg, false, userHandle); + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java new file mode 100644 index 0000000000000..da039a4030870 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayManagerTest.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 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.systemui.theme; + +import static com.android.systemui.theme.ThemeOverlayManager.ANDROID_PACKAGE; +import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_COLOR; +import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_FONT; +import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_ANDROID; +import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_LAUNCHER; +import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_SETTINGS; +import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_ICON_SYSUI; +import static com.android.systemui.theme.ThemeOverlayManager.OVERLAY_CATEGORY_SHAPE; +import static com.android.systemui.theme.ThemeOverlayManager.SETTINGS_PACKAGE; +import static com.android.systemui.theme.ThemeOverlayManager.SYSTEM_USER_CATEGORIES; +import static com.android.systemui.theme.ThemeOverlayManager.SYSUI_PACKAGE; +import static com.android.systemui.theme.ThemeOverlayManager.THEME_CATEGORIES; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.om.OverlayInfo; +import android.content.om.OverlayManager; +import android.os.UserHandle; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import com.google.android.collect.Maps; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class ThemeOverlayManagerTest extends SysuiTestCase { + private static final String TEST_DISABLED_PREFIX = "com.example."; + private static final String TEST_ENABLED_PREFIX = "com.example.enabled."; + + private static final Map ALL_CATEGORIES_MAP = Maps.newArrayMap(); + + static { + for (String category : THEME_CATEGORIES) { + ALL_CATEGORIES_MAP.put(category, TEST_DISABLED_PREFIX + category); + } + } + + private static final String LAUNCHER_PACKAGE = "com.android.launcher3"; + private static final UserHandle TEST_USER = UserHandle.of(5); + private static final Set TEST_USER_HANDLES = Sets.newHashSet(TEST_USER); + + @Mock + OverlayManager mOverlayManager; + + private ThemeOverlayManager mManager; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + mManager = new ThemeOverlayManager(mOverlayManager, LAUNCHER_PACKAGE); + when(mOverlayManager.getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM)) + .thenReturn(Lists.newArrayList( + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_COLOR, + ANDROID_PACKAGE, OVERLAY_CATEGORY_COLOR, false), + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_FONT, + ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, false), + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SHAPE, + ANDROID_PACKAGE, OVERLAY_CATEGORY_SHAPE, false), + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID, + ANDROID_PACKAGE, OVERLAY_CATEGORY_ICON_ANDROID, false), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_COLOR, + ANDROID_PACKAGE, OVERLAY_CATEGORY_COLOR, true), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_FONT, + ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, true), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SHAPE, + ANDROID_PACKAGE, OVERLAY_CATEGORY_SHAPE, true), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID, + ANDROID_PACKAGE, OVERLAY_CATEGORY_ICON_ANDROID, true))); + when(mOverlayManager.getOverlayInfosForTarget(SYSUI_PACKAGE, UserHandle.SYSTEM)) + .thenReturn(Lists.newArrayList( + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_SYSUI, + SYSUI_PACKAGE, OVERLAY_CATEGORY_ICON_SYSUI, false), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_SYSUI, + SYSUI_PACKAGE, OVERLAY_CATEGORY_ICON_SYSUI, true))); + when(mOverlayManager.getOverlayInfosForTarget(SETTINGS_PACKAGE, UserHandle.SYSTEM)) + .thenReturn(Lists.newArrayList( + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_SETTINGS, + SETTINGS_PACKAGE, OVERLAY_CATEGORY_ICON_SETTINGS, false), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_SETTINGS, + SETTINGS_PACKAGE, OVERLAY_CATEGORY_ICON_SETTINGS, true))); + when(mOverlayManager.getOverlayInfosForTarget(LAUNCHER_PACKAGE, UserHandle.SYSTEM)) + .thenReturn(Lists.newArrayList( + createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_LAUNCHER, + LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, false), + createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_LAUNCHER, + LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, true))); + } + + @Test + public void allCategoriesSpecified_allEnabledExclusively() { + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, TEST_USER_HANDLES); + + for (String overlayPackage : ALL_CATEGORIES_MAP.values()) { + verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, TEST_USER); + } + } + + @Test + public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() { + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, TEST_USER_HANDLES); + + for (Map.Entry entry : ALL_CATEGORIES_MAP.entrySet()) { + if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) { + verify(mOverlayManager).setEnabledExclusiveInCategory( + entry.getValue(), UserHandle.SYSTEM); + } else { + verify(mOverlayManager, never()).setEnabledExclusiveInCategory( + entry.getValue(), UserHandle.SYSTEM); + } + } + } + + @Test + public void allCategoriesSpecified_enabledForAllUserHandles() { + Set userHandles = Sets.newHashSet(TEST_USER_HANDLES); + UserHandle newUserHandle = UserHandle.of(10); + userHandles.add(newUserHandle); + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, userHandles); + + for (String overlayPackage : ALL_CATEGORIES_MAP.values()) { + verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, TEST_USER); + verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, newUserHandle); + } + } + + @Test + public void allCategoriesSpecified_overlayManagerNotQueried() { + mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, TEST_USER_HANDLES); + + verify(mOverlayManager, never()) + .getOverlayInfosForTarget(anyString(), any(UserHandle.class)); + } + + @Test + public void someCategoriesSpecified_specifiedEnabled_unspecifiedDisabled() { + Map categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); + categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS); + categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID); + + mManager.applyCurrentUserOverlays(categoryToPackage, TEST_USER_HANDLES); + + for (String overlayPackage : categoryToPackage.values()) { + verify(mOverlayManager).setEnabledExclusiveInCategory(overlayPackage, TEST_USER); + } + verify(mOverlayManager).setEnabled(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_SETTINGS, + false, TEST_USER); + verify(mOverlayManager).setEnabled(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID, + false, TEST_USER); + } + + @Test + public void zeroCategoriesSpecified_allDisabled() { + mManager.applyCurrentUserOverlays(Maps.newArrayMap(), TEST_USER_HANDLES); + + for (String category : THEME_CATEGORIES) { + verify(mOverlayManager).setEnabled(TEST_ENABLED_PREFIX + category, false, TEST_USER); + } + } + + @Test + public void nonThemeCategorySpecified_ignored() { + Map categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); + categoryToPackage.put("blah.category", "com.example.blah.category"); + + mManager.applyCurrentUserOverlays(categoryToPackage, TEST_USER_HANDLES); + + verify(mOverlayManager, never()).setEnabled("com.example.blah.category", false, TEST_USER); + verify(mOverlayManager, never()).setEnabledExclusiveInCategory("com.example.blah.category", + TEST_USER); + } + + @Test + public void overlayManagerOnlyQueriedForUnspecifiedPackages() { + Map categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); + categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS); + + mManager.applyCurrentUserOverlays(categoryToPackage, TEST_USER_HANDLES); + + verify(mOverlayManager).getOverlayInfosForTarget(SETTINGS_PACKAGE, UserHandle.SYSTEM); + verify(mOverlayManager, never()).getOverlayInfosForTarget(ANDROID_PACKAGE, + UserHandle.SYSTEM); + verify(mOverlayManager, never()).getOverlayInfosForTarget(SYSUI_PACKAGE, UserHandle.SYSTEM); + verify(mOverlayManager, never()).getOverlayInfosForTarget(LAUNCHER_PACKAGE, + UserHandle.SYSTEM); + } + + private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName, + String category, boolean enabled) { + return new OverlayInfo(packageName, targetPackageName, null, category, "", + enabled ? OverlayInfo.STATE_ENABLED : OverlayInfo.STATE_DISABLED, 0, 0, false); + } +}