1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.systemui.theme; 17 18 import static com.android.systemui.theme.ThemeOverlayApplier.ANDROID_PACKAGE; 19 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR; 20 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_DYNAMIC_COLOR; 21 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_FONT; 22 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_ANDROID; 23 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_LAUNCHER; 24 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SETTINGS; 25 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_SYSUI; 26 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_THEME_PICKER; 27 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SHAPE; 28 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE; 29 import static com.android.systemui.theme.ThemeOverlayApplier.SETTINGS_PACKAGE; 30 import static com.android.systemui.theme.ThemeOverlayApplier.SYSTEM_USER_CATEGORIES; 31 import static com.android.systemui.theme.ThemeOverlayApplier.SYSUI_PACKAGE; 32 import static com.android.systemui.theme.ThemeOverlayApplier.THEME_CATEGORIES; 33 34 import static org.mockito.ArgumentMatchers.any; 35 import static org.mockito.ArgumentMatchers.eq; 36 import static org.mockito.Mockito.clearInvocations; 37 import static org.mockito.Mockito.mock; 38 import static org.mockito.Mockito.never; 39 import static org.mockito.Mockito.verify; 40 import static org.mockito.Mockito.when; 41 42 import android.content.om.FabricatedOverlay; 43 import android.content.om.OverlayIdentifier; 44 import android.content.om.OverlayInfo; 45 import android.content.om.OverlayManager; 46 import android.content.om.OverlayManagerTransaction; 47 import android.os.UserHandle; 48 import android.testing.TestableLooper; 49 50 import androidx.test.ext.junit.runners.AndroidJUnit4; 51 import androidx.test.filters.SmallTest; 52 53 import com.android.systemui.SysuiTestCase; 54 import com.android.systemui.dump.DumpManager; 55 56 import com.google.android.collect.Maps; 57 import com.google.common.collect.Lists; 58 import com.google.common.collect.Sets; 59 import com.google.common.util.concurrent.MoreExecutors; 60 61 import org.junit.Before; 62 import org.junit.Test; 63 import org.junit.runner.RunWith; 64 import org.mockito.Mock; 65 import org.mockito.MockitoAnnotations; 66 67 import java.util.HashMap; 68 import java.util.Map; 69 import java.util.Set; 70 71 @SmallTest 72 @RunWith(AndroidJUnit4.class) 73 @TestableLooper.RunWithLooper 74 public class ThemeOverlayApplierTest extends SysuiTestCase { 75 private static final String TEST_DISABLED_PREFIX = "com.example."; 76 private static final String TEST_ENABLED_PREFIX = "com.example.enabled."; 77 78 private static final Map<String, OverlayIdentifier> ALL_CATEGORIES_MAP = Maps.newArrayMap(); 79 80 static { 81 for (String category : THEME_CATEGORIES) { ALL_CATEGORIES_MAP.put(category, new OverlayIdentifier(TEST_DISABLED_PREFIX + category))82 ALL_CATEGORIES_MAP.put(category, 83 new OverlayIdentifier(TEST_DISABLED_PREFIX + category)); 84 } 85 } 86 87 private static final String THEMEPICKER_PACKAGE = "com.android.wallpaper"; 88 private static final String LAUNCHER_PACKAGE = "com.android.launcher3"; 89 private static final UserHandle TEST_USER = UserHandle.of(5); 90 private static final UserHandle TEST_USER_MANAGED_PROFILE = UserHandle.of(6); 91 private static final Set<UserHandle> TEST_USER_HANDLES = 92 Sets.newHashSet(TEST_USER_MANAGED_PROFILE); 93 94 @Mock 95 OverlayManager mOverlayManager; 96 @Mock 97 DumpManager mDumpManager; 98 @Mock 99 OverlayManagerTransaction.Builder mTransactionBuilder; 100 101 private ThemeOverlayApplier mManager; 102 private boolean mGetOverlayInfoEnabled = true; 103 104 @Before setup()105 public void setup() throws Exception { 106 MockitoAnnotations.initMocks(this); 107 mManager = new ThemeOverlayApplier(mOverlayManager, MoreExecutors.directExecutor(), 108 LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager, 109 MoreExecutors.directExecutor()) { 110 @Override 111 protected OverlayManagerTransaction.Builder getTransactionBuilder() { 112 return mTransactionBuilder; 113 } 114 }; 115 when(mOverlayManager.getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM)) 116 .thenReturn(Lists.newArrayList( 117 createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_DYNAMIC_COLOR, 118 ANDROID_PACKAGE, OVERLAY_CATEGORY_DYNAMIC_COLOR, false), 119 createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ACCENT_COLOR, 120 ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, false), 121 createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE, 122 ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, false), 123 createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_FONT, 124 ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, false), 125 createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SHAPE, 126 ANDROID_PACKAGE, OVERLAY_CATEGORY_SHAPE, false), 127 createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID, 128 ANDROID_PACKAGE, OVERLAY_CATEGORY_ICON_ANDROID, false), 129 createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_DYNAMIC_COLOR, 130 ANDROID_PACKAGE, OVERLAY_CATEGORY_DYNAMIC_COLOR, true), 131 createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ACCENT_COLOR, 132 ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, true), 133 createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE, 134 ANDROID_PACKAGE, OVERLAY_CATEGORY_SYSTEM_PALETTE, true), 135 createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_FONT, 136 ANDROID_PACKAGE, OVERLAY_CATEGORY_FONT, true), 137 createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SHAPE, 138 ANDROID_PACKAGE, OVERLAY_CATEGORY_SHAPE, true), 139 createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID, 140 ANDROID_PACKAGE, OVERLAY_CATEGORY_ICON_ANDROID, true))); 141 when(mOverlayManager.getOverlayInfosForTarget(SYSUI_PACKAGE, UserHandle.SYSTEM)) 142 .thenReturn(Lists.newArrayList( 143 createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_SYSUI, 144 SYSUI_PACKAGE, OVERLAY_CATEGORY_ICON_SYSUI, false), 145 createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_SYSUI, 146 SYSUI_PACKAGE, OVERLAY_CATEGORY_ICON_SYSUI, true))); 147 when(mOverlayManager.getOverlayInfosForTarget(SETTINGS_PACKAGE, UserHandle.SYSTEM)) 148 .thenReturn(Lists.newArrayList( 149 createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_SETTINGS, 150 SETTINGS_PACKAGE, OVERLAY_CATEGORY_ICON_SETTINGS, false), 151 createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_SETTINGS, 152 SETTINGS_PACKAGE, OVERLAY_CATEGORY_ICON_SETTINGS, true))); 153 when(mOverlayManager.getOverlayInfosForTarget(LAUNCHER_PACKAGE, UserHandle.SYSTEM)) 154 .thenReturn(Lists.newArrayList( 155 createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_LAUNCHER, 156 LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, false), 157 createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_LAUNCHER, 158 LAUNCHER_PACKAGE, OVERLAY_CATEGORY_ICON_LAUNCHER, true))); 159 when(mOverlayManager.getOverlayInfosForTarget(THEMEPICKER_PACKAGE, UserHandle.SYSTEM)) 160 .thenReturn(Lists.newArrayList( 161 createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER, 162 THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, false), 163 createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_THEME_PICKER, 164 THEMEPICKER_PACKAGE, OVERLAY_CATEGORY_ICON_THEME_PICKER, true))); 165 166 OverlayInfo launcherTargetInfo = new OverlayInfo("packageName", LAUNCHER_PACKAGE, 167 null, null, "/", 0, 0, 0, false); 168 when(mOverlayManager.getOverlayInfo(any(OverlayIdentifier.class), any())) 169 .thenAnswer(answer -> { 170 if (mGetOverlayInfoEnabled) { 171 return launcherTargetInfo; 172 } 173 return null; 174 }); 175 clearInvocations(mOverlayManager); 176 verify(mDumpManager).registerDumpable(any(), any()); 177 } 178 179 @Test allCategoriesSpecified_allEnabledExclusively()180 public void allCategoriesSpecified_allEnabledExclusively() { 181 mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), 182 TEST_USER_HANDLES, null); 183 verify(mOverlayManager).commit(any()); 184 185 for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { 186 verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), 187 eq(TEST_USER.getIdentifier())); 188 } 189 } 190 191 @Test allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser()192 public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() { 193 mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), 194 TEST_USER_HANDLES, null); 195 196 for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) { 197 if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) { 198 verify(mTransactionBuilder).setEnabled(eq(entry.getValue()), eq(true), 199 eq(UserHandle.SYSTEM.getIdentifier())); 200 } else { 201 verify(mTransactionBuilder, never()).setEnabled( 202 eq(entry.getValue()), eq(true), eq(UserHandle.SYSTEM.getIdentifier())); 203 } 204 } 205 } 206 207 @Test allCategoriesSpecified_enabledForAllUserHandles()208 public void allCategoriesSpecified_enabledForAllUserHandles() { 209 Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES); 210 mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), 211 userHandles, null); 212 213 for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { 214 verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), 215 eq(TEST_USER.getIdentifier())); 216 // Not enabled for work profile because the target package is LAUNCHER_PACKAGE 217 verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true), 218 eq(TEST_USER_MANAGED_PROFILE.getIdentifier())); 219 } 220 } 221 222 @Test enablesOverlays_onlyIfItExistsForUser()223 public void enablesOverlays_onlyIfItExistsForUser() { 224 mGetOverlayInfoEnabled = false; 225 226 Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES); 227 mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(), 228 userHandles, null); 229 230 for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) { 231 verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true), 232 eq(TEST_USER.getIdentifier())); 233 } 234 } 235 236 @Test applyCurrentUserOverlays_createsPendingOverlays()237 public void applyCurrentUserOverlays_createsPendingOverlays() { 238 FabricatedOverlay[] pendingCreation = new FabricatedOverlay[]{ 239 mock(FabricatedOverlay.class) 240 }; 241 mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation, 242 TEST_USER.getIdentifier(), TEST_USER_HANDLES, null); 243 244 for (FabricatedOverlay overlay : pendingCreation) { 245 verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay)); 246 } 247 } 248 249 @Test someCategoriesSpecified_specifiedEnabled_unspecifiedDisabled()250 public void someCategoriesSpecified_specifiedEnabled_unspecifiedDisabled() { 251 Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); 252 categoryToPackage.remove(OVERLAY_CATEGORY_ICON_SETTINGS); 253 categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID); 254 255 mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(), 256 TEST_USER_HANDLES, null); 257 258 for (OverlayIdentifier overlayPackage : categoryToPackage.values()) { 259 verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true), 260 eq(TEST_USER.getIdentifier())); 261 } 262 verify(mTransactionBuilder).setEnabled( 263 eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_SETTINGS)), 264 eq(false), eq(TEST_USER.getIdentifier())); 265 verify(mTransactionBuilder).setEnabled( 266 eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID)), 267 eq(false), eq(TEST_USER.getIdentifier())); 268 } 269 270 @Test zeroCategoriesSpecified_allDisabled()271 public void zeroCategoriesSpecified_allDisabled() { 272 mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(), 273 TEST_USER_HANDLES, null); 274 275 for (String category : THEME_CATEGORIES) { 276 verify(mTransactionBuilder).setEnabled( 277 eq(new OverlayIdentifier(TEST_ENABLED_PREFIX + category)), eq(false), 278 eq(TEST_USER.getIdentifier())); 279 } 280 } 281 282 @Test nonThemeCategorySpecified_ignored()283 public void nonThemeCategorySpecified_ignored() { 284 Map<String, OverlayIdentifier> categoryToPackage = new HashMap<>(ALL_CATEGORIES_MAP); 285 categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category")); 286 287 mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(), 288 TEST_USER_HANDLES, null); 289 290 verify(mTransactionBuilder, never()).setEnabled( 291 eq(new OverlayIdentifier("com.example.blah.category")), eq(false), 292 eq(TEST_USER.getIdentifier())); 293 verify(mTransactionBuilder, never()).setEnabled( 294 eq(new OverlayIdentifier("com.example.blah.category")), eq(true), 295 eq(TEST_USER.getIdentifier())); 296 } 297 createOverlayInfo(String packageName, String targetPackageName, String category, boolean enabled)298 private static OverlayInfo createOverlayInfo(String packageName, String targetPackageName, 299 String category, boolean enabled) { 300 return new OverlayInfo(packageName, null, targetPackageName, null, category, "", 301 enabled ? OverlayInfo.STATE_ENABLED : OverlayInfo.STATE_DISABLED, 0, 0, false, 302 false); 303 } 304 } 305