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 --> // KMK -->
fun colorTheme() = preferenceStore.getInt("pref_color_theme", 0xFFDF0090.toInt()) 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 themeCoverBased() = preferenceStore.getBoolean("pref_theme_cover_based_key", true)
fun themeCoverBasedStyle() = preferenceStore.getEnum("pref_theme_cover_based_style_key", PaletteStyle.Vibrant) 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), subtitle = stringResource(KMR.strings.custom_color_description),
onClick = { navigator.push(AppCustomThemeColorPickerScreen()) }, 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 <-- // KMK <--
Preference.PreferenceItem.SwitchPreference( Preference.PreferenceItem.SwitchPreference(
pref = amoledPref, pref = amoledPref,

View file

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

View file

@ -1,18 +1,21 @@
package eu.kanade.presentation.theme.colorscheme package eu.kanade.presentation.theme.colorscheme
import android.annotation.SuppressLint import android.app.UiModeManager
import androidx.compose.material3.ColorScheme import android.content.Context
import android.os.Build
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import com.google.android.material.color.utilities.Hct import androidx.core.content.getSystemService
import com.google.android.material.color.utilities.MaterialDynamicColors import com.materialkolor.Contrast
import com.google.android.material.color.utilities.SchemeContent import com.materialkolor.PaletteStyle
import eu.kanade.domain.ui.UiPreferences 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(context, seed, style)
private val custom = CustomCompatColorScheme(seed)
override val darkScheme override val darkScheme
get() = custom.darkScheme get() = custom.darkScheme
@ -21,60 +24,32 @@ internal class CustomColorScheme(uiPreferences: UiPreferences) : BaseColorScheme
get() = custom.lightScheme 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 lightScheme = dynamicColorScheme(
override val darkScheme = generateColorSchemeFromSeed(seed = seed, dark = true) seedColor = Color(seed),
isDark = false,
companion object { isAmoled = false,
private fun Int.toComposeColor(): Color = Color(this) style = style,
contrastLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
@SuppressLint("RestrictedApi") context.getSystemService<UiModeManager>()?.contrast?.toDouble() ?: Contrast.Default.value
private fun generateColorSchemeFromSeed(seed: Int, dark: Boolean): ColorScheme { } else {
val scheme = SchemeContent( Contrast.Default.value
Hct.fromInt(seed), },
dark, )
0.0, override val darkScheme = dynamicColorScheme(
) seedColor = Color(seed),
val dynamicColors = MaterialDynamicColors() isDark = true,
return ColorScheme( isAmoled = false,
primary = dynamicColors.primary().getArgb(scheme).toComposeColor(), style = style,
onPrimary = dynamicColors.onPrimary().getArgb(scheme).toComposeColor(), contrastLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
primaryContainer = dynamicColors.primaryContainer().getArgb(scheme).toComposeColor(), context.getSystemService<UiModeManager>()?.contrast?.toDouble() ?: Contrast.Default.value
onPrimaryContainer = dynamicColors.onPrimaryContainer().getArgb(scheme).toComposeColor(), } else {
inversePrimary = dynamicColors.inversePrimary().getArgb(scheme).toComposeColor(), Contrast.Default.value
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(),
)
}
}
} }

View file

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.reader
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.UiModeManager
import android.app.assist.AssistContent import android.app.assist.AssistContent
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager 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.elevation.SurfaceColors
import com.google.android.material.transition.platform.MaterialContainerTransform import com.google.android.material.transition.platform.MaterialContainerTransform
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import com.materialkolor.Contrast
import com.materialkolor.dynamicColorScheme import com.materialkolor.dynamicColorScheme
import dev.chrisbanes.insetter.applyInsetter import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.base.BasePreferences
@ -744,6 +746,11 @@ class ReaderActivity : BaseActivity() {
isDark = isNightMode(), isDark = isNightMode(),
isAmoled = themeDarkAmoled, isAmoled = themeDarkAmoled,
style = themeCoverBasedStyle, 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 <-- // KMK <--

View file

@ -17,9 +17,10 @@
<!-- Preferences --> <!-- Preferences -->
<!-- Appearance section --> <!-- Appearance section -->
<string name="pref_custom_theme_style">Custom theme style</string>
<string name="pref_details_page_theme">Details page</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">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_tonalspot">Tonal Spot</string>
<string name="pref_theme_cover_based_style_neutral">Neutral</string> <string name="pref_theme_cover_based_style_neutral">Neutral</string>
<string name="pref_theme_cover_based_style_vibrant">Vibrant</string> <string name="pref_theme_cover_based_style_vibrant">Vibrant</string>
@ -44,7 +45,7 @@
<!-- Custom Color Picker --> <!-- Custom Color Picker -->
<string name="pref_custom_color">Palette Personalizer</string> <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> <string name="theme_custom">Custom Theme</string>
<!-- Library section --> <!-- Library section -->