From a11d68dca2f30df607f4b3261d78a72f437f3276 Mon Sep 17 00:00:00 2001 From: James O'Leary Date: Mon, 17 May 2021 15:16:35 -0400 Subject: Allow wallpaper color updates if screen off Originally, we planned to only allow one color update per boot cycle, preventing jank from wallpapers that may too eagerly send update events at runtime. To better accommodate live wallpapers, while continuing to avoid jank at runtime, we will allow color updates once the screen is off. Also, any color updates received when the screen is on are cached, and when the screen is turned off, they will be processed. Test: added tests to ThemeOverlayControllerTest, one confirms colors are not processed when the screen is on, and once the screen is off, they are processed. Another test confirms this behavior does not conflict with the expected behavior during setup wizard: _no_ color updates are processed until the device is fully setup, avoiding janky Activity restarts. Bug: 183792317 Change-Id: Ie48558d7724572271d7d8262835b5eba48174fd3 --- .../systemui/theme/ThemeOverlayController.java | 46 +++++++++++++--- .../systemui/theme/ThemeOverlayControllerTest.java | 62 ++++++++++++++++++++-- 2 files changed, 99 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 195114f0f19f..bb9ec6233f62 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -15,6 +15,7 @@ */ package com.android.systemui.theme; +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE; @@ -52,6 +53,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -113,6 +115,12 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { private FabricatedOverlay mNeutralOverlay; // If wallpaper color event will be accepted and change the UI colors. private boolean mAcceptColorEvents = true; + // If non-null, colors that were sent to the framework, and processing was deferred until + // the next time the screen is off. + private WallpaperColors mDeferredWallpaperColors; + private int mDeferredWallpaperColorsFlags; + private WakefulnessLifecycle mWakefulnessLifecycle; + // Defers changing themes until Setup Wizard is done. private boolean mDeferredThemeEvaluation; // Determines if we should ignore THEME_CUSTOMIZATION_OVERLAY_PACKAGES setting changes. @@ -135,18 +143,28 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { }; private final OnColorsChangedListener mOnColorsChangedListener = (wallpaperColors, which) -> { - if (!mAcceptColorEvents) { - Log.i(TAG, "Wallpaper color event rejected: " + wallpaperColors); + if (!mAcceptColorEvents && mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP) { + mDeferredWallpaperColors = wallpaperColors; + mDeferredWallpaperColorsFlags = which; + Log.i(TAG, "colors received; processing deferred until screen off: " + wallpaperColors); return; } + if (wallpaperColors != null) { mAcceptColorEvents = false; + // Any cache of colors deferred for process is now stale. + mDeferredWallpaperColors = null; + mDeferredWallpaperColorsFlags = 0; } + handleWallpaperColors(wallpaperColors, which); + }; + + private void handleWallpaperColors(WallpaperColors wallpaperColors, int flags) { final boolean hadWallpaperColors = mSystemColors != null; - if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { + if ((flags & WallpaperManager.FLAG_SYSTEM) != 0) { mSystemColors = wallpaperColors; - if (DEBUG) Log.d(TAG, "got new colors: " + wallpaperColors + " where: " + which); + if (DEBUG) Log.d(TAG, "got new colors: " + wallpaperColors + " where: " + flags); } if (mDeviceProvisionedController != null @@ -197,7 +215,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { } } reevaluateSystemTheme(false /* forceReload */); - }; + } private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -224,7 +242,8 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { @Background Executor bgExecutor, ThemeOverlayApplier themeOverlayApplier, SecureSettings secureSettings, WallpaperManager wallpaperManager, UserManager userManager, DeviceProvisionedController deviceProvisionedController, - UserTracker userTracker, DumpManager dumpManager, FeatureFlags featureFlags) { + UserTracker userTracker, DumpManager dumpManager, FeatureFlags featureFlags, + WakefulnessLifecycle wakefulnessLifecycle) { super(context); mIsMonetEnabled = featureFlags.isMonetEnabled(); @@ -238,6 +257,7 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { mSecureSettings = secureSettings; mWallpaperManager = wallpaperManager; mUserTracker = userTracker; + mWakefulnessLifecycle = wakefulnessLifecycle; dumpManager.registerDumpable(TAG, this); } @@ -302,6 +322,20 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { } mWallpaperManager.addOnColorsChangedListener(mOnColorsChangedListener, null, UserHandle.USER_ALL); + mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() { + @Override + public void onFinishedGoingToSleep() { + if (mDeferredWallpaperColors != null) { + WallpaperColors colors = mDeferredWallpaperColors; + int flags = mDeferredWallpaperColorsFlags; + + mDeferredWallpaperColors = null; + mDeferredWallpaperColorsFlags = 0; + + handleWallpaperColors(colors, flags); + } + } + }); } private void reevaluateSystemTheme(boolean forceReload) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index 9f1dad84b6ce..8350e178826b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.theme; +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR; import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE; @@ -51,6 +52,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -99,22 +101,26 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { private DeviceProvisionedController mDeviceProvisionedController; @Mock private FeatureFlags mFeatureFlags; + @Mock + private WakefulnessLifecycle mWakefulnessLifecycle; @Captor private ArgumentCaptor mBroadcastReceiver; @Captor private ArgumentCaptor mColorsListener; @Captor private ArgumentCaptor mDeviceProvisionedListener; - + @Captor + private ArgumentCaptor mWakefulnessLifecycleObserver; @Before public void setup() { MockitoAnnotations.initMocks(this); when(mFeatureFlags.isMonetEnabled()).thenReturn(true); + when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE); when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true); mThemeOverlayController = new ThemeOverlayController(null /* context */, mBroadcastDispatcher, mBgHandler, mMainExecutor, mBgExecutor, mThemeOverlayApplier, mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController, - mUserTracker, mDumpManager, mFeatureFlags) { + mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) { @Nullable @Override protected FabricatedOverlay getOverlay(int color, int type) { @@ -125,11 +131,13 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { } }; + mWakefulnessLifecycle.dispatchFinishedWakingUp(); mThemeOverlayController.start(); verify(mWallpaperManager).addOnColorsChangedListener(mColorsListener.capture(), eq(null), eq(UserHandle.USER_ALL)); verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiver.capture(), any(), eq(mMainExecutor), any()); + verify(mWakefulnessLifecycle).addObserver(mWakefulnessLifecycleObserver.capture()); verify(mDumpManager).registerDumpable(any(), any()); verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture()); } @@ -283,7 +291,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mThemeOverlayController = new ThemeOverlayController(null /* context */, mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier, mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController, - mUserTracker, mDumpManager, mFeatureFlags) { + mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) { @Nullable @Override protected FabricatedOverlay getOverlay(int color, int type) { @@ -316,6 +324,54 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); } + @Test + public void onWallpaperColorsChanged_screenOff_deviceSetupNotFinished_doesNotProcessQueued() { + when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false); + mDeviceProvisionedListener.getValue().onUserSetupChanged(); + + + // Second color application is not applied. + WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), + Color.valueOf(Color.BLUE), null); + mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM); + + clearInvocations(mThemeOverlayApplier); + + // Device went to sleep and second set of colors was applied. + mainColors = new WallpaperColors(Color.valueOf(Color.BLUE), + Color.valueOf(Color.RED), null); + mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM); + verify(mThemeOverlayApplier, never()) + .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + + mWakefulnessLifecycle.dispatchFinishedGoingToSleep(); + verify(mThemeOverlayApplier, never()) + .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + } + + @Test + public void onWallpaperColorsChanged_screenOff_processesQueued() { + when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true); + mDeviceProvisionedListener.getValue().onUserSetupChanged(); + + // Second color application is not applied. + WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), + Color.valueOf(Color.BLUE), null); + mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM); + + clearInvocations(mThemeOverlayApplier); + + // Device went to sleep and second set of colors was applied. + mainColors = new WallpaperColors(Color.valueOf(Color.BLUE), + Color.valueOf(Color.RED), null); + mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM); + verify(mThemeOverlayApplier, never()) + .applyCurrentUserOverlays(any(), any(), anyInt(), any()); + + mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep(); + verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any()); + } + @Test public void onWallpaperColorsChanged_parsesColorsFromWallpaperPicker() { WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), -- cgit v1.2.3-59-g8ed1b