Add Monet color(dynamic color) support on API 34

This commit is contained in:
Wind He 2025-03-15 20:17:04 +08:00
parent 3c352fbf81
commit 636566e3a5
7 changed files with 137 additions and 65 deletions

View File

@ -166,9 +166,6 @@ class FcitxApplication : Application() {
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
ThemeManager.onSystemDarkModeChange(newConfig.isDarkMode())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ThemeManager.onSystemPlatteChange()
}
Locales.onLocaleChange(resources.configuration)
}

View File

@ -261,4 +261,80 @@ sealed class Theme : Parcelable {
)
}
}
@Parcelize
data class Monet(
override val name: String,
override val isDark: Boolean,
override val backgroundColor: Int,
override val barColor: Int,
override val keyboardColor: Int,
override val keyBackgroundColor: Int,
override val keyTextColor: Int,
override val candidateTextColor: Int,
override val candidateLabelColor: Int,
override val candidateCommentColor: Int,
override val altKeyBackgroundColor: Int,
override val altKeyTextColor: Int,
override val accentKeyBackgroundColor: Int,
override val accentKeyTextColor: Int,
override val keyPressHighlightColor: Int,
override val keyShadowColor: Int,
override val popupBackgroundColor: Int,
override val popupTextColor: Int,
override val spaceBarColor: Int,
override val dividerColor: Int,
override val clipboardEntryColor: Int,
override val genericActiveBackgroundColor: Int,
override val genericActiveForegroundColor: Int
) : Theme() {
constructor(
isDark: Boolean,
surfaceContainer: Int,
surfaceBright: Int,
onSurface: Int,
inversePrimary: Int,
onPrimaryContainer: Int,
secondaryContainer: Int,
onSecondaryContainer: Int,
) : this(
name = "Monet" + if (isDark) "Dark" else "Light",
isDark = isDark,
backgroundColor = surfaceContainer,
barColor = surfaceBright,
keyboardColor = surfaceContainer,
keyBackgroundColor = surfaceBright,
keyTextColor = onSurface,
candidateTextColor = onSurface,
candidateLabelColor = onSurface,
candidateCommentColor = onSurface,
altKeyBackgroundColor = secondaryContainer,
altKeyTextColor = onSecondaryContainer,
accentKeyBackgroundColor = inversePrimary,
accentKeyTextColor = onPrimaryContainer,
keyPressHighlightColor = inversePrimary,
keyShadowColor = 0x000000,
popupBackgroundColor = surfaceContainer,
popupTextColor = onSurface,
spaceBarColor = surfaceBright,
dividerColor = surfaceBright,
clipboardEntryColor = surfaceBright,
genericActiveBackgroundColor = inversePrimary,
genericActiveForegroundColor = onPrimaryContainer
)
}
}

View File

@ -1,43 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* SPDX-FileCopyrightText: Copyright 2025 Fcitx5 for Android Contributors
*/
package org.fcitx.fcitx5.android.data.theme
import android.os.Build
import androidx.annotation.RequiresApi
import org.fcitx.fcitx5.android.utils.appContext
class ThemeDynamic {
@RequiresApi(Build.VERSION_CODES.S)
val MaterialYou = Theme.Builtin(
name = "MaterialYou",
isDark = false,
backgroundColor = appContext.getColor(android.R.color.system_accent1_100),
barColor = 0xffe4e7e9,
keyboardColor = 0xffeceff1,
keyBackgroundColor = 0xfffbfbfc,
keyTextColor = 0xff37474f,
candidateTextColor = 0xff37474f,
candidateLabelColor = 0xff37474f,
candidateCommentColor = 0xff7a858a,
altKeyBackgroundColor = 0xffdfe2e4,
altKeyTextColor = 0xff7a858a,
accentKeyBackgroundColor = 0xff5cb5ab,
accentKeyTextColor = 0xffffffff,
keyPressHighlightColor = 0x1f000000,
keyShadowColor = 0xffc0c3c4,
popupBackgroundColor = 0xffd9dbdd,
popupTextColor = 0xff37474f,
spaceBarColor = 0xffc9ced1,
dividerColor = 0x1f000000,
clipboardEntryColor = 0xfffbfbfc,
genericActiveBackgroundColor = 0xff80cbc4,
genericActiveForegroundColor = 0xff37474f
)
}
object ThemeDynamicProvider {
var current = ThemeDynamic()
}

View File

