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