Improve custom theme

* update string

* migrate to use DynamicMaterialTheme instead

* support various style for custom theme

* use BaseColorScheme with dynamicColorScheme instead, to avoid Activity being recreated

* apply theme right when changing theme style

* change default style to Fidelity

* adaptable to system contrast with Custom theme & Theme based cover
This commit is contained in:
Cuong-Tran 2024-09-23 19:41:34 +07:00 committed by GitHub
parent 39e5644f5f
commit b72d910cc9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 101 additions and 68 deletions

View file

@ -32,6 +32,8 @@ class UiPreferences(
// KMK -->
fun colorTheme() = preferenceStore.getInt("pref_color_theme", 0xFFDF0090.toInt())
fun customThemeStyle() = preferenceStore.getEnum("pref_custom_theme_style_key", PaletteStyle.Fidelity)
fun themeCoverBased() = preferenceStore.getBoolean("pref_theme_cover_based_key", true)
fun themeCoverBasedStyle() = preferenceStore.getEnum("pref_theme_cover_based_style_key", PaletteStyle.Vibrant)

View file

@ -106,6 +106,40 @@ object SettingsAppearanceScreen : SearchableSettings {
subtitle = stringResource(KMR.strings.custom_color_description),
onClick = { navigator.push(AppCustomThemeColorPickerScreen()) },
),
Preference.PreferenceItem.ListPreference(
pref = uiPreferences.customThemeStyle(),
title = stringResource(KMR.strings.pref_custom_theme_style),
enabled = appTheme == AppTheme.CUSTOM,
entries = PaletteStyle.entries
.associateWith {
when (it) {
PaletteStyle.TonalSpot ->
stringResource(KMR.strings.pref_theme_cover_based_style_tonalspot)
PaletteStyle.Neutral ->
stringResource(KMR.strings.pref_theme_cover_based_style_neutral)
PaletteStyle.Vibrant ->
stringResource(KMR.strings.pref_theme_cover_based_style_vibrant)
PaletteStyle.Expressive ->
stringResource(KMR.strings.pref_theme_cover_based_style_expressive)
PaletteStyle.Rainbow ->
stringResource(KMR.strings.pref_theme_cover_based_style_rainbow)
PaletteStyle.FruitSalad ->
stringResource(KMR.strings.pref_theme_cover_based_style_fruitsalad)
PaletteStyle.Monochrome ->
stringResource(KMR.strings.pref_theme_cover_based_style_monochrome)
PaletteStyle.Fidelity ->
stringResource(KMR.strings.pref_theme_cover_based_style_fidelity)
PaletteStyle.Content ->
stringResource(KMR.strings.pref_theme_cover_based_style_content)
else -> it.name
}
}
.toImmutableMap(),
onValueChanged = {
(context as? Activity)?.let { ActivityCompat.recreate(it) }
true
},
),
// KMK <--
Preference.PreferenceItem.SwitchPreference(
pref = amoledPref,

View file

@ -1,5 +1,7 @@
package eu.kanade.presentation.theme
import android.app.UiModeManager
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
@ -7,6 +9,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.getSystemService
import com.materialkolor.Contrast
import com.materialkolor.DynamicMaterialTheme
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.domain.ui.model.AppTheme
@ -58,6 +62,7 @@ fun TachiyomiTheme(
) {
val uiPreferences = Injekt.get<UiPreferences>()
// KMK -->
val context = LocalContext.current
val isAmoled = amoled ?: uiPreferences.themeDarkAmoled().get()
if (seedColor != null) {
DynamicMaterialTheme(
@ -67,6 +72,11 @@ fun TachiyomiTheme(
style = uiPreferences.themeCoverBasedStyle().get(),
animate = true,
content = content,
contrastLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
context.getSystemService<UiModeManager>()?.contrast?.toDouble() ?: Contrast.Default.value
} else {
Contrast.Default.value
},
)
} else {
// KMK <--
@ -110,7 +120,11 @@ private fun getThemeColorScheme(
}
// KMK -->
AppTheme.CUSTOM -> {
CustomColorScheme(uiPreferences)
CustomColorScheme(
context = LocalContext.current,
seed = uiPreferences.colorTheme().get(),
style = uiPreferences.customThemeStyle().get(),
)
}
// KMK <--
else -> {

View file

@ -1,18 +1,21 @@
package eu.kanade.presentation.theme.colorscheme
import android.annotation.SuppressLint
import androidx.compose.material3.ColorScheme
import android.app.UiModeManager
import android.content.Context
import android.os.Build
import androidx.compose.ui.graphics.Color
import com.google.android.material.color.utilities.Hct
import com.google.android.material.color.utilities.MaterialDynamicColors
import com.google.android.material.color.utilities.SchemeContent
import eu.kanade.domain.ui.UiPreferences
import androidx.core.content.getSystemService
import com.materialkolor.Contrast
import com.materialkolor.PaletteStyle
import com.materialkolor.dynamicColorScheme
internal class CustomColorScheme(uiPreferences: UiPreferences) : BaseColorScheme() {
internal class CustomColorScheme(
context: Context,
seed: Int,
style: PaletteStyle,
) : BaseColorScheme() {
private val seed = uiPreferences.colorTheme().get()
private val custom = CustomCompatColorScheme(seed)
private val custom = CustomCompatColorScheme(context, seed, style)
override val darkScheme
get() = custom.darkScheme
@ -21,60 +24,32 @@ internal class CustomColorScheme(uiPreferences: UiPreferences) : BaseColorScheme
get() = custom.lightScheme
}
private class CustomCompatColorScheme(seed: Int) : BaseColorScheme() {
private class CustomCompatColorScheme(
context: Context,
seed: Int,
style: PaletteStyle,
) : BaseColorScheme() {
override val lightScheme = generateColorSchemeFromSeed(seed = seed, dark = false)
override val darkScheme = generateColorSchemeFromSeed(seed = seed, dark = true)
companion object {
private fun Int.toComposeColor(): Color = Color(this)
@SuppressLint("RestrictedApi")
private fun generateColorSchemeFromSeed(seed: Int, dark: Boolean): ColorScheme {
val scheme = SchemeContent(
Hct.fromInt(seed),
dark,
0.0,
)
val dynamicColors = MaterialDynamicColors()
return ColorScheme(
primary = dynamicColors.primary().getArgb(scheme).toComposeColor(),
onPrimary = dynamicColors.onPrimary().getArgb(scheme).toComposeColor(),
primaryContainer = dynamicColors.primaryContainer().getArgb(scheme).toComposeColor(),
onPrimaryContainer = dynamicColors.onPrimaryContainer().getArgb(scheme).toComposeColor(),
inversePrimary = dynamicColors.inversePrimary().getArgb(scheme).toComposeColor(),
secondary = dynamicColors.secondary().getArgb(scheme).toComposeColor(),
onSecondary = dynamicColors.onSecondary().getArgb(scheme).toComposeColor(),
secondaryContainer = dynamicColors.secondaryContainer().getArgb(scheme).toComposeColor(),
onSecondaryContainer = dynamicColors.onSecondaryContainer().getArgb(scheme).toComposeColor(),
tertiary = dynamicColors.tertiary().getArgb(scheme).toComposeColor(),
onTertiary = dynamicColors.onTertiary().getArgb(scheme).toComposeColor(),
tertiaryContainer = dynamicColors.tertiary().getArgb(scheme).toComposeColor(),
onTertiaryContainer = dynamicColors.onTertiaryContainer().getArgb(scheme).toComposeColor(),
background = dynamicColors.background().getArgb(scheme).toComposeColor(),
onBackground = dynamicColors.onBackground().getArgb(scheme).toComposeColor(),
surface = dynamicColors.surface().getArgb(scheme).toComposeColor(),
onSurface = dynamicColors.onSurface().getArgb(scheme).toComposeColor(),
surfaceVariant = dynamicColors.surfaceVariant().getArgb(scheme).toComposeColor(),
onSurfaceVariant = dynamicColors.onSurfaceVariant().getArgb(scheme).toComposeColor(),
surfaceTint = dynamicColors.surfaceTint().getArgb(scheme).toComposeColor(),
inverseSurface = dynamicColors.inverseSurface().getArgb(scheme).toComposeColor(),
inverseOnSurface = dynamicColors.inverseOnSurface().getArgb(scheme).toComposeColor(),
error = dynamicColors.error().getArgb(scheme).toComposeColor(),
onError = dynamicColors.onError().getArgb(scheme).toComposeColor(),
errorContainer = dynamicColors.errorContainer().getArgb(scheme).toComposeColor(),
onErrorContainer = dynamicColors.onErrorContainer().getArgb(scheme).toComposeColor(),
outline = dynamicColors.outline().getArgb(scheme).toComposeColor(),
outlineVariant = dynamicColors.outlineVariant().getArgb(scheme).toComposeColor(),
scrim = Color.Black,
surfaceBright = dynamicColors.surfaceBright().getArgb(scheme).toComposeColor(),
surfaceDim = dynamicColors.surfaceDim().getArgb(scheme).toComposeColor(),
surfaceContainer = dynamicColors.surfaceContainer().getArgb(scheme).toComposeColor(),
surfaceContainerHigh = dynamicColors.surfaceContainerHigh().getArgb(scheme).toComposeColor(),
surfaceContainerHighest = dynamicColors.surfaceContainerHighest().getArgb(scheme).toComposeColor(),
surfaceContainerLow = dynamicColors.surfaceContainerLow().getArgb(scheme).toComposeColor(),
surfaceContainerLowest = dynamicColors.surfaceContainerLowest().getArgb(scheme).toComposeColor(),
)
}
}
override val lightScheme = dynamicColorScheme(
seedColor = Color(seed),
isDark = false,
isAmoled = false,
style = style,
contrastLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
context.getSystemService<UiModeManager>()?.contrast?.toDouble() ?: Contrast.Default.value
} else {
Contrast.Default.value
},
)
override val darkScheme = dynamicColorScheme(
seedColor = Color(seed),
isDark = true,
isAmoled = false,
style = style,
contrastLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
context.getSystemService<UiModeManager>()?.contrast?.toDouble() ?: Contrast.Default.value
} else {
Contrast.Default.value
},
)
}

View file

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.reader
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.UiModeManager
import android.app.assist.AssistContent
import android.content.ClipData
import android.content.ClipboardManager
@ -59,6 +60,7 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.elevation.SurfaceColors
import com.google.android.material.transition.platform.MaterialContainerTransform
import com.hippo.unifile.UniFile
import com.materialkolor.Contrast
import com.materialkolor.dynamicColorScheme
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.base.BasePreferences
@ -744,6 +746,11 @@ class ReaderActivity : BaseActivity() {
isDark = isNightMode(),
isAmoled = themeDarkAmoled,
style = themeCoverBasedStyle,
contrastLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
getSystemService<UiModeManager>()?.contrast?.toDouble() ?: Contrast.Default.value
} else {
Contrast.Default.value
},
)
}
// KMK <--

View file

@ -17,9 +17,10 @@
<!-- Preferences -->
<!-- Appearance section -->
<string name="pref_custom_theme_style">Custom theme style</string>
<string name="pref_details_page_theme">Details page</string>
<string name="pref_theme_cover_based">Theme based on cover</string>
<string name="pref_theme_cover_based_style">Theme style</string>
<string name="pref_theme_cover_based_style">Cover based theme style</string>
<string name="pref_theme_cover_based_style_tonalspot">Tonal Spot</string>
<string name="pref_theme_cover_based_style_neutral">Neutral</string>
<string name="pref_theme_cover_based_style_vibrant">Vibrant</string>
@ -44,7 +45,7 @@
<!-- Custom Color Picker -->
<string name="pref_custom_color">Palette Personalizer</string>
<string name="custom_color_description">Pick a color for the custom Theme</string>
<string name="custom_color_description">Pick a seed color for the custom Theme</string>
<string name="theme_custom">Custom Theme</string>
<!-- Library section -->