android 壁纸主题色
官方文档:
https://source.android.google.cn/docs/core/display/material?hl=zh-cn
https://source.android.google.cn/docs/core/display/dynamic-color?hl=zh-cn
adb命令设置颜色:
adb shell settings put secure theme_customization_overlay_packages "\{\"android.theme.customization.system_palette\":\"1CC886\",\"android.theme.customization.theme_style\":\"TONAL_SPOT\"\}"
adb shell settings put secure theme_customization_overlay_packages "\{\"android.theme.customization.system_palette\":\"1CC886\",\"android.theme.customization.theme_style\":\"EXPRESSIVE\"\}"
主要样式:
Arrays.asList(Style.EXPRESSIVE, Style.SPRITZ, Style.TONAL_SPOT, Style.FRUIT_SALAD, Style.RAINBOW, Style.VIBRANT);
Primary:主色调(品牌主色调)Secondary:副色调(能从主色调中跳色出来)Tertiary:变化色或称第三色(可以中和主色调和副色调)Background:背景底色Surface:表层色Error:用在错误字、错误对话框
在 Material Design 3 官网还有这样区分颜色
Primary、Secondary、Tertiary又称为强调色(Accent colors)
Background、Surface 又称自然色(Neutral colors)
Error 则是追加色(Additional colors)
vendor/packages/apps/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
mWallpaperManager.addOnColorsChangedListener(mOnColorsChangedListener,null,UserHandle.USER_ALL);
private final OnColorsChangedListener mOnColorsChangedListener = new OnColorsChangedListener() {
@Override
public void onColorsChanged(WallpaperColors wallpaperColors, int which) {
throw new IllegalStateException("This should never be invoked, all messages should "
+ "arrive on the overload that has a user id");
}
@Override
public void onColorsChanged(WallpaperColors wallpaperColors, int which, int userId) {
......
handleWallpaperColors(wallpaperColors, which, userId);
}
};
private void handleWallpaperColors(WallpaperColors wallpaperColors, int flags, int userId) {
String overlayPackageJson = mSecureSettings.getStringForUser(
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
currentUser);
boolean isDestinationBoth = (flags == (WallpaperManager.FLAG_SYSTEM
| WallpaperManager.FLAG_LOCK));
try {
JSONObject jsonObject = (overlayPackageJson == null) ? new JSONObject()
: new JSONObject(overlayPackageJson);
// The latest applied wallpaper should be the source of system colors when:
// There is not preset color applied and the incoming wallpaper color is not applied
if (!COLOR_SOURCE_PRESET.equals(jsonObject.optString(OVERLAY_COLOR_SOURCE))
&& ((flags & latestWallpaperType) != 0 && !isSeedColorSet(jsonObject,
wallpaperColors))) {
mSkipSettingChange = true;
if (jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR) || jsonObject.has(
OVERLAY_CATEGORY_SYSTEM_PALETTE)) {
jsonObject.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
jsonObject.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
jsonObject.remove(OVERLAY_COLOR_INDEX);
}
// Keep color_both value because users can change either or both home and
// lock screen wallpapers.
jsonObject.put(OVERLAY_COLOR_BOTH, isDestinationBoth ? "1" : "0");
jsonObject.put(OVERLAY_COLOR_SOURCE,
(flags == WallpaperManager.FLAG_LOCK) ? COLOR_SOURCE_LOCK
: COLOR_SOURCE_HOME);
jsonObject.put(TIMESTAMP_FIELD, System.currentTimeMillis());
if (DEBUG) {
Log.d(TAG, "Updating theme setting from "
+ overlayPackageJson + " to " + jsonObject.toString());
}
mSecureSettings.putStringForUser(
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
jsonObject.toString(), UserHandle.USER_CURRENT);
}
} catch (JSONException e) {
Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
}
reevaluateSystemTheme(false /* forceReload */);
}
2.切换用户,壁纸变换,开机引导走完等调用reevaluateSystemTheme(true)
reevaluateSystemTheme(boolean forceReload)`
3.根据壁纸颜色,得到主要颜色 mSecondaryOverlay,mNeutralOverlay
private void reevaluateSystemTheme(boolean forceReload) {
final WallpaperColors currentColors = mCurrentColors.get(mUserTracker.getUserId());
final int mainColor;
if (currentColors == null) {
mainColor = Color.TRANSPARENT;
} else {
mainColor = getNeutralColor(currentColors);
}
if (mMainWallpaperColor == mainColor && !forceReload) {
return;
}
mMainWallpaperColor = mainColor;
if (mIsMonetEnabled) {
mThemeStyle = fetchThemeStyleFromSetting();
mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle);
mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle);
mNeedsOverlayCreation = true;
if (DEBUG) {
Log.d(TAG, "fetched overlays. accent: " + mSecondaryOverlay
+ " neutral: " + mNeutralOverlay);
}
}
updateThemeOverlays();
}
protected @Nullable FabricatedOverlay getOverlay(int color, int type, Style style) {
boolean nightMode = (mResources.getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
mColorScheme = new ColorScheme(color, nightMode, style);
List<Integer> colorShades = type == ACCENT
? mColorScheme.getAllAccentColors() : mColorScheme.getAllNeutralColors();
String name = type == ACCENT ? "accent" : "neutral";
int paletteSize = mColorScheme.getAccent1().size();
FabricatedOverlay.Builder overlay =
new FabricatedOverlay.Builder("com.android.systemui", name, "android");
for (int i = 0; i < colorShades.size(); i++) {
int luminosity = i % paletteSize;
int paletteIndex = i / paletteSize + 1;
String resourceName;
switch (luminosity) {
case 0:
resourceName = "android:color/system_" + name + paletteIndex + "_10";
break;
case 1:
resourceName = "android:color/system_" + name + paletteIndex + "_50";
break;
default:
int l = luminosity - 1;
resourceName = "android:color/system_" + name + paletteIndex + "_" + l + "00";
}
overlay.setResourceValue(resourceName, TypedValue.TYPE_INT_COLOR_ARGB8,
ColorUtils.setAlphaComponent(colorShades.get(i), 0xFF));
}
return overlay.build();
}
mssi/vendor/mediatek/proprietary/packages/apps/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
class ColorScheme(
@ColorInt val seed: Int,
val darkTheme: Boolean,
val style: Style = Style.TONAL_SPOT
) {
val accent1: List<Int>
val accent2: List<Int>
val accent3: List<Int>
val neutral1: List<Int>
val neutral2: List<Int>
constructor(@ColorInt seed: Int, darkTheme: Boolean):
this(seed, darkTheme, Style.TONAL_SPOT)
@JvmOverloads
constructor(
wallpaperColors: WallpaperColors,
darkTheme: Boolean,
style: Style = Style.TONAL_SPOT
):
this(getSeedColor(wallpaperColors, style != Style.CONTENT), darkTheme, style)
val allAccentColors: List<Int>
get() {
val allColors = mutableListOf<Int>()
allColors.addAll(accent1)
allColors.addAll(accent2)
allColors.addAll(accent3)
return allColors
}
val allNeutralColors: List<Int>
get() {
val allColors = mutableListOf<Int>()
allColors.addAll(neutral1)
allColors.addAll(neutral2)
return allColors
}
val backgroundColor
get() = ColorUtils.setAlphaComponent(if (darkTheme) neutral1[8] else neutral1[0], 0xFF)
val accentColor
get() = ColorUtils.setAlphaComponent(if (darkTheme) accent1[2] else accent1[6], 0xFF)
init {
val proposedSeedCam = Cam.fromInt(seed)
val seedArgb = if (seed == Color.TRANSPARENT) {
GOOGLE_BLUE
} else if (style != Style.CONTENT && proposedSeedCam.chroma < 5) {
GOOGLE_BLUE
} else {
seed
}
val camSeed = Cam.fromInt(seedArgb)
//通过从壁纸中取到seedColor得到一个camSeed Cam对象,5组颜色(其中3组强调色,2组自然色),每一组里有13种颜色
accent1 = style.coreSpec.a1.shades(camSeed)
accent2 = style.coreSpec.a2.shades(camSeed)
accent3 = style.coreSpec.a3.shades(camSeed)
neutral1 = style.coreSpec.n1.shades(camSeed)
neutral2 = style.coreSpec.n2.shades(camSeed)
}
4.应用主题颜色到系统中applyCurrentUserOverlays
private void updateThemeOverlays() {
final int currentUser = mUserTracker.getUserId();
final String overlayPackageJson = mSecureSettings.getStringForUser(
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
currentUser);
if (DEBUG) Log.d(TAG, "updateThemeOverlays. Setting: " + overlayPackageJson);
final Map<String, OverlayIdentifier> categoryToPackage = new ArrayMap<>();
if (!TextUtils.isEmpty(overlayPackageJson)) {
try {
JSONObject object = new JSONObject(overlayPackageJson);
for (String category : ThemeOverlayApplier.THEME_CATEGORIES) {
if (object.has(category)) {
OverlayIdentifier identifier =
new OverlayIdentifier(object.getString(category));
categoryToPackage.put(category, identifier);
}
}
} catch (JSONException e) {
Log.i(TAG, "Failed to parse THEME_CUSTOMIZATION_OVERLAY_PACKAGES.", e);
}
}
// 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) {
try {
String colorString = systemPalette.getPackageName().toLowerCase();
if (!colorString.startsWith("#")) {
colorString = "#" + colorString;
}
int color = Color.parseColor(colorString);
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);
}
} else if (!mIsMonetEnabled && systemPalette != null) {
try {
// It's possible that we flipped the flag off and still have a @ColorInt in the
// setting. We need to sanitize the input, otherwise the overlay transaction will
// fail.
categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
} catch (NumberFormatException e) {
// This is a package name. All good, let's continue
}
}
// Compatibility with legacy themes, where full packages were defined, instead of just
// colors.
if (!categoryToPackage.containsKey(OVERLAY_CATEGORY_SYSTEM_PALETTE)
&& mNeutralOverlay != null) {
categoryToPackage.put(OVERLAY_CATEGORY_SYSTEM_PALETTE,
mNeutralOverlay.getIdentifier());
}
if (!categoryToPackage.containsKey(OVERLAY_CATEGORY_ACCENT_COLOR)
&& mSecondaryOverlay != null) {
categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, mSecondaryOverlay.getIdentifier());
}
Set<UserHandle> managedProfiles = new HashSet<>();
for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) {
if (userInfo.isManagedProfile()) {
managedProfiles.add(userInfo.getUserHandle());
}
}
if (colorSchemeIsApplied(managedProfiles)) {
return;
}
if (DEBUG) {
Log.d(TAG, "Applying overlays: " + categoryToPackage.keySet().stream()
.map(key -> key + " -> " + categoryToPackage.get(key)).collect(
Collectors.joining(", ")));
}
boolean defMaterialYouEnabled = (Settings.Global.getInt(mContext.getContentResolver(),Settings.Global.DEF_MATERIAL_YOU_ENABLED, 1) == 1);
Log.d(TAG,"defMaterialYouEnabled:"+defMaterialYouEnabled);
if (mNeedsOverlayCreation) {
mNeedsOverlayCreation = false;
if(defMaterialYouEnabled)
mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
mSecondaryOverlay, mNeutralOverlay
}, currentUser, managedProfiles);
} else {
if(defMaterialYouEnabled)
mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
managedProfiles);
}
}