1 /*
2  * Copyright (C) 2020 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 
17 package com.android.systemui.theme;
18 
19 import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
20 
21 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
22 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
23 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
24 
25 import static com.google.common.truth.Truth.assertThat;
26 
27 import static org.mockito.ArgumentMatchers.any;
28 import static org.mockito.ArgumentMatchers.anyInt;
29 import static org.mockito.ArgumentMatchers.eq;
30 import static org.mockito.Mockito.clearInvocations;
31 import static org.mockito.Mockito.mock;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.reset;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.verifyNoMoreInteractions;
37 import static org.mockito.Mockito.when;
38 
39 import android.app.ActivityManager;
40 import android.app.UiModeManager;
41 import android.app.WallpaperColors;
42 import android.app.WallpaperManager;
43 import android.content.BroadcastReceiver;
44 import android.content.Intent;
45 import android.content.om.FabricatedOverlay;
46 import android.content.om.OverlayIdentifier;
47 import android.content.res.Resources;
48 import android.database.ContentObserver;
49 import android.graphics.Color;
50 import android.os.Handler;
51 import android.os.UserHandle;
52 import android.os.UserManager;
53 import android.provider.Settings;
54 import android.testing.AndroidTestingRunner;
55 
56 import androidx.annotation.VisibleForTesting;
57 import androidx.test.filters.SmallTest;
58 
59 import com.android.systemui.SysuiTestCase;
60 import com.android.systemui.broadcast.BroadcastDispatcher;
61 import com.android.systemui.dump.DumpManager;
62 import com.android.systemui.flags.FeatureFlags;
63 import com.android.systemui.flags.Flags;
64 import com.android.systemui.keyguard.WakefulnessLifecycle;
65 import com.android.systemui.monet.Style;
66 import com.android.systemui.settings.UserTracker;
67 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
68 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
69 import com.android.systemui.util.settings.SecureSettings;
70 
71 import com.google.common.util.concurrent.MoreExecutors;
72 
73 import org.junit.Before;
74 import org.junit.Test;
75 import org.junit.runner.RunWith;
76 import org.mockito.ArgumentCaptor;
77 import org.mockito.Captor;
78 import org.mockito.Mock;
79 import org.mockito.MockitoAnnotations;
80 
81 import java.util.Arrays;
82 import java.util.List;
83 import java.util.Map;
84 import java.util.concurrent.Executor;
85 
86 @SmallTest
87 @RunWith(AndroidTestingRunner.class)
88 public class ThemeOverlayControllerTest extends SysuiTestCase {
89 
90     private static final int USER_SYSTEM = UserHandle.USER_SYSTEM;
91     private static final int USER_SECONDARY = 10;
92 
93     private ThemeOverlayController mThemeOverlayController;
94     @Mock
95     private Executor mBgExecutor;
96     @Mock
97     private Executor mMainExecutor;
98     @Mock
99     private BroadcastDispatcher mBroadcastDispatcher;
100     @Mock
101     private Handler mBgHandler;
102     @Mock
103     private ThemeOverlayApplier mThemeOverlayApplier;
104     @Mock
105     private SecureSettings mSecureSettings;
106     @Mock
107     private WallpaperManager mWallpaperManager;
108     @Mock
109     private UserManager mUserManager;
110     @Mock
111     private UserTracker mUserTracker;
112     @Mock
113     private DumpManager mDumpManager;
114     @Mock
115     private DeviceProvisionedController mDeviceProvisionedController;
116     @Mock
117     private FeatureFlags mFeatureFlags;
118     @Mock
119     private Resources mResources;
120     @Mock
121     private WakefulnessLifecycle mWakefulnessLifecycle;
122     @Mock
123     private UiModeManager mUiModeManager;
124     @Mock
125     private ActivityManager mActivityManager;
126     @Captor
127     private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiver;
128     @Captor
129     private ArgumentCaptor<WallpaperManager.OnColorsChangedListener> mColorsListener;
130     @Captor
131     private ArgumentCaptor<DeviceProvisionedListener> mDeviceProvisionedListener;
132     @Captor
133     private ArgumentCaptor<WakefulnessLifecycle.Observer> mWakefulnessLifecycleObserver;
134     @Captor
135     private ArgumentCaptor<UserTracker.Callback> mUserTrackerCallback;
136     @Captor
137     private ArgumentCaptor<ContentObserver> mSettingsObserver;
138 
139     @Before
setup()140     public void setup() {
141         MockitoAnnotations.initMocks(this);
142         when(mFeatureFlags.isEnabled(Flags.MONET)).thenReturn(true);
143         when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
144         when(mUiModeManager.getContrast()).thenReturn(0.5f);
145         when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
146         when(mResources.getColor(eq(android.R.color.system_accent1_500), any()))
147                 .thenReturn(Color.RED);
148         when(mResources.getColor(eq(android.R.color.system_accent2_500), any()))
149                 .thenReturn(Color.GREEN);
150         when(mResources.getColor(eq(android.R.color.system_accent3_500), any()))
151                 .thenReturn(Color.BLUE);
152         when(mResources.getColor(eq(android.R.color.system_neutral1_500), any()))
153                 .thenReturn(Color.YELLOW);
154         when(mResources.getColor(eq(android.R.color.system_neutral2_500), any()))
155                 .thenReturn(Color.BLACK);
156         mThemeOverlayController = new ThemeOverlayController(mContext,
157                 mBroadcastDispatcher, mBgHandler, mMainExecutor, mBgExecutor, mThemeOverlayApplier,
158                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
159                 mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
160                 mUiModeManager, mActivityManager) {
161             @VisibleForTesting
162             protected boolean isNightMode() {
163                 return false;
164             }
165 
166             @VisibleForTesting
167             protected FabricatedOverlay newFabricatedOverlay(String name) {
168                 FabricatedOverlay overlay = mock(FabricatedOverlay.class);
169                 when(overlay.getIdentifier())
170                         .thenReturn(new OverlayIdentifier(
171                                 Integer.toHexString(mColorScheme.getSeed() | 0xff000000)));
172                 return overlay;
173             }
174         };
175 
176         mWakefulnessLifecycle.dispatchFinishedWakingUp();
177         mThemeOverlayController.start();
178         verify(mUserTracker).addCallback(mUserTrackerCallback.capture(), eq(mMainExecutor));
179         verify(mWallpaperManager).addOnColorsChangedListener(mColorsListener.capture(), eq(null),
180                 eq(UserHandle.USER_ALL));
181         verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiver.capture(), any(),
182                 eq(mMainExecutor), any());
183         verify(mWakefulnessLifecycle).addObserver(mWakefulnessLifecycleObserver.capture());
184         verify(mDumpManager).registerDumpable(any(), any());
185         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
186         verify(mSecureSettings).registerContentObserverForUser(
187                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES),
188                 eq(false), mSettingsObserver.capture(), eq(UserHandle.USER_ALL)
189         );
190     }
191 
192     @Test
start_checksWallpaper()193     public void start_checksWallpaper() {
194         ArgumentCaptor<Runnable> registrationRunnable = ArgumentCaptor.forClass(Runnable.class);
195         verify(mBgExecutor).execute(registrationRunnable.capture());
196 
197         registrationRunnable.getValue().run();
198         verify(mWallpaperManager).getWallpaperColors(eq(WallpaperManager.FLAG_SYSTEM));
199     }
200 
201     @Test
onWallpaperColorsChanged_setsTheme_whenForeground()202     public void onWallpaperColorsChanged_setsTheme_whenForeground() {
203         // Should ask for a new theme when wallpaper colors change
204         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
205                 Color.valueOf(Color.BLUE), null);
206         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
207                 USER_SYSTEM);
208         ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
209                 ArgumentCaptor.forClass(Map.class);
210 
211         verify(mThemeOverlayApplier)
212                 .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
213 
214         // Assert that we received the colors that we were expecting
215         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
216                 .isEqualTo(new OverlayIdentifier("ffff0000"));
217         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
218                 .isEqualTo(new OverlayIdentifier("ffff0000"));
219 
220         // Should not ask again if changed to same value
221         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
222                 USER_SYSTEM);
223         verifyNoMoreInteractions(mThemeOverlayApplier);
224 
225         // Should not ask again even for new colors until we change wallpapers
226         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
227                 null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
228         verifyNoMoreInteractions(mThemeOverlayApplier);
229 
230         // But should change theme after changing wallpapers
231         clearInvocations(mThemeOverlayApplier);
232         Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
233         intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true);
234         mBroadcastReceiver.getValue().onReceive(null, intent);
235         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
236                 null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
237         verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
238     }
239 
240     @Test
onWallpaperColorsChanged_setsTheme_skipWhenBackground()241     public void onWallpaperColorsChanged_setsTheme_skipWhenBackground() {
242         // Should ask for a new theme when wallpaper colors change
243         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
244                 Color.valueOf(Color.BLUE), null);
245         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
246                 USER_SYSTEM);
247         ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
248                 ArgumentCaptor.forClass(Map.class);
249 
250         verify(mThemeOverlayApplier)
251                 .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
252 
253         // Should not change theme after changing wallpapers, if intent doesn't have
254         // WallpaperManager.EXTRA_FROM_FOREGROUND_APP set to true.
255         clearInvocations(mThemeOverlayApplier);
256         mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
257         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
258                 null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
259         verify(mThemeOverlayApplier, never())
260                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
261     }
262 
263     @Test
onWallpaperColorsChanged_preservesWallpaperPickerTheme()264     public void onWallpaperColorsChanged_preservesWallpaperPickerTheme() {
265         // Should ask for a new theme when wallpaper colors change
266         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
267                 Color.valueOf(Color.BLUE), null);
268 
269         String jsonString =
270                 "{\"android.theme.customization.system_palette\":\"override.package.name\","
271                         + "\"android.theme.customization.color_source\":\"preset\"}";
272         when(mSecureSettings.getStringForUser(
273                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
274                 .thenReturn(jsonString);
275 
276         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
277                 USER_SYSTEM);
278         ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
279                 ArgumentCaptor.forClass(Map.class);
280 
281         verify(mThemeOverlayApplier)
282                 .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
283 
284         // Assert that we received the colors that we were expecting
285         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
286                 .isEqualTo(new OverlayIdentifier("override.package.name"));
287     }
288 
289     @Test
onWallpaperColorsChanged_resetThemeIfNotPreset()290     public void onWallpaperColorsChanged_resetThemeIfNotPreset() {
291         // Should ask for a new theme when wallpaper colors change
292         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
293                 Color.valueOf(Color.BLUE), null);
294 
295         String jsonString =
296                 "{\"android.theme.customization.color_source\":\"home_wallpaper\","
297                         + "\"android.theme.customization.system_palette\":\"A16B00\","
298                         + "\"android.theme.customization.accent_color\":\"A16B00\","
299                         + "\"android.theme.customization.color_index\":\"2\"}";
300 
301         when(mSecureSettings.getStringForUser(
302                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
303                 .thenReturn(jsonString);
304 
305         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
306                 USER_SYSTEM);
307 
308         ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
309         verify(mSecureSettings).putStringForUser(
310                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture(),
311                 anyInt());
312 
313         assertThat(updatedSetting.getValue().contains("android.theme.customization.accent_color"))
314                 .isFalse();
315         assertThat(updatedSetting.getValue().contains("android.theme.customization.system_palette"))
316                 .isFalse();
317         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
318                 .isFalse();
319 
320         verify(mThemeOverlayApplier)
321                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
322     }
323 
324     @Test
onWallpaperColorsChanged_resetThemeWithNewHomeWallpapers()325     public void onWallpaperColorsChanged_resetThemeWithNewHomeWallpapers() {
326         // Should ask for a new theme when wallpaper colors change
327         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
328                 Color.valueOf(Color.BLUE), null);
329 
330         String jsonString =
331                 "{\"android.theme.customization.color_source\":\"home_wallpaper\","
332                         + "\"android.theme.customization.system_palette\":\"A16B00\","
333                         + "\"android.theme.customization.accent_color\":\"A16B00\","
334                         + "\"android.theme.customization.color_index\":\"2\"}";
335 
336         when(mSecureSettings.getStringForUser(
337                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
338                 .thenReturn(jsonString);
339         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
340                 .thenReturn(20);
341         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
342                 .thenReturn(21);
343 
344         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
345                 USER_SYSTEM);
346 
347         ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
348         verify(mSecureSettings).putStringForUser(
349                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture(),
350                 anyInt());
351 
352         assertThat(updatedSetting.getValue().contains(
353                 "android.theme.customization.color_both\":\"0")).isTrue();
354 
355         verify(mThemeOverlayApplier)
356                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
357     }
358 
359     @Test
onWallpaperColorsChanged_keepsThemeWhenSetFromLockScreen()360     public void onWallpaperColorsChanged_keepsThemeWhenSetFromLockScreen() {
361         // Should ask for a new theme when wallpaper colors change
362         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
363                 Color.valueOf(Color.BLUE), null);
364         String jsonString =
365                 "{\"android.theme.customization.color_source\":\"lock_wallpaper\","
366                         + "\"android.theme.customization.system_palette\":\"A16B00\","
367                         + "\"android.theme.customization.accent_color\":\"A16B00\","
368                         + "\"android.theme.customization.color_index\":\"2\"}";
369         when(mSecureSettings.getStringForUser(
370                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
371                 .thenReturn(jsonString);
372         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
373                 .thenReturn(20);
374         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
375                 .thenReturn(21);
376         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
377                 USER_SYSTEM);
378         verify(mSecureSettings, never()).putStringForUser(
379                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), any(), anyInt());
380     }
381 
382     @Test
onWallpaperColorsChanged_resetLockScreenThemeWhenBothSet()383     public void onWallpaperColorsChanged_resetLockScreenThemeWhenBothSet() {
384         // Should ask for a new theme when wallpaper colors change
385         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
386                 Color.valueOf(Color.BLUE), null);
387         String jsonString =
388                 "{\"android.theme.customization.color_source\":\"lock_wallpaper\","
389                         + "\"android.theme.customization.system_palette\":\"A16B00\","
390                         + "\"android.theme.customization.accent_color\":\"A16B00\","
391                         + "\"android.theme.customization.color_index\":\"2\"}";
392         when(mSecureSettings.getStringForUser(
393                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
394                 .thenReturn(jsonString);
395         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
396                 .thenReturn(20);
397         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
398                 .thenReturn(21);
399 
400         mColorsListener.getValue().onColorsChanged(mainColors,
401                 WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK,
402                 USER_SYSTEM);
403 
404         ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
405         verify(mSecureSettings).putStringForUser(
406                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture(),
407                 anyInt());
408         assertThat(updatedSetting.getValue().contains(
409                 "android.theme.customization.color_both\":\"1")).isTrue();
410         verify(mThemeOverlayApplier)
411                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
412     }
413 
414     @Test
onSettingChanged_honorThemeStyle()415     public void onSettingChanged_honorThemeStyle() {
416         when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true);
417         List<Style> validStyles = Arrays.asList(Style.EXPRESSIVE, Style.SPRITZ, Style.TONAL_SPOT,
418                 Style.FRUIT_SALAD, Style.RAINBOW, Style.VIBRANT);
419         for (Style style : validStyles) {
420             reset(mSecureSettings);
421 
422             String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\","
423                     + "\"android.theme.customization.theme_style\":\"" + style.name() + "\"}";
424 
425             when(mSecureSettings.getStringForUser(
426                     eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
427                     .thenReturn(jsonString);
428 
429             mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId());
430 
431             assertThat(mThemeOverlayController.mThemeStyle).isEqualTo(style);
432         }
433     }
434 
435     @Test
onSettingChanged_invalidStyle()436     public void onSettingChanged_invalidStyle() {
437         when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true);
438         String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\","
439                 + "\"android.theme.customization.theme_style\":\"some_invalid_name\"}";
440 
441         when(mSecureSettings.getStringForUser(
442                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
443                 .thenReturn(jsonString);
444 
445         mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId());
446 
447         assertThat(mThemeOverlayController.mThemeStyle).isEqualTo(Style.TONAL_SPOT);
448     }
449 
450     @Test
onWallpaperColorsChanged_resetThemeWithNewHomeAndLockWallpaper()451     public void onWallpaperColorsChanged_resetThemeWithNewHomeAndLockWallpaper() {
452         // Should ask for a new theme when wallpaper colors change
453         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
454                 Color.valueOf(Color.BLUE), null);
455 
456         String jsonString =
457                 "{\"android.theme.customization.color_source\":\"home_wallpaper\","
458                         + "\"android.theme.customization.system_palette\":\"A16B00\","
459                         + "\"android.theme.customization.accent_color\":\"A16B00\","
460                         + "\"android.theme.customization.color_index\":\"2\"}";
461 
462         when(mSecureSettings.getStringForUser(
463                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
464                 .thenReturn(jsonString);
465         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
466                 .thenReturn(-1);
467 
468         mColorsListener.getValue().onColorsChanged(mainColors,
469                 WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK, USER_SYSTEM);
470 
471         ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
472         verify(mSecureSettings).putStringForUser(
473                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture(),
474                 anyInt());
475 
476         assertThat(updatedSetting.getValue().contains(
477                 "android.theme.customization.color_both\":\"1")).isTrue();
478 
479         verify(mThemeOverlayApplier)
480                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
481     }
482 
483     @Test
onWallpaperColorsChanged_changeLockWallpaper()484     public void onWallpaperColorsChanged_changeLockWallpaper() {
485         // Should ask for a new theme when wallpaper colors change
486         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
487                 Color.valueOf(Color.BLUE), null);
488         String jsonString =
489                 "{\"android.theme.customization.color_source\":\"home_wallpaper\","
490                         + "\"android.theme.customization.system_palette\":\"A16B00\","
491                         + "\"android.theme.customization.accent_color\":\"A16B00\","
492                         + "\"android.theme.customization.color_index\":\"2\"}";
493         when(mSecureSettings.getStringForUser(
494                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
495                 .thenReturn(jsonString);
496         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
497                 .thenReturn(1);
498 
499         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_LOCK,
500                 USER_SYSTEM);
501 
502         ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
503         verify(mSecureSettings).putStringForUser(
504                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture(),
505                 anyInt());
506         assertThat(updatedSetting.getValue().contains(
507                 "android.theme.customization.color_source\":\"lock_wallpaper")).isTrue();
508         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
509                 .isFalse();
510         verify(mThemeOverlayApplier)
511                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
512     }
513 
514     @Test
onWallpaperColorsChanged_changeHomeWallpaper()515     public void onWallpaperColorsChanged_changeHomeWallpaper() {
516         // Should ask for a new theme when wallpaper colors change
517         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
518                 Color.valueOf(Color.BLUE), null);
519         String jsonString =
520                 "{\"android.theme.customization.color_source\":\"home_wallpaper\","
521                         + "\"android.theme.customization.system_palette\":\"A16B00\","
522                         + "\"android.theme.customization.accent_color\":\"A16B00\","
523                         + "\"android.theme.customization.color_index\":\"2\"}";
524         when(mSecureSettings.getStringForUser(
525                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
526                 .thenReturn(jsonString);
527         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
528                 .thenReturn(-1);
529 
530         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
531                 USER_SYSTEM);
532 
533         ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
534         verify(mSecureSettings).putStringForUser(
535                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture(),
536                 anyInt());
537         assertThat(updatedSetting.getValue().contains(
538                 "android.theme.customization.color_source\":\"home_wallpaper")).isTrue();
539         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
540                 .isFalse();
541         verify(mThemeOverlayApplier)
542                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
543     }
544 
545     @Test
onWallpaperColorsChanged_resetThemeWhenFromLatestWallpaper()546     public void onWallpaperColorsChanged_resetThemeWhenFromLatestWallpaper() {
547         // Should ask for a new theme when the colors of the last applied wallpaper change
548         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
549                 Color.valueOf(Color.BLUE), null);
550 
551         String jsonString =
552                 "{\"android.theme.customization.color_source\":\"home_wallpaper\","
553                         + "\"android.theme.customization.system_palette\":\"A16B00\","
554                         + "\"android.theme.customization.accent_color\":\"A16B00\","
555                         + "\"android.theme.customization.color_index\":\"2\"}";
556 
557         when(mSecureSettings.getStringForUser(
558                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
559                 .thenReturn(jsonString);
560         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
561                 .thenReturn(1);
562         // SYSTEM wallpaper is the last applied one
563         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
564                 .thenReturn(2);
565 
566         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
567                 USER_SYSTEM);
568 
569         ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
570         verify(mSecureSettings).putStringForUser(
571                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture(),
572                 anyInt());
573 
574         verify(mThemeOverlayApplier)
575                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
576     }
577 
578     @Test
onWallpaperColorsChanged_keepThemeWhenFromLatestWallpaperAndSpecifiedColor()579     public void onWallpaperColorsChanged_keepThemeWhenFromLatestWallpaperAndSpecifiedColor() {
580         // Shouldn't ask for a new theme when the colors of the last applied wallpaper change
581         // with the same specified system palette one.
582         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
583                 Color.valueOf(0xffa16b00), null);
584 
585         String jsonString =
586                 "{\"android.theme.customization.color_source\":\"home_wallpaper\","
587                         + "\"android.theme.customization.system_palette\":\"A16B00\","
588                         + "\"android.theme.customization.accent_color\":\"A16B00\","
589                         + "\"android.theme.customization.color_index\":\"2\"}";
590 
591         when(mSecureSettings.getStringForUser(
592                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
593                 .thenReturn(jsonString);
594         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
595                 .thenReturn(1);
596         // SYSTEM wallpaper is the last applied one
597         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
598                 .thenReturn(2);
599 
600         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
601                 USER_SYSTEM);
602 
603         ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
604         verify(mSecureSettings, never()).putString(
605                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
606 
607         // Apply overlay by existing theme from secure setting
608         verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
609     }
610 
611     @Test
onWallpaperColorsChanged_keepThemeIfNotLatestWallpaper()612     public void onWallpaperColorsChanged_keepThemeIfNotLatestWallpaper() {
613         // Shouldn't ask for a new theme when the colors of the wallpaper that is not the last
614         // applied one change
615         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
616                 Color.valueOf(Color.BLUE), null);
617 
618         String jsonString =
619                 "{\"android.theme.customization.color_source\":\"home_wallpaper\","
620                         + "\"android.theme.customization.system_palette\":\"A16B00\","
621                         + "\"android.theme.customization.accent_color\":\"A16B00\","
622                         + "\"android.theme.customization.color_index\":\"2\"}";
623 
624         when(mSecureSettings.getStringForUser(
625                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
626                 .thenReturn(jsonString);
627         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
628                 .thenReturn(1);
629         // SYSTEM wallpaper is the last applied one
630         when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
631                 .thenReturn(2);
632 
633         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_LOCK,
634                 USER_SYSTEM);
635 
636         verify(mSecureSettings, never()).putString(
637                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), any());
638 
639 
640         verify(mThemeOverlayApplier, never())
641                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
642     }
643 
644     @Test
onUserSwitching_setsTheme()645     public void onUserSwitching_setsTheme() {
646         // Setup users with different colors
647         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), null, null);
648         WallpaperColors secondaryColors =
649                 new WallpaperColors(Color.valueOf(Color.BLUE), null, null);
650         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
651                 USER_SYSTEM);
652         mColorsListener.getValue().onColorsChanged(secondaryColors, WallpaperManager.FLAG_SYSTEM,
653                 USER_SECONDARY);
654 
655         // When changing users
656         clearInvocations(mThemeOverlayApplier);
657         when(mUserTracker.getUserId()).thenReturn(USER_SECONDARY);
658         mUserTrackerCallback.getValue().onUserChanged(USER_SECONDARY, mContext);
659 
660         ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
661                 ArgumentCaptor.forClass(Map.class);
662         verify(mThemeOverlayApplier)
663                 .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
664 
665         // Assert that we received secondary user colors
666         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
667                 .isEqualTo(new OverlayIdentifier("ff0000ff"));
668         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR))
669                 .isEqualTo(new OverlayIdentifier("ff0000ff"));
670     }
671 
672     @Test
onProfileAdded_setsTheme()673     public void onProfileAdded_setsTheme() {
674         mBroadcastReceiver.getValue().onReceive(null,
675                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
676         verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
677     }
678 
679     @Test
onUserAdded_appliesTheme_ifNotManagedProfile()680     public void onUserAdded_appliesTheme_ifNotManagedProfile() {
681         reset(mDeviceProvisionedController);
682         when(mUserManager.isManagedProfile(anyInt())).thenReturn(false);
683         mBroadcastReceiver.getValue().onReceive(null,
684                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
685         verify(mThemeOverlayApplier)
686                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
687     }
688 
689     @Test
onProfileAdded_ignoresUntilSetupComplete()690     public void onProfileAdded_ignoresUntilSetupComplete() {
691         reset(mDeviceProvisionedController);
692         when(mUserManager.isManagedProfile(anyInt())).thenReturn(true);
693         mBroadcastReceiver.getValue().onReceive(null,
694                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
695         verify(mThemeOverlayApplier, never())
696                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
697     }
698 
699     @Test
onWallpaperColorsChanged_firstEventBeforeUserSetup_shouldBeAccepted()700     public void onWallpaperColorsChanged_firstEventBeforeUserSetup_shouldBeAccepted() {
701         // By default, on setup() we make this controller return that the user finished setup
702         // wizard. This test on the other hand, is testing the setup flow.
703         reset(mDeviceProvisionedController);
704         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
705                 Color.valueOf(Color.BLUE), null);
706         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
707                 USER_SYSTEM);
708 
709         verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
710 
711         // Regression test: null events should not reset the internal state and allow colors to be
712         // applied again.
713         clearInvocations(mThemeOverlayApplier);
714         Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
715         intent.putExtra(WallpaperManager.EXTRA_FROM_FOREGROUND_APP, true);
716         mBroadcastReceiver.getValue().onReceive(null, intent);
717         mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
718         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
719                 any(), any());
720         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN),
721                 null, null), WallpaperManager.FLAG_SYSTEM, USER_SYSTEM);
722         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
723                 any(), any());
724     }
725 
726     @Test
catchException_whenPackageNameIsOverlayName()727     public void catchException_whenPackageNameIsOverlayName() {
728         mDeviceProvisionedController = mock(DeviceProvisionedController.class);
729         mThemeOverlayApplier = mock(ThemeOverlayApplier.class);
730         mWallpaperManager = mock(WallpaperManager.class);
731 
732         // Assume we have some wallpaper colors at boot.
733         when(mWallpaperManager.getWallpaperColors(anyInt()))
734                 .thenReturn(new WallpaperColors(Color.valueOf(Color.GRAY), null, null));
735 
736         Executor executor = MoreExecutors.directExecutor();
737 
738         mThemeOverlayController = new ThemeOverlayController(mContext,
739                 mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier,
740                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
741                 mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
742                 mUiModeManager, mActivityManager) {
743             @VisibleForTesting
744             protected boolean isNightMode() {
745                 return false;
746             }
747 
748             @VisibleForTesting
749             protected FabricatedOverlay newFabricatedOverlay(String name) {
750                 FabricatedOverlay overlay = mock(FabricatedOverlay.class);
751                 when(overlay.getIdentifier())
752                         .thenReturn(new OverlayIdentifier("com.thebest.livewallpaperapp.ever"));
753                 return overlay;
754             }
755         };
756         mThemeOverlayController.start();
757 
758         verify(mWallpaperManager).addOnColorsChangedListener(mColorsListener.capture(), eq(null),
759                 eq(UserHandle.USER_ALL));
760         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
761 
762         // Colors were applied during controller initialization.
763         verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
764         clearInvocations(mThemeOverlayApplier);
765     }
766 
767     @Test
onWallpaperColorsChanged_defersUntilSetupIsCompleted_ifHasColors()768     public void onWallpaperColorsChanged_defersUntilSetupIsCompleted_ifHasColors() {
769         mDeviceProvisionedController = mock(DeviceProvisionedController.class);
770         mThemeOverlayApplier = mock(ThemeOverlayApplier.class);
771         mWallpaperManager = mock(WallpaperManager.class);
772 
773         // Assume we have some wallpaper colors at boot.
774         when(mWallpaperManager.getWallpaperColors(anyInt()))
775                 .thenReturn(new WallpaperColors(Color.valueOf(Color.GRAY), null, null));
776 
777         Executor executor = MoreExecutors.directExecutor();
778         mThemeOverlayController = new ThemeOverlayController(mContext,
779                 mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier,
780                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
781                 mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
782                 mUiModeManager, mActivityManager) {
783             @VisibleForTesting
784             protected boolean isNightMode() {
785                 return false;
786             }
787 
788             @VisibleForTesting
789             protected FabricatedOverlay newFabricatedOverlay(String name) {
790                 FabricatedOverlay overlay = mock(FabricatedOverlay.class);
791                 when(overlay.getIdentifier())
792                         .thenReturn(new OverlayIdentifier(
793                                 Integer.toHexString(mColorScheme.getSeed() | 0xff000000)));
794                 return overlay;
795             }
796         };
797         mThemeOverlayController.start();
798         verify(mWallpaperManager).addOnColorsChangedListener(mColorsListener.capture(), eq(null),
799                 eq(UserHandle.USER_ALL));
800         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
801 
802         // Colors were applied during controller initialization.
803         verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
804         clearInvocations(mThemeOverlayApplier);
805 
806         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
807                 Color.valueOf(Color.BLUE), null);
808         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
809                 USER_SYSTEM);
810 
811         reset(mResources);
812         when(mResources.getColor(eq(android.R.color.system_accent1_500), any()))
813                 .thenReturn(mThemeOverlayController.mColorScheme.getAccent1().getS500());
814         when(mResources.getColor(eq(android.R.color.system_accent2_500), any()))
815                 .thenReturn(mThemeOverlayController.mColorScheme.getAccent2().getS500());
816         when(mResources.getColor(eq(android.R.color.system_accent3_500), any()))
817                 .thenReturn(mThemeOverlayController.mColorScheme.getAccent3().getS500());
818         when(mResources.getColor(eq(android.R.color.system_neutral1_500), any()))
819                 .thenReturn(mThemeOverlayController.mColorScheme.getNeutral1().getS500());
820         when(mResources.getColor(eq(android.R.color.system_neutral2_500), any()))
821                 .thenReturn(mThemeOverlayController.mColorScheme.getNeutral2().getS500());
822 
823         // Defers event because we already have initial colors.
824         verify(mThemeOverlayApplier, never())
825                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
826 
827         // Then event happens after setup phase is over.
828         when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
829         mDeviceProvisionedListener.getValue().onUserSetupChanged();
830         verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
831     }
832 
833     @Test
onWallpaperColorsChanged_screenOff_deviceSetupNotFinished_doesNotProcessQueued()834     public void onWallpaperColorsChanged_screenOff_deviceSetupNotFinished_doesNotProcessQueued() {
835         when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
836         mDeviceProvisionedListener.getValue().onUserSetupChanged();
837 
838 
839         // Second color application is not applied.
840         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
841                 Color.valueOf(Color.BLUE), null);
842         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
843                 USER_SYSTEM);
844 
845         clearInvocations(mThemeOverlayApplier);
846 
847         // Device went to sleep and second set of colors was applied.
848         mainColors =  new WallpaperColors(Color.valueOf(Color.BLUE),
849                 Color.valueOf(Color.RED), null);
850         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
851                 USER_SYSTEM);
852         verify(mThemeOverlayApplier, never())
853                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
854 
855         mWakefulnessLifecycle.dispatchFinishedGoingToSleep();
856         verify(mThemeOverlayApplier, never())
857                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
858     }
859 
860     @Test
onWallpaperColorsChanged_screenOff_processesQueued()861     public void onWallpaperColorsChanged_screenOff_processesQueued() {
862         when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
863         mDeviceProvisionedListener.getValue().onUserSetupChanged();
864 
865         // Second color application is not applied.
866         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
867                 Color.valueOf(Color.BLUE), null);
868         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
869                 USER_SYSTEM);
870 
871         clearInvocations(mThemeOverlayApplier);
872 
873         // Device went to sleep and second set of colors was applied.
874         mainColors =  new WallpaperColors(Color.valueOf(Color.BLUE),
875                 Color.valueOf(Color.RED), null);
876         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
877                 USER_SYSTEM);
878         verify(mThemeOverlayApplier, never())
879                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
880 
881         mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep();
882         verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
883     }
884 
885     @Test
onWallpaperColorsChanged_parsesColorsFromWallpaperPicker()886     public void onWallpaperColorsChanged_parsesColorsFromWallpaperPicker() {
887         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
888                 Color.valueOf(Color.BLUE), null);
889 
890         String jsonString =
891                 "{\"android.theme.customization.system_palette\":\"00FF00\"}";
892         when(mSecureSettings.getStringForUser(
893                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
894                 .thenReturn(jsonString);
895 
896         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
897                 USER_SYSTEM);
898         ArgumentCaptor<Map<String, OverlayIdentifier>> themeOverlays =
899                 ArgumentCaptor.forClass(Map.class);
900 
901         verify(mThemeOverlayApplier)
902                 .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
903 
904         // Assert that we received the colors that we were expecting
905         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
906                 .isEqualTo(new OverlayIdentifier("ff00ff00"));
907     }
908 
909     // Regression test for b/234603929, where a reboot would generate a wallpaper colors changed
910     // event for the already-set colors that would then set the theme incorrectly on screen sleep.
911     @Test
onWallpaperColorsSetToSame_keepsTheme()912     public void onWallpaperColorsSetToSame_keepsTheme() {
913         // Set initial colors and verify.
914         WallpaperColors startingColors = new WallpaperColors(Color.valueOf(Color.RED),
915                 Color.valueOf(Color.BLUE), null);
916         WallpaperColors sameColors = new WallpaperColors(Color.valueOf(Color.RED),
917                 Color.valueOf(Color.BLUE), null);
918         mColorsListener.getValue().onColorsChanged(startingColors, WallpaperManager.FLAG_SYSTEM,
919                 USER_SYSTEM);
920         verify(mThemeOverlayApplier)
921                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
922         clearInvocations(mThemeOverlayApplier);
923 
924         // Set to the same colors.
925         mColorsListener.getValue().onColorsChanged(sameColors, WallpaperManager.FLAG_SYSTEM,
926                 USER_SYSTEM);
927         verify(mThemeOverlayApplier, never())
928                 .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
929 
930         // Verify that no change resulted.
931         mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep();
932         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
933                 any(), any());
934     }
935 
936     @Test
createDynamicOverlay_addsAllDynamicColors()937     public void createDynamicOverlay_addsAllDynamicColors() {
938         // Trigger new wallpaper colors to generate an overlay
939         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
940                 Color.valueOf(Color.BLUE), null);
941         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
942                 USER_SYSTEM);
943         ArgumentCaptor<FabricatedOverlay[]> themeOverlays =
944                 ArgumentCaptor.forClass(FabricatedOverlay[].class);
945 
946         verify(mThemeOverlayApplier)
947                 .applyCurrentUserOverlays(any(), themeOverlays.capture(), anyInt(), any(), any());
948 
949         FabricatedOverlay[] overlays = themeOverlays.getValue();
950         FabricatedOverlay accents = overlays[0];
951         FabricatedOverlay neutrals = overlays[1];
952         FabricatedOverlay dynamic = overlays[2];
953 
954         final int colorsPerPalette = 12;
955 
956         // Color resources were added for all 3 accent palettes
957         verify(accents, times(colorsPerPalette * 3))
958                 .setResourceValue(any(String.class), eq(TYPE_INT_COLOR_ARGB8), anyInt(), eq(null));
959         // Color resources were added for all 2 neutral palettes
960         verify(neutrals, times(colorsPerPalette * 2))
961                 .setResourceValue(any(String.class), eq(TYPE_INT_COLOR_ARGB8), anyInt(), eq(null));
962         // All dynamic colors were added twice: light and dark them
963         // All fixed colors were added once
964         verify(dynamic, times(
965                 DynamicColors.allDynamicColorsMapped(false).size() * 2
966                         + DynamicColors.getFixedColorsMapped(false).size())
967         ).setResourceValue(any(String.class), eq(TYPE_INT_COLOR_ARGB8), anyInt(), eq(null));
968     }
969 }
970