@ -41,13 +41,7 @@ object ThemeManager {
fun getTheme(name: String) =
customThemes.find { it.name == name } ?: BuiltinThemes.find { it.name == name }
fun getAllThemes(): List<Theme> {
val themes = customThemes + BuiltinThemes
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
return listOf(ThemeDynamicProvider.current.MaterialYou) + themes
}
return themes
}
fun getAllThemes() = listOf(ThemeMonet.light, ThemeMonet.dark) + customThemes + BuiltinThemes
fun refreshThemes() {
customThemes.clear()
@ -156,13 +150,4 @@ object ThemeManager {
}
}
@RequiresApi(Build.VERSION_CODES.S)
fun onSystemPlatteChange() {
if (_activeTheme.name == "MaterialYou") {
println("We are using M3 and system platte change!")
ThemeDynamicProvider.current = ThemeDynamic()
_activeTheme = ThemeDynamicProvider.current.MaterialYou
}
}
}

View File

@ -0,0 +1,57 @@
package org.fcitx.fcitx5.android.data.theme
import android.os.Build
import androidx.annotation.RequiresApi
import org.fcitx.fcitx5.android.utils.appContext
// Ref:
// https://github.com/material-components/material-components-android/blob/master/docs/theming/Color.md
// https://github.com/material-components/material-components-android/tree/6f41625f5780d8d3e9a0261ee23b84f08b46dcd2/lib/java/com/google/android/material/color/res/color-v31
// https://www.figma.com/community/file/809865700885504168/material-3-android-15
// FIXME: SDK < 34 can only have approximate color values, maybe we can implement our own color algorithm.
// See: https://github.com/XayahSuSuSu/Android-DataBackup/blob/e8b087fb55519c659bebdc46c0217731fe80a0d7/source/core/ui/src/main/kotlin/com/xayah/core/ui/material3/DynamicTonalPalette.kt#L185
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
object ThemeMonet {
val light
get() = Theme.Monet(
isDark = false,
surfaceContainer = appContext.getColor(colorRolesLight.surfaceContainer),
surfaceBright = appContext.getColor(colorRolesLight.surfaceBright),
onSurface = appContext.getColor(colorRolesLight.onSurface),
inversePrimary = appContext.getColor(colorRolesLight.inversePrimary),
onPrimaryContainer = appContext.getColor(colorRolesLight.onPrimaryContainer),
secondaryContainer = appContext.getColor(colorRolesLight.secondaryContainer),
onSecondaryContainer = appContext.getColor(colorRolesLight.onSecondaryContainer)
)
val dark
get() = Theme.Monet(
isDark = true,
surfaceContainer = appContext.getColor(colorRolesDark.surfaceContainer),
surfaceBright = appContext.getColor(colorRolesDark.surfaceBright),
onSurface = appContext.getColor(colorRolesDark.onSurface),
inversePrimary = appContext.getColor(colorRolesDark.inversePrimary),
onPrimaryContainer = appContext.getColor(colorRolesDark.onPrimaryContainer),
secondaryContainer = appContext.getColor(colorRolesDark.secondaryContainer),
onSecondaryContainer = appContext.getColor(colorRolesDark.onSecondaryContainer)
)
private data object colorRolesLight {
const val surfaceContainer = android.R.color.system_surface_container_light
const val surfaceBright = android.R.color.system_surface_bright_light
const val onSurface = android.R.color.system_on_surface_light
const val inversePrimary = android.R.color.system_primary_dark
const val onPrimaryContainer = android.R.color.system_on_primary_container_light
const val secondaryContainer = android.R.color.system_secondary_container_light
const val onSecondaryContainer = android.R.color.system_on_secondary_container_light
}
private data object colorRolesDark {
const val surfaceContainer = android.R.color.system_surface_container_dark
const val surfaceBright = android.R.color.system_surface_bright_dark
const val onSurface = android.R.color.system_on_surface_dark
const val inversePrimary = android.R.color.system_primary_light
const val onPrimaryContainer = android.R.color.system_on_primary_container_dark
const val secondaryContainer = android.R.color.system_secondary_container_dark
const val onSecondaryContainer = android.R.color.system_on_secondary_container_dark
}
}

View File

@ -94,7 +94,7 @@ class NavigationBarManager {
window.setNavbarBackgroundColor(
when (theme) {
is Theme.Builtin -> if (keyBorder) theme.backgroundColor else theme.keyboardColor
is Theme.Custom -> theme.backgroundColor
is Theme.Custom, is Theme.Monet -> theme.backgroundColor
}
)
}

View File

@ -29,7 +29,7 @@ class PreeditComponent : UniqueComponent<PreeditComponent>(), Dependent, InputBr
val keyBorder = ThemeManager.prefs.keyBorder.getValue()
val bkgColor = when (theme) {
is Theme.Builtin -> if (keyBorder) theme.backgroundColor else theme.barColor
is Theme.Custom -> theme.backgroundColor
is Theme.Custom, is Theme.Monet -> theme.backgroundColor
}
PreeditUi(context, theme, setupTextView = {
backgroundColor = bkgColor