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:
@@ -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" +
|
||||
"}"
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
|
||||
Reference in New Issue
Block a user