From 7719202d382f2f8c00038084a80bbccd7f16aed1 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Thu, 6 Jan 2022 15:48:03 -0800 Subject: [PATCH] Hook up new styles to customization setting The new styles: - spritz - expressive - vibrant Are now correctly hooked up to the customization setting, allowing for picker integration. Bug: 195969565 Test: atest ThemeOverlayControllerTest Test: adb shell settings put secure theme_customization_overlay_packages '''\{\"android.theme.customization.system_palette\":\"B1611C\",\"android.theme.customization.theme_style\":\"SPRITZ\"}''' Change-Id: I106f05b7f0aade470b8216b47e14ca4aa6db0970 --- .../com/android/systemui/monet/ColorScheme.kt | 17 ++--- .../systemui/theme/ThemeOverlayApplier.java | 2 + .../theme/ThemeOverlayController.java | 66 ++++++++----------- .../theme/ThemeOverlayControllerTest.java | 51 ++++++++++++-- 4 files changed, 85 insertions(+), 51 deletions(-) diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt index 37df597a28b31..29221aa04699c 100644 --- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt +++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt @@ -31,15 +31,15 @@ const val ACCENT1_CHROMA = 48.0f const val GOOGLE_BLUE = 0xFF1b6ef3.toInt() const val MIN_CHROMA = 5 -enum class ChromaStrategy { +internal enum class ChromaStrategy { EQ, GTE } -enum class HueStrategy { +internal enum class HueStrategy { SOURCE, ADD, SUBTRACT } -class Chroma(val strategy: ChromaStrategy, val value: Double) { +internal class Chroma(val strategy: ChromaStrategy, val value: Double) { fun get(sourceChroma: Double): Double { return when (strategy) { ChromaStrategy.EQ -> value @@ -48,7 +48,7 @@ class Chroma(val strategy: ChromaStrategy, val value: Double) { } } -class Hue(val strategy: HueStrategy = HueStrategy.SOURCE, val value: Double = 0.0) { +internal class Hue(val strategy: HueStrategy = HueStrategy.SOURCE, val value: Double = 0.0) { fun get(sourceHue: Double): Double { return when (strategy) { HueStrategy.SOURCE -> sourceHue @@ -58,7 +58,7 @@ class Hue(val strategy: HueStrategy = HueStrategy.SOURCE, val value: Double = 0. } } -class TonalSpec(val hue: Hue = Hue(), val chroma: Chroma) { +internal class TonalSpec(val hue: Hue = Hue(), val chroma: Chroma) { fun shades(sourceColor: Cam): List { val hue = hue.get(sourceColor.hue.toDouble()) val chroma = chroma.get(sourceColor.chroma.toDouble()) @@ -66,7 +66,7 @@ class TonalSpec(val hue: Hue = Hue(), val chroma: Chroma) { } } -class CoreSpec( +internal class CoreSpec( val a1: TonalSpec, val a2: TonalSpec, val a3: TonalSpec, @@ -74,7 +74,7 @@ class CoreSpec( val n2: TonalSpec ) -enum class Style(val coreSpec: CoreSpec) { +enum class Style(internal val coreSpec: CoreSpec) { SPRITZ(CoreSpec( a1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)), a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 4.0)), @@ -105,7 +105,7 @@ enum class Style(val coreSpec: CoreSpec) { )), } -public class ColorScheme( +class ColorScheme( @ColorInt seed: Int, val darkTheme: Boolean, val style: Style = Style.TONAL_SPOT @@ -170,6 +170,7 @@ public class ColorScheme( " accent1: ${humanReadable(accent1)}\n" + " accent2: ${humanReadable(accent2)}\n" + " accent3: ${humanReadable(accent3)}\n" + + " style: $style\n" + "}" } diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index e2d0bb9991edf..31407b1d5cb2e 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -66,6 +66,8 @@ public class ThemeOverlayApplier implements Dumpable { "android.theme.customization.accent_color"; static final String OVERLAY_CATEGORY_SYSTEM_PALETTE = "android.theme.customization.system_palette"; + static final String OVERLAY_CATEGORY_THEME_STYLE = + "android.theme.customization.theme_style"; static final String OVERLAY_COLOR_SOURCE = "android.theme.customization.color_source"; diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index c86d77bfcc9fb..fb8055199d612 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -66,6 +66,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.monet.ColorScheme; +import com.android.systemui.monet.Style; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; @@ -121,8 +122,8 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable { private boolean mNeedsOverlayCreation; // Dominant color extracted from wallpaper, NOT the color used on the overlay protected int mMainWallpaperColor = Color.TRANSPARENT; - // Accent color extracted from wallpaper, NOT the color used on the overlay - protected int mWallpaperAccentColor = Color.TRANSPARENT; + // Theme variant: Vibrant, Tonal, Expressive, etc + private Style mThemeStyle = Style.TONAL_SPOT; // Accent colors overlay private FabricatedOverlay mSecondaryOverlay; // Neutral system colors overlay @@ -453,26 +454,20 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable { private void reevaluateSystemTheme(boolean forceReload) { final WallpaperColors currentColors = mCurrentColors.get(mUserTracker.getUserId()); final int mainColor; - final int accentCandidate; if (currentColors == null) { mainColor = Color.TRANSPARENT; - accentCandidate = Color.TRANSPARENT; } else { mainColor = getNeutralColor(currentColors); - accentCandidate = getAccentColor(currentColors); } - if (mMainWallpaperColor == mainColor && mWallpaperAccentColor == accentCandidate - && !forceReload) { + if (mMainWallpaperColor == mainColor && !forceReload) { return; } - mMainWallpaperColor = mainColor; - mWallpaperAccentColor = accentCandidate; if (mIsMonetEnabled) { - mSecondaryOverlay = getOverlay(mWallpaperAccentColor, ACCENT); - mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL); + mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle); + mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle); mNeedsOverlayCreation = true; if (DEBUG) { Log.d(TAG, "fetched overlays. accent: " + mSecondaryOverlay @@ -497,11 +492,11 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable { /** * Given a color candidate, return an overlay definition. */ - protected @Nullable FabricatedOverlay getOverlay(int color, int type) { + protected @Nullable FabricatedOverlay getOverlay(int color, int type, Style style) { boolean nightMode = (mContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; - mColorScheme = new ColorScheme(color, nightMode); + mColorScheme = new ColorScheme(color, nightMode, style); List colorShades = type == ACCENT ? mColorScheme.getAllAccentColors() : mColorScheme.getAllNeutralColors(); String name = type == ACCENT ? "accent" : "neutral"; @@ -537,6 +532,7 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable { currentUser); if (DEBUG) Log.d(TAG, "updateThemeOverlays. Setting: " + overlayPackageJson); final Map categoryToPackage = new ArrayMap<>(); + Style newStyle = mThemeStyle; if (!TextUtils.isEmpty(overlayPackageJson)) { try { JSONObject object = new JSONObject(overlayPackageJson); @@ -547,11 +543,25 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable { categoryToPackage.put(category, identifier); } } + + try { + newStyle = Style.valueOf( + object.getString(ThemeOverlayApplier.OVERLAY_CATEGORY_THEME_STYLE)); + } catch (IllegalArgumentException e) { + newStyle = Style.TONAL_SPOT; + } } catch (JSONException e) { Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e); } } + if (mIsMonetEnabled && newStyle != mThemeStyle) { + mThemeStyle = newStyle; + mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle); + mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle); + mNeedsOverlayCreation = true; + } + // Let's generate system overlay if the style picker decided to override it. OverlayIdentifier systemPalette = categoryToPackage.get(OVERLAY_CATEGORY_SYSTEM_PALETTE); if (mIsMonetEnabled && systemPalette != null && systemPalette.getPackageName() != null) { @@ -561,9 +571,11 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable { colorString = "#" + colorString; } int color = Color.parseColor(colorString); - mNeutralOverlay = getOverlay(color, NEUTRAL); + mNeutralOverlay = getOverlay(color, NEUTRAL, mThemeStyle); + mSecondaryOverlay = getOverlay(color, ACCENT, mThemeStyle); mNeedsOverlayCreation = true; categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE); + categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR); } catch (Exception e) { // Color.parseColor doesn't catch any exceptions from the calls it makes Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName(), e); @@ -574,30 +586,6 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable { // setting. We need to sanitize the input, otherwise the overlay transaction will // fail. categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE); - } catch (NumberFormatException e) { - // This is a package name. All good, let's continue - } - } - - // Same for accent color. - OverlayIdentifier accentPalette = categoryToPackage.get(OVERLAY_CATEGORY_ACCENT_COLOR); - if (mIsMonetEnabled && accentPalette != null && accentPalette.getPackageName() != null) { - try { - String colorString = accentPalette.getPackageName().toLowerCase(); - if (!colorString.startsWith("#")) { - colorString = "#" + colorString; - } - int color = Color.parseColor(colorString); - mSecondaryOverlay = getOverlay(color, ACCENT); - mNeedsOverlayCreation = true; - categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR); - } catch (Exception e) { - // Color.parseColor doesn't catch any exceptions from the calls it makes - Log.w(TAG, "Invalid color definition: " + accentPalette.getPackageName(), e); - } - } else if (!mIsMonetEnabled && accentPalette != null) { - try { - Integer.parseInt(accentPalette.getPackageName().toLowerCase(), 16); categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR); } catch (NumberFormatException e) { // This is a package name. All good, let's continue @@ -642,7 +630,6 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable { public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("mSystemColors=" + mCurrentColors); pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor)); - pw.println("mWallpaperAccentColor=" + Integer.toHexString(mWallpaperAccentColor)); pw.println("mSecondaryOverlay=" + mSecondaryOverlay); pw.println("mNeutralOverlay=" + mNeutralOverlay); pw.println("mIsMonetEnabled=" + mIsMonetEnabled); @@ -650,5 +637,6 @@ public class ThemeOverlayController extends CoreStartable implements Dumpable { pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation); pw.println("mAcceptColorEvents=" + mAcceptColorEvents); pw.println("mDeferredThemeEvaluation=" + mDeferredThemeEvaluation); + pw.println("mThemeStyle=" + mThemeStyle); } } 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 34407b0440c6f..fd225f2de6b48 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -39,6 +39,7 @@ import android.content.BroadcastReceiver; import android.content.Intent; import android.content.om.FabricatedOverlay; import android.content.om.OverlayIdentifier; +import android.database.ContentObserver; import android.graphics.Color; import android.os.Handler; import android.os.UserHandle; @@ -55,6 +56,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.monet.Style; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; @@ -117,6 +119,9 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { private ArgumentCaptor mWakefulnessLifecycleObserver; @Captor private ArgumentCaptor mUserTrackerCallback; + @Captor + private ArgumentCaptor mSettingsObserver; + private Style mCurrentStyle; @Before public void setup() { @@ -130,10 +135,11 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) { @Nullable @Override - protected FabricatedOverlay getOverlay(int color, int type) { + protected FabricatedOverlay getOverlay(int color, int type, Style style) { FabricatedOverlay overlay = mock(FabricatedOverlay.class); when(overlay.getIdentifier()) .thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000))); + mCurrentStyle = style; return overlay; } }; @@ -148,6 +154,10 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { verify(mWakefulnessLifecycle).addObserver(mWakefulnessLifecycleObserver.capture()); verify(mDumpManager).registerDumpable(any(), any()); verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture()); + verify(mSecureSettings).registerContentObserverForUser( + eq(Settings.Secure.getUriFor(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES)), + eq(false), mSettingsObserver.capture(), eq(UserHandle.USER_ALL) + ); } @Test @@ -321,6 +331,40 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { .applyCurrentUserOverlays(any(), any(), anyInt(), any()); } + @Test + public void onSettingChanged_honorThemeStyle() { + when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true); + for (Style style : Style.values()) { + reset(mSecureSettings); + + String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\"," + + "\"android.theme.customization.theme_style\":\"" + style.name() + "\"}"; + + when(mSecureSettings.getStringForUser( + eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt())) + .thenReturn(jsonString); + + mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId()); + + assertThat(mCurrentStyle).isEqualTo(style); + } + } + + @Test + public void onSettingChanged_invalidStyle() { + when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true); + String jsonString = "{\"android.theme.customization.system_palette\":\"A16B00\"," + + "\"android.theme.customization.theme_style\":\"some_invalid_name\"}"; + + when(mSecureSettings.getStringForUser( + eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt())) + .thenReturn(jsonString); + + mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId()); + + assertThat(mCurrentStyle).isEqualTo(Style.TONAL_SPOT); + } + @Test public void onWallpaperColorsChanged_ResetThemeWithNewHomeAndLockWallpaper() { // Should ask for a new theme when wallpaper colors change @@ -611,11 +655,10 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) { @Nullable @Override - protected FabricatedOverlay getOverlay(int color, int type) { + protected FabricatedOverlay getOverlay(int color, int type, Style style) { FabricatedOverlay overlay = mock(FabricatedOverlay.class); when(overlay.getIdentifier()) .thenReturn(new OverlayIdentifier("com.thebest.livewallpaperapp.ever")); - return overlay; } @@ -648,7 +691,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mUserTracker, mDumpManager, mFeatureFlags, mWakefulnessLifecycle) { @Nullable @Override - protected FabricatedOverlay getOverlay(int color, int type) { + protected FabricatedOverlay getOverlay(int color, int type, Style style) { FabricatedOverlay overlay = mock(FabricatedOverlay.class); when(overlay.getIdentifier()) .thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000)));