Loading packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt +1 −200 Original line number Original line Diff line number Diff line Loading @@ -17,34 +17,24 @@ package com.android.systemui.shared.clocks package com.android.systemui.shared.clocks import android.content.Context import android.content.Context import android.content.res.ColorStateList import android.content.res.Resources import android.content.res.Resources import android.graphics.Color import android.graphics.Typeface import android.graphics.Typeface import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable import android.util.TypedValue import android.util.TypedValue import com.android.internal.graphics.ColorUtils import com.android.internal.graphics.cam.Cam import com.android.internal.graphics.cam.CamUtils import com.android.internal.policy.SystemBarUtils import com.android.internal.policy.SystemBarUtils import com.android.systemui.log.core.Logger import com.android.systemui.log.core.Logger import com.android.systemui.log.core.MessageBuffer import com.android.systemui.log.core.MessageBuffer import com.android.systemui.monet.ColorScheme import com.android.systemui.monet.Style as MonetStyle import com.android.systemui.monet.Style as MonetStyle import com.android.systemui.monet.TonalPalette import java.io.IOException import java.io.IOException import kotlin.math.abs class AssetLoader class AssetLoader private constructor( private constructor( private val pluginCtx: Context, private val pluginCtx: Context, private val sysuiCtx: Context, private val sysuiCtx: Context, private val baseDir: String, private val baseDir: String, var colorScheme: ColorScheme?, var seedColor: Int?, var seedColor: Int?, var overrideChroma: Float?, var overrideChroma: Float?, val typefaceCache: TypefaceCache, val typefaceCache: TypefaceCache, val getThemeSeedColor: (Context) -> Int, messageBuffer: MessageBuffer, messageBuffer: MessageBuffer, ) { ) { val logger = Logger(messageBuffer, TAG) val logger = Logger(messageBuffer, TAG) Loading @@ -59,12 +49,10 @@ private constructor( sysuiCtx: Context, sysuiCtx: Context, baseDir: String, baseDir: String, messageBuffer: MessageBuffer, messageBuffer: MessageBuffer, getThemeSeedColor: ((Context) -> Int)? = null, ) : this( ) : this( pluginCtx, pluginCtx, sysuiCtx, sysuiCtx, baseDir, baseDir, colorScheme = null, seedColor = null, seedColor = null, overrideChroma = null, overrideChroma = null, typefaceCache = typefaceCache = Loading @@ -72,7 +60,6 @@ private constructor( // TODO(b/364680873): Move constant to config_clockFontFamily when shipping // TODO(b/364680873): Move constant to config_clockFontFamily when shipping return@TypefaceCache Typeface.create("google-sans-flex-clock", Typeface.NORMAL) return@TypefaceCache Typeface.create("google-sans-flex-clock", Typeface.NORMAL) }, }, getThemeSeedColor = getThemeSeedColor ?: Companion::getThemeSeedColor, messageBuffer = messageBuffer, messageBuffer = messageBuffer, ) ) Loading @@ -92,107 +79,6 @@ private constructor( return res.getString(id) return res.getString(id) } } fun tryReadColor(resStr: String): Int? = tryRead(resStr, ::readColor) fun readColor(resStr: String): Int { if (resStr.startsWith("#")) { return Color.parseColor(resStr) } val schemeColor = tryParseColorFromScheme(resStr) if (schemeColor != null) { logColor("ColorScheme: $resStr", schemeColor) return checkChroma(schemeColor) } val result = resolveColorResourceId(resStr) if (result == null) { throw IOException("Failed to parse color: $resStr") } val (res, colorId, targetTone) = result val color = res.getColor(colorId) if (targetTone == null || TonalPalette.SHADE_KEYS.contains(targetTone.toInt())) { logColor("Resources: $resStr", color) return checkChroma(color) } else { val interpolatedColor = ColorStateList.valueOf(color) .withLStar((1000f - targetTone) / 10f) .getDefaultColor() logColor("Resources (interpolated tone): $resStr", interpolatedColor) return checkChroma(interpolatedColor) } } private fun checkChroma(color: Int): Int { return overrideChroma?.let { val cam = Cam.fromInt(color) val tone = CamUtils.lstarFromInt(color) val result = ColorUtils.CAMToColor(cam.hue, it, tone) logColor("Chroma override", result) result } ?: color } private fun tryParseColorFromScheme(resStr: String): Int? { val colorScheme = this.colorScheme if (colorScheme == null) { logger.w("No color scheme available") return null } val (packageName, category, name) = parseResourceId(resStr) if (packageName != "android" || category != "color") { logger.w("Failed to parse package from $resStr") return null } var parts = name.split('_') if (parts.size != 3) { logger.w("Failed to find palette and shade from $name") return null } val (_, paletteKey, shadeKeyStr) = parts val palette = when (paletteKey) { "accent1" -> colorScheme.accent1 "accent2" -> colorScheme.accent2 "accent3" -> colorScheme.accent3 "neutral1" -> colorScheme.neutral1 "neutral2" -> colorScheme.neutral2 else -> return null } if (shadeKeyStr.contains("+") || shadeKeyStr.contains("-")) { val signIndex = shadeKeyStr.indexOfLast { it == '-' || it == '+' } // Use the tone of the seed color if it was set explicitly. var baseTone = if (seedColor != null) colorScheme.seedTone.toFloat() else shadeKeyStr.substring(0, signIndex).toFloatOrNull() val diff = shadeKeyStr.substring(signIndex).toFloatOrNull() if (baseTone == null) { logger.w("Failed to parse base tone from $shadeKeyStr") return null } if (diff == null) { logger.w("Failed to parse relative tone from $shadeKeyStr") return null } return palette.getAtTone(baseTone + diff) } else { val shadeKey = shadeKeyStr.toIntOrNull() if (shadeKey == null) { logger.w("Failed to parse tone from $shadeKeyStr") return null } return palette.allShadesMapped.get(shadeKey) ?: palette.getAtTone(shadeKey.toFloat()) } } fun readFontAsset(resStr: String): Typeface = typefaceCache.getTypeface(resStr) fun readFontAsset(resStr: String): Typeface = typefaceCache.getTypeface(resStr) fun tryReadTextAsset(path: String?): String? = tryRead(path, ::readTextAsset) fun tryReadTextAsset(path: String?): String? = tryRead(path, ::readTextAsset) Loading Loading @@ -250,52 +136,6 @@ private constructor( } } } } fun resolveColorResourceId(resStr: String): Triple<Resources, Int, Float?>? { var (packageName, category, name) = parseResourceId(resStr) // Convert relative tonal specifiers to standard val relIndex = name.indexOfLast { it == '_' } val isToneRelative = name.contains("-") || name.contains("+") val targetTone = if (packageName != "android") { null } else if (isToneRelative) { val signIndex = name.indexOfLast { it == '-' || it == '+' } val baseTone = name.substring(relIndex + 1, signIndex).toFloatOrNull() var diff = name.substring(signIndex).toFloatOrNull() if (baseTone == null || diff == null) { logger.w("Failed to parse relative tone from $name") return null } baseTone + diff } else { val absTone = name.substring(relIndex + 1).toFloatOrNull() if (absTone == null) { logger.w("Failed to parse absolute tone from $name") return null } absTone } if ( targetTone != null && (isToneRelative || !TonalPalette.SHADE_KEYS.contains(targetTone.toInt())) ) { val closeTone = TonalPalette.SHADE_KEYS.minBy { abs(it - targetTone) } val prevName = name name = name.substring(0, relIndex + 1) + closeTone logger.i("Converted $prevName to $name") } val result = resolveResourceId(packageName, category, name) if (result == null) { return null } val (res, resId) = result return Triple(res, resId, targetTone) } fun resolveResourceId(resStr: String): Pair<Resources, Int>? { fun resolveResourceId(resStr: String): Pair<Resources, Int>? { val (packageName, category, name) = parseResourceId(resStr) val (packageName, category, name) = parseResourceId(resStr) return resolveResourceId(packageName, category, name) return resolveResourceId(packageName, category, name) Loading Loading @@ -331,8 +171,7 @@ private constructor( try { try { if (path.startsWith("@")) { if (path.startsWith("@")) { val pair = resolveResourceId(path) val pair = resolveResourceId(path) val colorPair = resolveColorResourceId(path) return pair != null return pair != null || colorPair != null } else { } else { val stream = pluginCtx.resources.assets.open("$baseDir$path") val stream = pluginCtx.resources.assets.open("$baseDir$path") if (stream == null) { if (stream == null) { Loading @@ -352,37 +191,14 @@ private constructor( pluginCtx, pluginCtx, sysuiCtx, sysuiCtx, baseDir, baseDir, colorScheme, seedColor, seedColor, overrideChroma, overrideChroma, typefaceCache, typefaceCache, getThemeSeedColor, messageBuffer ?: logger.buffer, messageBuffer ?: logger.buffer, ) ) fun setSeedColor(seedColor: Int?, style: MonetStyle?) { fun setSeedColor(seedColor: Int?, style: MonetStyle?) { this.seedColor = seedColor this.seedColor = seedColor refreshColorPalette(style) } fun refreshColorPalette(style: MonetStyle?) { val seedColor = this.seedColor ?: getThemeSeedColor(sysuiCtx).also { logColor("Theme Seed Color", it) } this.colorScheme = ColorScheme( seedColor, false, // darkTheme is not used for palette generation style ?: MonetStyle.CLOCK, ) // Enforce low chroma on output colors if low chroma theme is selected this.overrideChroma = run { val cam = colorScheme?.seed?.let { Cam.fromInt(it) } if (cam != null && cam.chroma < LOW_CHROMA_LIMIT) { return@run cam.chroma * LOW_CHROMA_SCALE } return@run null } } } fun getClockPaddingStart(): Int { fun getClockPaddingStart(): Int { Loading Loading @@ -430,22 +246,7 @@ private constructor( throw Exception("Cannot find id of $name from $TAG") throw Exception("Cannot find id of $name from $TAG") } } private fun logColor(name: String, color: Int) { if (DEBUG_COLOR) { val cam = Cam.fromInt(color) val tone = CamUtils.lstarFromInt(color) logger.i("$name -> (hue: ${cam.hue}, chroma: ${cam.chroma}, tone: $tone)") } } companion object { companion object { private val DEBUG_COLOR = true private val LOW_CHROMA_LIMIT = 15 private val LOW_CHROMA_SCALE = 1.5f private val TAG = AssetLoader::class.simpleName!! private val TAG = AssetLoader::class.simpleName!! private fun getThemeSeedColor(ctx: Context): Int { return ctx.resources.getColor(android.R.color.system_palette_key_color_primary_light) } } } } } packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt +10 −12 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceEvents import com.android.systemui.plugins.clocks.ClockFaceEvents import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.shared.clocks.view.FlexClockView import com.android.systemui.shared.clocks.view.FlexClockView Loading @@ -46,7 +47,6 @@ class ComposedDigitalLayerController( val layerControllers = mutableListOf<SimpleClockLayerController>() val layerControllers = mutableListOf<SimpleClockLayerController>() val dozeState = DefaultClockController.AnimationState(1F) val dozeState = DefaultClockController.AnimationState(1F) var isRegionDark = true override val view = FlexClockView(ctx, assets, messageBuffer) override val view = FlexClockView(ctx, assets, messageBuffer) Loading Loading @@ -103,10 +103,6 @@ class ComposedDigitalLayerController( view.onZenDataChanged(data) view.onZenDataChanged(data) } } override fun onColorPaletteChanged(resources: Resources) {} override fun onSeedColorChanged(seedColor: Int?) {} override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {} override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {} override var isReactiveTouchInteractionEnabled override var isReactiveTouchInteractionEnabled Loading @@ -116,10 +112,6 @@ class ComposedDigitalLayerController( } } } } override fun updateColors() { view.updateColors(assets, isRegionDark) } override val animations = override val animations = object : ClockAnimations { object : ClockAnimations { override fun enter() { override fun enter() { Loading Loading @@ -158,9 +150,15 @@ class ComposedDigitalLayerController( refreshTime() refreshTime() } } override fun onRegionDarknessChanged(isRegionDark: Boolean) { override fun onThemeChanged(theme: ThemeConfig) { this@ComposedDigitalLayerController.isRegionDark = isRegionDark val color = updateColors() when { theme.seedColor != null -> theme.seedColor!! theme.isDarkTheme -> resources.getColor(android.R.color.system_accent1_100) else -> resources.getColor(android.R.color.system_accent2_600) } view.updateColor(color) } } override fun onFontSettingChanged(fontSizePx: Float) { override fun onFontSettingChanged(fontSizePx: Float) { Loading packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +32 −46 Original line number Original line Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ClockSettings import com.android.systemui.plugins.clocks.ClockSettings import com.android.systemui.plugins.clocks.DefaultClockFaceLayout import com.android.systemui.plugins.clocks.DefaultClockFaceLayout import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.plugins.clocks.ZenData import java.io.PrintWriter import java.io.PrintWriter Loading Loading @@ -100,28 +101,33 @@ class DefaultClockController( events.onLocaleChanged(Locale.getDefault()) events.onLocaleChanged(Locale.getDefault()) } } override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) { override fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) { largeClock.recomputePadding(null) largeClock.recomputePadding(null) largeClock.animations = LargeClockAnimations(largeClock.view, dozeFraction, foldFraction) largeClock.animations = LargeClockAnimations(largeClock.view, dozeFraction, foldFraction) smallClock.animations = DefaultClockAnimations(smallClock.view, dozeFraction, foldFraction) smallClock.animations = DefaultClockAnimations(smallClock.view, dozeFraction, foldFraction) events.onColorPaletteChanged(resources) val theme = ThemeConfig(isDarkTheme, settings?.seedColor) largeClock.events.onThemeChanged(theme) smallClock.events.onThemeChanged(theme) events.onTimeZoneChanged(TimeZone.getDefault()) events.onTimeZoneChanged(TimeZone.getDefault()) smallClock.events.onTimeTick() smallClock.events.onTimeTick() largeClock.events.onTimeTick() largeClock.events.onTimeTick() } } open inner class DefaultClockFaceController( open inner class DefaultClockFaceController( override val view: AnimatableClockView, override val view: AnimatableClockView, var seedColor: Int?, seedColor: Int?, messageBuffer: MessageBuffer?, messageBuffer: MessageBuffer?, ) : ClockFaceController { ) : ClockFaceController { // MAGENTA is a placeholder, and will be assigned correctly in initialize // MAGENTA is a placeholder, and will be assigned correctly in initialize private var currentColor = Color.MAGENTA private var currentColor = seedColor ?: Color.MAGENTA private var isRegionDark = false protected var targetRegion: Rect? = null protected var targetRegion: Rect? = null override val config = ClockFaceConfig() override val config = ClockFaceConfig() override var theme = ThemeConfig(true, seedColor) override val layout = override val layout = DefaultClockFaceLayout(view).apply { DefaultClockFaceLayout(view).apply { views[0].id = views[0].id = Loading @@ -132,9 +138,6 @@ class DefaultClockController( internal set internal set init { init { if (seedColor != null) { currentColor = seedColor!! } view.setColors(DOZE_COLOR, currentColor) view.setColors(DOZE_COLOR, currentColor) messageBuffer?.let { view.messageBuffer = it } messageBuffer?.let { view.messageBuffer = it } } } Loading @@ -143,9 +146,26 @@ class DefaultClockController( object : ClockFaceEvents { object : ClockFaceEvents { override fun onTimeTick() = view.refreshTime() override fun onTimeTick() = view.refreshTime() override fun onRegionDarknessChanged(isRegionDark: Boolean) { override fun onThemeChanged(theme: ThemeConfig) { this@DefaultClockFaceController.isRegionDark = isRegionDark this@DefaultClockFaceController.theme = theme updateColor() val color = when { theme.seedColor != null -> theme.seedColor!! theme.isDarkTheme -> resources.getColor(android.R.color.system_accent1_100) else -> resources.getColor(android.R.color.system_accent2_600) } if (currentColor == color) { return } currentColor = color view.setColors(DOZE_COLOR, color) if (!animations.dozeState.isActive) { view.animateColorChange() } } } override fun onTargetRegionChanged(targetRegion: Rect?) { override fun onTargetRegionChanged(targetRegion: Rect?) { Loading @@ -165,27 +185,6 @@ class DefaultClockController( } } open fun recomputePadding(targetRegion: Rect?) {} open fun recomputePadding(targetRegion: Rect?) {} fun updateColor() { val color = if (seedColor != null) { seedColor!! } else if (isRegionDark) { resources.getColor(android.R.color.system_accent1_100) } else { resources.getColor(android.R.color.system_accent2_600) } if (currentColor == color) { return } currentColor = color view.setColors(DOZE_COLOR, color) if (!animations.dozeState.isActive) { view.animateColorChange() } } } } inner class LargeClockFaceController( inner class LargeClockFaceController( Loading Loading @@ -248,19 +247,6 @@ class DefaultClockController( override fun onTimeZoneChanged(timeZone: TimeZone) = override fun onTimeZoneChanged(timeZone: TimeZone) = clocks.forEach { it.onTimeZoneChanged(timeZone) } clocks.forEach { it.onTimeZoneChanged(timeZone) } override fun onColorPaletteChanged(resources: Resources) { largeClock.updateColor() smallClock.updateColor() } override fun onSeedColorChanged(seedColor: Int?) { largeClock.seedColor = seedColor smallClock.seedColor = seedColor largeClock.updateColor() smallClock.updateColor() } override fun onLocaleChanged(locale: Locale) { override fun onLocaleChanged(locale: Locale) { val nf = NumberFormat.getInstance(locale) val nf = NumberFormat.getInstance(locale) if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) { if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) { Loading packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +1 −0 Original line number Original line Diff line number Diff line Loading @@ -58,6 +58,7 @@ class DefaultClockProvider( val buffer = val buffer = messageBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.INFO) messageBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.INFO) val assets = AssetLoader(ctx, ctx, "clocks/", buffer) val assets = AssetLoader(ctx, ctx, "clocks/", buffer) assets.setSeedColor(settings.seedColor, null) FlexClockController(ctx, resources, assets, FLEX_DESIGN, messageBuffers) FlexClockController(ctx, resources, assets, FLEX_DESIGN, messageBuffers) } else { } else { DefaultClockController( DefaultClockController( Loading packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt +16 −26 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.shared.clocks.view.FlexClockView import com.android.systemui.shared.clocks.view.FlexClockView Loading Loading @@ -97,24 +98,6 @@ class FlexClockController( largeClock.events.onLocaleChanged(locale) largeClock.events.onLocaleChanged(locale) } } override fun onColorPaletteChanged(resources: Resources) { assets.refreshColorPalette(design.colorPalette) smallClock.assets.refreshColorPalette(design.colorPalette) largeClock.assets.refreshColorPalette(design.colorPalette) smallClock.events.onColorPaletteChanged(resources) largeClock.events.onColorPaletteChanged(resources) } override fun onSeedColorChanged(seedColor: Int?) { assets.setSeedColor(seedColor, design.colorPalette) smallClock.assets.setSeedColor(seedColor, design.colorPalette) largeClock.assets.setSeedColor(seedColor, design.colorPalette) smallClock.events.onSeedColorChanged(seedColor) largeClock.events.onSeedColorChanged(seedColor) } override fun onWeatherDataChanged(data: WeatherData) { override fun onWeatherDataChanged(data: WeatherData) { smallClock.events.onWeatherDataChanged(data) smallClock.events.onWeatherDataChanged(data) largeClock.events.onWeatherDataChanged(data) largeClock.events.onWeatherDataChanged(data) Loading @@ -136,14 +119,21 @@ class FlexClockController( } } } } override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) { override fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) { events.onColorPaletteChanged(resources) val theme = ThemeConfig(isDarkTheme, assets.seedColor) smallClock.animations.doze(dozeFraction) smallClock.run { largeClock.animations.doze(dozeFraction) events.onThemeChanged(theme) smallClock.animations.fold(foldFraction) animations.doze(dozeFraction) largeClock.animations.fold(foldFraction) animations.fold(foldFraction) smallClock.events.onTimeTick() events.onTimeTick() largeClock.events.onTimeTick() } largeClock.run { events.onThemeChanged(theme) animations.doze(dozeFraction) animations.fold(foldFraction) events.onTimeTick() } } } override fun dump(pw: PrintWriter) {} override fun dump(pw: PrintWriter) {} Loading Loading
packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AssetLoader.kt +1 −200 Original line number Original line Diff line number Diff line Loading @@ -17,34 +17,24 @@ package com.android.systemui.shared.clocks package com.android.systemui.shared.clocks import android.content.Context import android.content.Context import android.content.res.ColorStateList import android.content.res.Resources import android.content.res.Resources import android.graphics.Color import android.graphics.Typeface import android.graphics.Typeface import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable import android.util.TypedValue import android.util.TypedValue import com.android.internal.graphics.ColorUtils import com.android.internal.graphics.cam.Cam import com.android.internal.graphics.cam.CamUtils import com.android.internal.policy.SystemBarUtils import com.android.internal.policy.SystemBarUtils import com.android.systemui.log.core.Logger import com.android.systemui.log.core.Logger import com.android.systemui.log.core.MessageBuffer import com.android.systemui.log.core.MessageBuffer import com.android.systemui.monet.ColorScheme import com.android.systemui.monet.Style as MonetStyle import com.android.systemui.monet.Style as MonetStyle import com.android.systemui.monet.TonalPalette import java.io.IOException import java.io.IOException import kotlin.math.abs class AssetLoader class AssetLoader private constructor( private constructor( private val pluginCtx: Context, private val pluginCtx: Context, private val sysuiCtx: Context, private val sysuiCtx: Context, private val baseDir: String, private val baseDir: String, var colorScheme: ColorScheme?, var seedColor: Int?, var seedColor: Int?, var overrideChroma: Float?, var overrideChroma: Float?, val typefaceCache: TypefaceCache, val typefaceCache: TypefaceCache, val getThemeSeedColor: (Context) -> Int, messageBuffer: MessageBuffer, messageBuffer: MessageBuffer, ) { ) { val logger = Logger(messageBuffer, TAG) val logger = Logger(messageBuffer, TAG) Loading @@ -59,12 +49,10 @@ private constructor( sysuiCtx: Context, sysuiCtx: Context, baseDir: String, baseDir: String, messageBuffer: MessageBuffer, messageBuffer: MessageBuffer, getThemeSeedColor: ((Context) -> Int)? = null, ) : this( ) : this( pluginCtx, pluginCtx, sysuiCtx, sysuiCtx, baseDir, baseDir, colorScheme = null, seedColor = null, seedColor = null, overrideChroma = null, overrideChroma = null, typefaceCache = typefaceCache = Loading @@ -72,7 +60,6 @@ private constructor( // TODO(b/364680873): Move constant to config_clockFontFamily when shipping // TODO(b/364680873): Move constant to config_clockFontFamily when shipping return@TypefaceCache Typeface.create("google-sans-flex-clock", Typeface.NORMAL) return@TypefaceCache Typeface.create("google-sans-flex-clock", Typeface.NORMAL) }, }, getThemeSeedColor = getThemeSeedColor ?: Companion::getThemeSeedColor, messageBuffer = messageBuffer, messageBuffer = messageBuffer, ) ) Loading @@ -92,107 +79,6 @@ private constructor( return res.getString(id) return res.getString(id) } } fun tryReadColor(resStr: String): Int? = tryRead(resStr, ::readColor) fun readColor(resStr: String): Int { if (resStr.startsWith("#")) { return Color.parseColor(resStr) } val schemeColor = tryParseColorFromScheme(resStr) if (schemeColor != null) { logColor("ColorScheme: $resStr", schemeColor) return checkChroma(schemeColor) } val result = resolveColorResourceId(resStr) if (result == null) { throw IOException("Failed to parse color: $resStr") } val (res, colorId, targetTone) = result val color = res.getColor(colorId) if (targetTone == null || TonalPalette.SHADE_KEYS.contains(targetTone.toInt())) { logColor("Resources: $resStr", color) return checkChroma(color) } else { val interpolatedColor = ColorStateList.valueOf(color) .withLStar((1000f - targetTone) / 10f) .getDefaultColor() logColor("Resources (interpolated tone): $resStr", interpolatedColor) return checkChroma(interpolatedColor) } } private fun checkChroma(color: Int): Int { return overrideChroma?.let { val cam = Cam.fromInt(color) val tone = CamUtils.lstarFromInt(color) val result = ColorUtils.CAMToColor(cam.hue, it, tone) logColor("Chroma override", result) result } ?: color } private fun tryParseColorFromScheme(resStr: String): Int? { val colorScheme = this.colorScheme if (colorScheme == null) { logger.w("No color scheme available") return null } val (packageName, category, name) = parseResourceId(resStr) if (packageName != "android" || category != "color") { logger.w("Failed to parse package from $resStr") return null } var parts = name.split('_') if (parts.size != 3) { logger.w("Failed to find palette and shade from $name") return null } val (_, paletteKey, shadeKeyStr) = parts val palette = when (paletteKey) { "accent1" -> colorScheme.accent1 "accent2" -> colorScheme.accent2 "accent3" -> colorScheme.accent3 "neutral1" -> colorScheme.neutral1 "neutral2" -> colorScheme.neutral2 else -> return null } if (shadeKeyStr.contains("+") || shadeKeyStr.contains("-")) { val signIndex = shadeKeyStr.indexOfLast { it == '-' || it == '+' } // Use the tone of the seed color if it was set explicitly. var baseTone = if (seedColor != null) colorScheme.seedTone.toFloat() else shadeKeyStr.substring(0, signIndex).toFloatOrNull() val diff = shadeKeyStr.substring(signIndex).toFloatOrNull() if (baseTone == null) { logger.w("Failed to parse base tone from $shadeKeyStr") return null } if (diff == null) { logger.w("Failed to parse relative tone from $shadeKeyStr") return null } return palette.getAtTone(baseTone + diff) } else { val shadeKey = shadeKeyStr.toIntOrNull() if (shadeKey == null) { logger.w("Failed to parse tone from $shadeKeyStr") return null } return palette.allShadesMapped.get(shadeKey) ?: palette.getAtTone(shadeKey.toFloat()) } } fun readFontAsset(resStr: String): Typeface = typefaceCache.getTypeface(resStr) fun readFontAsset(resStr: String): Typeface = typefaceCache.getTypeface(resStr) fun tryReadTextAsset(path: String?): String? = tryRead(path, ::readTextAsset) fun tryReadTextAsset(path: String?): String? = tryRead(path, ::readTextAsset) Loading Loading @@ -250,52 +136,6 @@ private constructor( } } } } fun resolveColorResourceId(resStr: String): Triple<Resources, Int, Float?>? { var (packageName, category, name) = parseResourceId(resStr) // Convert relative tonal specifiers to standard val relIndex = name.indexOfLast { it == '_' } val isToneRelative = name.contains("-") || name.contains("+") val targetTone = if (packageName != "android") { null } else if (isToneRelative) { val signIndex = name.indexOfLast { it == '-' || it == '+' } val baseTone = name.substring(relIndex + 1, signIndex).toFloatOrNull() var diff = name.substring(signIndex).toFloatOrNull() if (baseTone == null || diff == null) { logger.w("Failed to parse relative tone from $name") return null } baseTone + diff } else { val absTone = name.substring(relIndex + 1).toFloatOrNull() if (absTone == null) { logger.w("Failed to parse absolute tone from $name") return null } absTone } if ( targetTone != null && (isToneRelative || !TonalPalette.SHADE_KEYS.contains(targetTone.toInt())) ) { val closeTone = TonalPalette.SHADE_KEYS.minBy { abs(it - targetTone) } val prevName = name name = name.substring(0, relIndex + 1) + closeTone logger.i("Converted $prevName to $name") } val result = resolveResourceId(packageName, category, name) if (result == null) { return null } val (res, resId) = result return Triple(res, resId, targetTone) } fun resolveResourceId(resStr: String): Pair<Resources, Int>? { fun resolveResourceId(resStr: String): Pair<Resources, Int>? { val (packageName, category, name) = parseResourceId(resStr) val (packageName, category, name) = parseResourceId(resStr) return resolveResourceId(packageName, category, name) return resolveResourceId(packageName, category, name) Loading Loading @@ -331,8 +171,7 @@ private constructor( try { try { if (path.startsWith("@")) { if (path.startsWith("@")) { val pair = resolveResourceId(path) val pair = resolveResourceId(path) val colorPair = resolveColorResourceId(path) return pair != null return pair != null || colorPair != null } else { } else { val stream = pluginCtx.resources.assets.open("$baseDir$path") val stream = pluginCtx.resources.assets.open("$baseDir$path") if (stream == null) { if (stream == null) { Loading @@ -352,37 +191,14 @@ private constructor( pluginCtx, pluginCtx, sysuiCtx, sysuiCtx, baseDir, baseDir, colorScheme, seedColor, seedColor, overrideChroma, overrideChroma, typefaceCache, typefaceCache, getThemeSeedColor, messageBuffer ?: logger.buffer, messageBuffer ?: logger.buffer, ) ) fun setSeedColor(seedColor: Int?, style: MonetStyle?) { fun setSeedColor(seedColor: Int?, style: MonetStyle?) { this.seedColor = seedColor this.seedColor = seedColor refreshColorPalette(style) } fun refreshColorPalette(style: MonetStyle?) { val seedColor = this.seedColor ?: getThemeSeedColor(sysuiCtx).also { logColor("Theme Seed Color", it) } this.colorScheme = ColorScheme( seedColor, false, // darkTheme is not used for palette generation style ?: MonetStyle.CLOCK, ) // Enforce low chroma on output colors if low chroma theme is selected this.overrideChroma = run { val cam = colorScheme?.seed?.let { Cam.fromInt(it) } if (cam != null && cam.chroma < LOW_CHROMA_LIMIT) { return@run cam.chroma * LOW_CHROMA_SCALE } return@run null } } } fun getClockPaddingStart(): Int { fun getClockPaddingStart(): Int { Loading Loading @@ -430,22 +246,7 @@ private constructor( throw Exception("Cannot find id of $name from $TAG") throw Exception("Cannot find id of $name from $TAG") } } private fun logColor(name: String, color: Int) { if (DEBUG_COLOR) { val cam = Cam.fromInt(color) val tone = CamUtils.lstarFromInt(color) logger.i("$name -> (hue: ${cam.hue}, chroma: ${cam.chroma}, tone: $tone)") } } companion object { companion object { private val DEBUG_COLOR = true private val LOW_CHROMA_LIMIT = 15 private val LOW_CHROMA_SCALE = 1.5f private val TAG = AssetLoader::class.simpleName!! private val TAG = AssetLoader::class.simpleName!! private fun getThemeSeedColor(ctx: Context): Int { return ctx.resources.getColor(android.R.color.system_palette_key_color_primary_light) } } } } }
packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt +10 −12 Original line number Original line Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceEvents import com.android.systemui.plugins.clocks.ClockFaceEvents import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.shared.clocks.view.FlexClockView import com.android.systemui.shared.clocks.view.FlexClockView Loading @@ -46,7 +47,6 @@ class ComposedDigitalLayerController( val layerControllers = mutableListOf<SimpleClockLayerController>() val layerControllers = mutableListOf<SimpleClockLayerController>() val dozeState = DefaultClockController.AnimationState(1F) val dozeState = DefaultClockController.AnimationState(1F) var isRegionDark = true override val view = FlexClockView(ctx, assets, messageBuffer) override val view = FlexClockView(ctx, assets, messageBuffer) Loading Loading @@ -103,10 +103,6 @@ class ComposedDigitalLayerController( view.onZenDataChanged(data) view.onZenDataChanged(data) } } override fun onColorPaletteChanged(resources: Resources) {} override fun onSeedColorChanged(seedColor: Int?) {} override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {} override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {} override var isReactiveTouchInteractionEnabled override var isReactiveTouchInteractionEnabled Loading @@ -116,10 +112,6 @@ class ComposedDigitalLayerController( } } } } override fun updateColors() { view.updateColors(assets, isRegionDark) } override val animations = override val animations = object : ClockAnimations { object : ClockAnimations { override fun enter() { override fun enter() { Loading Loading @@ -158,9 +150,15 @@ class ComposedDigitalLayerController( refreshTime() refreshTime() } } override fun onRegionDarknessChanged(isRegionDark: Boolean) { override fun onThemeChanged(theme: ThemeConfig) { this@ComposedDigitalLayerController.isRegionDark = isRegionDark val color = updateColors() when { theme.seedColor != null -> theme.seedColor!! theme.isDarkTheme -> resources.getColor(android.R.color.system_accent1_100) else -> resources.getColor(android.R.color.system_accent2_600) } view.updateColor(color) } } override fun onFontSettingChanged(fontSizePx: Float) { override fun onFontSettingChanged(fontSizePx: Float) { Loading
packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +32 −46 Original line number Original line Diff line number Diff line Loading @@ -37,6 +37,7 @@ import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ClockSettings import com.android.systemui.plugins.clocks.ClockSettings import com.android.systemui.plugins.clocks.DefaultClockFaceLayout import com.android.systemui.plugins.clocks.DefaultClockFaceLayout import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.plugins.clocks.ZenData import java.io.PrintWriter import java.io.PrintWriter Loading Loading @@ -100,28 +101,33 @@ class DefaultClockController( events.onLocaleChanged(Locale.getDefault()) events.onLocaleChanged(Locale.getDefault()) } } override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) { override fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) { largeClock.recomputePadding(null) largeClock.recomputePadding(null) largeClock.animations = LargeClockAnimations(largeClock.view, dozeFraction, foldFraction) largeClock.animations = LargeClockAnimations(largeClock.view, dozeFraction, foldFraction) smallClock.animations = DefaultClockAnimations(smallClock.view, dozeFraction, foldFraction) smallClock.animations = DefaultClockAnimations(smallClock.view, dozeFraction, foldFraction) events.onColorPaletteChanged(resources) val theme = ThemeConfig(isDarkTheme, settings?.seedColor) largeClock.events.onThemeChanged(theme) smallClock.events.onThemeChanged(theme) events.onTimeZoneChanged(TimeZone.getDefault()) events.onTimeZoneChanged(TimeZone.getDefault()) smallClock.events.onTimeTick() smallClock.events.onTimeTick() largeClock.events.onTimeTick() largeClock.events.onTimeTick() } } open inner class DefaultClockFaceController( open inner class DefaultClockFaceController( override val view: AnimatableClockView, override val view: AnimatableClockView, var seedColor: Int?, seedColor: Int?, messageBuffer: MessageBuffer?, messageBuffer: MessageBuffer?, ) : ClockFaceController { ) : ClockFaceController { // MAGENTA is a placeholder, and will be assigned correctly in initialize // MAGENTA is a placeholder, and will be assigned correctly in initialize private var currentColor = Color.MAGENTA private var currentColor = seedColor ?: Color.MAGENTA private var isRegionDark = false protected var targetRegion: Rect? = null protected var targetRegion: Rect? = null override val config = ClockFaceConfig() override val config = ClockFaceConfig() override var theme = ThemeConfig(true, seedColor) override val layout = override val layout = DefaultClockFaceLayout(view).apply { DefaultClockFaceLayout(view).apply { views[0].id = views[0].id = Loading @@ -132,9 +138,6 @@ class DefaultClockController( internal set internal set init { init { if (seedColor != null) { currentColor = seedColor!! } view.setColors(DOZE_COLOR, currentColor) view.setColors(DOZE_COLOR, currentColor) messageBuffer?.let { view.messageBuffer = it } messageBuffer?.let { view.messageBuffer = it } } } Loading @@ -143,9 +146,26 @@ class DefaultClockController( object : ClockFaceEvents { object : ClockFaceEvents { override fun onTimeTick() = view.refreshTime() override fun onTimeTick() = view.refreshTime() override fun onRegionDarknessChanged(isRegionDark: Boolean) { override fun onThemeChanged(theme: ThemeConfig) { this@DefaultClockFaceController.isRegionDark = isRegionDark this@DefaultClockFaceController.theme = theme updateColor() val color = when { theme.seedColor != null -> theme.seedColor!! theme.isDarkTheme -> resources.getColor(android.R.color.system_accent1_100) else -> resources.getColor(android.R.color.system_accent2_600) } if (currentColor == color) { return } currentColor = color view.setColors(DOZE_COLOR, color) if (!animations.dozeState.isActive) { view.animateColorChange() } } } override fun onTargetRegionChanged(targetRegion: Rect?) { override fun onTargetRegionChanged(targetRegion: Rect?) { Loading @@ -165,27 +185,6 @@ class DefaultClockController( } } open fun recomputePadding(targetRegion: Rect?) {} open fun recomputePadding(targetRegion: Rect?) {} fun updateColor() { val color = if (seedColor != null) { seedColor!! } else if (isRegionDark) { resources.getColor(android.R.color.system_accent1_100) } else { resources.getColor(android.R.color.system_accent2_600) } if (currentColor == color) { return } currentColor = color view.setColors(DOZE_COLOR, color) if (!animations.dozeState.isActive) { view.animateColorChange() } } } } inner class LargeClockFaceController( inner class LargeClockFaceController( Loading Loading @@ -248,19 +247,6 @@ class DefaultClockController( override fun onTimeZoneChanged(timeZone: TimeZone) = override fun onTimeZoneChanged(timeZone: TimeZone) = clocks.forEach { it.onTimeZoneChanged(timeZone) } clocks.forEach { it.onTimeZoneChanged(timeZone) } override fun onColorPaletteChanged(resources: Resources) { largeClock.updateColor() smallClock.updateColor() } override fun onSeedColorChanged(seedColor: Int?) { largeClock.seedColor = seedColor smallClock.seedColor = seedColor largeClock.updateColor() smallClock.updateColor() } override fun onLocaleChanged(locale: Locale) { override fun onLocaleChanged(locale: Locale) { val nf = NumberFormat.getInstance(locale) val nf = NumberFormat.getInstance(locale) if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) { if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) { Loading
packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +1 −0 Original line number Original line Diff line number Diff line Loading @@ -58,6 +58,7 @@ class DefaultClockProvider( val buffer = val buffer = messageBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.INFO) messageBuffers?.infraMessageBuffer ?: LogcatOnlyMessageBuffer(LogLevel.INFO) val assets = AssetLoader(ctx, ctx, "clocks/", buffer) val assets = AssetLoader(ctx, ctx, "clocks/", buffer) assets.setSeedColor(settings.seedColor, null) FlexClockController(ctx, resources, assets, FLEX_DESIGN, messageBuffers) FlexClockController(ctx, resources, assets, FLEX_DESIGN, messageBuffers) } else { } else { DefaultClockController( DefaultClockController( Loading
packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt +16 −26 Original line number Original line Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.systemui.plugins.clocks.ClockController import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.shared.clocks.view.FlexClockView import com.android.systemui.shared.clocks.view.FlexClockView Loading Loading @@ -97,24 +98,6 @@ class FlexClockController( largeClock.events.onLocaleChanged(locale) largeClock.events.onLocaleChanged(locale) } } override fun onColorPaletteChanged(resources: Resources) { assets.refreshColorPalette(design.colorPalette) smallClock.assets.refreshColorPalette(design.colorPalette) largeClock.assets.refreshColorPalette(design.colorPalette) smallClock.events.onColorPaletteChanged(resources) largeClock.events.onColorPaletteChanged(resources) } override fun onSeedColorChanged(seedColor: Int?) { assets.setSeedColor(seedColor, design.colorPalette) smallClock.assets.setSeedColor(seedColor, design.colorPalette) largeClock.assets.setSeedColor(seedColor, design.colorPalette) smallClock.events.onSeedColorChanged(seedColor) largeClock.events.onSeedColorChanged(seedColor) } override fun onWeatherDataChanged(data: WeatherData) { override fun onWeatherDataChanged(data: WeatherData) { smallClock.events.onWeatherDataChanged(data) smallClock.events.onWeatherDataChanged(data) largeClock.events.onWeatherDataChanged(data) largeClock.events.onWeatherDataChanged(data) Loading @@ -136,14 +119,21 @@ class FlexClockController( } } } } override fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float) { override fun initialize(isDarkTheme: Boolean, dozeFraction: Float, foldFraction: Float) { events.onColorPaletteChanged(resources) val theme = ThemeConfig(isDarkTheme, assets.seedColor) smallClock.animations.doze(dozeFraction) smallClock.run { largeClock.animations.doze(dozeFraction) events.onThemeChanged(theme) smallClock.animations.fold(foldFraction) animations.doze(dozeFraction) largeClock.animations.fold(foldFraction) animations.fold(foldFraction) smallClock.events.onTimeTick() events.onTimeTick() largeClock.events.onTimeTick() } largeClock.run { events.onThemeChanged(theme) animations.doze(dozeFraction) animations.fold(foldFraction) events.onTimeTick() } } } override fun dump(pw: PrintWriter) {} override fun dump(pw: PrintWriter) {} Loading