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
This commit is contained in:
Lucas Dupin
2022-01-06 15:48:03 -08:00
parent a94b166036
commit 7719202d38
4 changed files with 85 additions and 51 deletions

View File

@@ -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<Int> {
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" +
"}"
}

View File

@@ -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";

View File

@@ -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<Integer> 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<String, OverlayIdentifier> 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);
}
}

View File

@@ -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<WakefulnessLifecycle.Observer> mWakefulnessLifecycleObserver;
@Captor
private ArgumentCaptor<UserTracker.Callback> mUserTrackerCallback;
@Captor
private ArgumentCaptor<ContentObserver> 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)));