/* * 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.ThemeOverlayApplier.ANDROID_PACKAGE; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_DYNAMIC_COLOR; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_FONT; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_ANDROID; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_LAUNCHER; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SETTINGS; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SYSUI; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_THEME_PICKER; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SHAPE; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE; import static com.android.systemui.theme.ThemeOverlayApplier.SETTINGS_PACKAGE; import static com.android.systemui.theme.ThemeOverlayApplier.SYSTEM_USER_CATEGORIES; import static com.android.systemui.theme.ThemeOverlayApplier.SYSUI_PACKAGE; import static com.android.systemui.theme.ThemeOverlayApplier.THEME_CATEGORIES; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.om.FabricatedOverlay; import android.content.om.OverlayIdentifier; import android.content.om.OverlayInfo; import android.content.om.OverlayManager; import android.content.om.OverlayManagerTransaction; import android.os.UserHandle; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.google.android.collect.Maps; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.util.concurrent.MoreExecutors; 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 ThemeOverlayApplierTest 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, new OverlayIdentifier(TEST_DISABLED_PREFIX + category)); } } private static final String THEMEPICKER_PACKAGE = "com.android.wallpaper"; private static final String LAUNCHER_PACKAGE = "com.android.launcher3"; private static final UserHandle TEST_USER = UserHandle.of(5); private static final UserHandle TEST_USER_MANAGED_PROFILE = UserHandle.of(6); private static final Set TEST_USER_HANDLES = Sets.newHashSet(TEST_USER_MANAGED_PROFILE); @Mock OverlayManager mOverlayManager; @Mock DumpManager mDumpManager; @Mock OverlayManagerTransaction.Builder mTransactionBuilder; private ThemeOverlayApplier mManager; private boolean mGetOverlayInfoEnabled = true; @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); mManager = new ThemeOverlayApplier(mOverlayManager, MoreExecutors.directExecutor(), LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager, MoreExecutors.directExecutor()) { @Override protected OverlayManagerTransaction.Builder getTransactionBuilder() { return mTransactionBuilder; } }; when(mOverlayManager.getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM)) .thenReturn(Lists.newArrayList( createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_DYNAMIC_COLOR, ANDROID_PACKAGE, OVERLAY_CATEGORY_DYNAMIC_COLOR, false), createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ACCENT_COLOR, ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, false), createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE, ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, 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_DYNAMIC_COLOR, ANDROID_PACKAGE, OVERLAY_CATEGORY_DYNAMIC_COLOR, true), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ACCENT_COLOR, ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, true), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE, ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, 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))); when(mOverlayManager.getOverlayInfosForTarget(THEMEPICKER_PACKAGE, UserHandle.SYSTEM)) .thenReturn(Lists.newArrayList( createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER, THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, false), createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER, THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, true))); OverlayInfo launcherTargetInfo = new OverlayInfo("packageName", LAUNCHER_PACKAGE, null, null, "/", 0, 0, 0, false); when(mOverlayManager.getOverlayInfo(any(OverlayIdentifier.class), any())) .thenAnswer(answer -> { if (mGetOverlayInfoEnabled) { return launcherTargetInfo; } return null; }); clearInvocations(mOverlayManager); verify(mDumpManager).registerDumpable(any(), any()); } @Test public void allCategoriesSpecified_allEnabledExclusively() { mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), TEST_USER_HANDLES, null); verify(mOverlayManager).commit(any()); for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), eq(TEST_USER.getIdentifier())); } } @Test public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() { mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), TEST_USER_HANDLES, null); for (Map.Entry entry : ALL_CATEGORIES_MAP.entrySet()) { if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) { verify(mTransactionBuilder).setEnabled(eq(entry.getValue()), eq(true), eq(UserHandle.SYSTEM.getIdentifier())); } else { verify(mTransactionBuilder, never()).setEnabled( eq(entry.getValue()), eq(true), eq(UserHandle.SYSTEM.getIdentifier())); } } } @Test public void allCategoriesSpecified_enabledForAllUserHandles() { Set userHandles = Sets.newHashSet(TEST_USER_HANDLES); mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), userHandles, null); for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), eq(TEST_USER.getIdentifier())); // Not enabled for work profile because the target package is LAUNCHER_PACKAGE verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true), eq(TEST_USER_MANAGED_PROFILE.getIdentifier())); } } @Test public void enablesOverlays_onlyIfItExistsForUser() { mGetOverlayInfoEnabled = false; Set userHandles = Sets.newHashSet(TEST_USER_HANDLES); mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), userHandles, null); for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true), eq(TEST_USER.getIdentifier())); } } @Test public void applyCurrentUserOverlays_createsPendingOverlays() { FabricatedOverlay[] pendingCreation = new FabricatedOverlay[]{ mock(FabricatedOverlay.class) }; mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation, TEST_USER.getIdentifier(), TEST_USER_HANDLES, null); for (FabricatedOverlay overlay : pendingCreation) { verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay)); } } @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, null, TEST_USER.getIdentifier(), TEST_USER_HANDLES, null); for (OverlayIdentifier overlayPackage : categoryToPackage.values()) { verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), eq(TEST_USER.getIdentifier())); } verify(mTransactionBuilder).setEnabled( eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_SETTINGS)), eq(false), eq(TEST_USER.getIdentifier())); verify(mTransactionBuilder).setEnabled( eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID)), eq(false), eq(TEST_USER.getIdentifier())); } @Test public void zeroCategoriesSpecified_allDisabled() { mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(), TEST_USER_HANDLES, null); for (String category : THEME_CATEGORIES) { verify(mTransactionBuilder).setEnabled( eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + category)), eq(false), eq(TEST_USER.getIdentifier())); } } @Test public void nonThemeCategorySpecified_ignored() { Map categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category")); mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(), TEST_USER_HANDLES, null); verify(mTransactionBuilder, never()).setEnabled( eq(new OverlayIdentifier("com.example.blah.category")), eq(false), eq(TEST_USER.getIdentifier())); verify(mTransactionBuilder, never()).setEnabled( eq(new OverlayIdentifier("com.example.blah.category")), eq(true), eq(TEST_USER.getIdentifier())); } private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName, String category, boolean enabled) { return new OverlayInfo(packageName, null, targetPackageName, null, category, "", enabled ? OverlayInfo.STATE_ENABLED : OverlayInfo.STATE_DISABLED, 0, 0, false, false); } }