[优化] 优化了Compose的主题系统,现在可以处理背景图片、纯黑暗色模式等情况
This commit is contained in:
@@ -4,7 +4,6 @@ import android.annotation.SuppressLint
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
@@ -29,19 +28,20 @@ import io.legado.app.constant.PreferKey
|
|||||||
import io.legado.app.constant.Theme
|
import io.legado.app.constant.Theme
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.help.config.ThemeConfig
|
import io.legado.app.help.config.ThemeConfig
|
||||||
|
import io.legado.app.lib.theme.primaryColor
|
||||||
|
import io.legado.app.ui.theme.ThemeSyncer
|
||||||
import io.legado.app.utils.applyOpenTint
|
import io.legado.app.utils.applyOpenTint
|
||||||
import io.legado.app.utils.applyTint
|
import io.legado.app.utils.applyTint
|
||||||
import io.legado.app.utils.disableAutoFill
|
import io.legado.app.utils.disableAutoFill
|
||||||
|
import io.legado.app.utils.fullScreen
|
||||||
import io.legado.app.utils.getPrefString
|
import io.legado.app.utils.getPrefString
|
||||||
import io.legado.app.utils.hideSoftInput
|
import io.legado.app.utils.hideSoftInput
|
||||||
import io.legado.app.utils.observeEvent
|
import io.legado.app.utils.observeEvent
|
||||||
import io.legado.app.utils.toastOnUi
|
|
||||||
import io.legado.app.utils.windowSize
|
|
||||||
import io.legado.app.lib.theme.primaryColor
|
|
||||||
import io.legado.app.utils.fullScreen
|
|
||||||
import io.legado.app.utils.setNavigationBarColorAuto
|
import io.legado.app.utils.setNavigationBarColorAuto
|
||||||
import io.legado.app.utils.setStatusBarColorAuto
|
import io.legado.app.utils.setStatusBarColorAuto
|
||||||
import io.legado.app.utils.themeColor
|
import io.legado.app.utils.themeColor
|
||||||
|
import io.legado.app.utils.toastOnUi
|
||||||
|
import io.legado.app.utils.windowSize
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
@@ -245,6 +245,7 @@ abstract class BaseActivity<VB : ViewBinding>(
|
|||||||
|
|
||||||
open fun observeLiveBus() {
|
open fun observeLiveBus() {
|
||||||
observeEvent<String>(EventBus.RECREATE) {
|
observeEvent<String>(EventBus.RECREATE) {
|
||||||
|
ThemeSyncer.syncAll()
|
||||||
recreate()
|
recreate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,23 @@
|
|||||||
package io.legado.app.base
|
package io.legado.app.base
|
||||||
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
import androidx.core.graphics.scale
|
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import com.google.android.material.color.DynamicColors
|
|
||||||
import com.google.android.material.color.DynamicColorsOptions
|
|
||||||
import io.legado.app.R
|
|
||||||
import io.legado.app.constant.EventBus
|
import io.legado.app.constant.EventBus
|
||||||
import io.legado.app.constant.PreferKey
|
|
||||||
import io.legado.app.constant.Theme
|
import io.legado.app.constant.Theme
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.help.config.ThemeConfig
|
import io.legado.app.help.config.ThemeConfig
|
||||||
import io.legado.app.lib.theme.primaryColor
|
|
||||||
import io.legado.app.ui.theme.AppTheme
|
import io.legado.app.ui.theme.AppTheme
|
||||||
import io.legado.app.ui.theme.ThemeState
|
import io.legado.app.ui.theme.ThemeSyncer
|
||||||
import io.legado.app.utils.disableAutoFill
|
import io.legado.app.utils.disableAutoFill
|
||||||
import io.legado.app.utils.fullScreen
|
import io.legado.app.utils.fullScreen
|
||||||
import io.legado.app.utils.getPrefString
|
|
||||||
import io.legado.app.utils.observeEvent
|
import io.legado.app.utils.observeEvent
|
||||||
import io.legado.app.utils.setStatusBarColorAuto
|
import io.legado.app.utils.setStatusBarColorAuto
|
||||||
import io.legado.app.utils.themeColor
|
import io.legado.app.utils.themeColor
|
||||||
import io.legado.app.utils.windowSize
|
import io.legado.app.utils.windowSize
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
abstract class BaseComposeActivity(
|
abstract class BaseComposeActivity(
|
||||||
val fullScreen: Boolean = true,
|
val fullScreen: Boolean = true,
|
||||||
@@ -85,7 +74,7 @@ abstract class BaseComposeActivity(
|
|||||||
|
|
||||||
open fun observeLiveBus() {
|
open fun observeLiveBus() {
|
||||||
observeEvent<String>(EventBus.RECREATE) {
|
observeEvent<String>(EventBus.RECREATE) {
|
||||||
ThemeState.updateThemeMode()
|
ThemeSyncer.syncAll()
|
||||||
recreate()
|
recreate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -881,6 +881,15 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
|
|||||||
appCtx.getPrefBoolean(PreferKey.pureBlack, value)
|
appCtx.getPrefBoolean(PreferKey.pureBlack, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val hasLightBg: Boolean
|
||||||
|
get() = !appCtx.getPrefString(PreferKey.bgImage).isNullOrEmpty()
|
||||||
|
|
||||||
|
val hasDarkBg: Boolean
|
||||||
|
get() = !appCtx.getPrefString(PreferKey.bgImageN).isNullOrEmpty()
|
||||||
|
|
||||||
|
val hasImageBg: Boolean
|
||||||
|
get() = hasLightBg && hasDarkBg
|
||||||
|
|
||||||
var labelVisibilityMode
|
var labelVisibilityMode
|
||||||
get() = appCtx.getPrefString(PreferKey.labelVisibilityMode, "auto")
|
get() = appCtx.getPrefString(PreferKey.labelVisibilityMode, "auto")
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import io.legado.app.lib.prefs.ThemeCardPreference
|
|||||||
import io.legado.app.lib.prefs.ThemeModePreference
|
import io.legado.app.lib.prefs.ThemeModePreference
|
||||||
import io.legado.app.lib.theme.ThemeStore
|
import io.legado.app.lib.theme.ThemeStore
|
||||||
import io.legado.app.lib.theme.primaryColor
|
import io.legado.app.lib.theme.primaryColor
|
||||||
|
import io.legado.app.ui.theme.ThemeSyncer
|
||||||
import io.legado.app.ui.widget.number.NumberPickerDialog
|
import io.legado.app.ui.widget.number.NumberPickerDialog
|
||||||
import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
|
import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
|
||||||
import io.legado.app.utils.ColorUtils
|
import io.legado.app.utils.ColorUtils
|
||||||
@@ -180,6 +181,7 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
|
|||||||
PreferKey.customMode -> handleRestartRequired()
|
PreferKey.customMode -> handleRestartRequired()
|
||||||
|
|
||||||
PreferKey.pureBlack -> {
|
PreferKey.pureBlack -> {
|
||||||
|
ThemeSyncer.syncAll()
|
||||||
Handler(Looper.getMainLooper()).postDelayed({
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
recreateActivities()
|
recreateActivities()
|
||||||
}, 100)
|
}, 100)
|
||||||
|
|||||||
@@ -1,33 +1,14 @@
|
|||||||
package io.legado.app.ui.theme
|
package io.legado.app.ui.theme
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.MaterialExpressiveTheme
|
import androidx.compose.material3.MaterialExpressiveTheme
|
||||||
import androidx.compose.material3.MotionScheme
|
import androidx.compose.material3.MotionScheme
|
||||||
import androidx.compose.material3.Shapes
|
import androidx.compose.material3.Shapes
|
||||||
import androidx.compose.material3.Typography
|
import androidx.compose.material3.Typography
|
||||||
import androidx.compose.material3.dynamicDarkColorScheme
|
|
||||||
import androidx.compose.material3.dynamicLightColorScheme
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import com.materialkolor.PaletteStyle
|
|
||||||
import io.legado.app.help.config.AppConfig
|
|
||||||
import io.legado.app.lib.theme.primaryColor
|
|
||||||
import io.legado.app.ui.theme.colorScheme.AugustColorScheme
|
|
||||||
import io.legado.app.ui.theme.colorScheme.CarlottaColorScheme
|
|
||||||
import io.legado.app.ui.theme.colorScheme.ElinkColorScheme
|
|
||||||
import io.legado.app.ui.theme.colorScheme.GRColorScheme
|
|
||||||
import io.legado.app.ui.theme.colorScheme.KoharuColorScheme
|
|
||||||
import io.legado.app.ui.theme.colorScheme.LemonColorScheme
|
|
||||||
import io.legado.app.ui.theme.colorScheme.MujikaColorScheme
|
|
||||||
import io.legado.app.ui.theme.colorScheme.PhoebeColorScheme
|
|
||||||
import io.legado.app.ui.theme.colorScheme.SoraColorScheme
|
|
||||||
import io.legado.app.ui.theme.colorScheme.TransparentColorScheme
|
|
||||||
import io.legado.app.ui.theme.colorScheme.WHColorScheme
|
|
||||||
import io.legado.app.ui.theme.colorScheme.YuukaColorScheme
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -35,30 +16,16 @@ fun AppTheme(
|
|||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
val appThemeMode by ThemeState.themeMode.collectAsState()
|
val appThemeMode by ThemeState.themeMode.collectAsState()
|
||||||
val isAmoled = AppConfig.pureBlack
|
val isPureBlack by ThemeState.isPureBlack.collectAsState()
|
||||||
|
val hasImageBg by ThemeState.hasImageBg.collectAsState()
|
||||||
|
|
||||||
val colorScheme = when (appThemeMode) {
|
val colorScheme = ThemeManager.getColorScheme(
|
||||||
AppThemeMode.Dynamic -> {
|
mode = appThemeMode,
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
darkTheme = darkTheme,
|
||||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
isAmoled = isPureBlack,
|
||||||
} else {
|
isImageBg = hasImageBg
|
||||||
GRColorScheme.getColorScheme(darkTheme, isAmoled)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
AppThemeMode.CUSTOM -> {
|
|
||||||
CustomColorScheme(
|
|
||||||
context = context,
|
|
||||||
seed = context.primaryColor,
|
|
||||||
style = PaletteStyle.Vibrant,
|
|
||||||
).getColorScheme(darkTheme, isAmoled)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val scheme = colorSchemes.getOrDefault(appThemeMode, GRColorScheme)
|
|
||||||
scheme.getColorScheme(darkTheme, isAmoled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialExpressiveTheme(
|
MaterialExpressiveTheme(
|
||||||
colorScheme = colorScheme,
|
colorScheme = colorScheme,
|
||||||
@@ -68,18 +35,3 @@ fun AppTheme(
|
|||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val colorSchemes: Map<AppThemeMode, BaseColorScheme> = mapOf(
|
|
||||||
AppThemeMode.GR to GRColorScheme,
|
|
||||||
AppThemeMode.Lemon to LemonColorScheme,
|
|
||||||
AppThemeMode.WH to WHColorScheme,
|
|
||||||
AppThemeMode.Elink to ElinkColorScheme,
|
|
||||||
AppThemeMode.Sora to SoraColorScheme,
|
|
||||||
AppThemeMode.August to AugustColorScheme,
|
|
||||||
AppThemeMode.Carlotta to CarlottaColorScheme,
|
|
||||||
AppThemeMode.Koharu to KoharuColorScheme,
|
|
||||||
AppThemeMode.Yuuka to YuukaColorScheme,
|
|
||||||
AppThemeMode.Phoebe to PhoebeColorScheme,
|
|
||||||
AppThemeMode.Mujika to MujikaColorScheme,
|
|
||||||
AppThemeMode.Transparent to TransparentColorScheme,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package io.legado.app.ui.theme
|
|||||||
import androidx.compose.material3.ColorScheme
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.ReadOnlyComposable
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
|
|
||||||
abstract class BaseColorScheme {
|
abstract class BaseColorScheme {
|
||||||
abstract val lightScheme: ColorScheme
|
abstract val lightScheme: ColorScheme
|
||||||
@@ -11,17 +10,7 @@ abstract class BaseColorScheme {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ReadOnlyComposable
|
@ReadOnlyComposable
|
||||||
fun getColorScheme(
|
fun getColorScheme(darkTheme: Boolean): ColorScheme {
|
||||||
darkTheme: Boolean,
|
return if (darkTheme) darkScheme else lightScheme
|
||||||
isAmoled: Boolean,
|
|
||||||
): ColorScheme {
|
|
||||||
var scheme = if (darkTheme) darkScheme else lightScheme
|
|
||||||
if (darkTheme && isAmoled) {
|
|
||||||
scheme = scheme.copy(
|
|
||||||
background = Color.Black,
|
|
||||||
surface = Color.Black,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return scheme
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,51 +3,42 @@ package io.legado.app.ui.theme
|
|||||||
import android.app.UiModeManager
|
import android.app.UiModeManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import com.materialkolor.Contrast
|
import com.materialkolor.Contrast
|
||||||
import com.materialkolor.PaletteStyle
|
import com.materialkolor.PaletteStyle
|
||||||
import com.materialkolor.dynamicColorScheme
|
import com.materialkolor.dynamicColorScheme
|
||||||
|
|
||||||
internal class CustomColorScheme(
|
class CustomColorScheme(
|
||||||
context: Context,
|
context: Context,
|
||||||
seed: Int,
|
seed: Int,
|
||||||
style: PaletteStyle,
|
style: PaletteStyle,
|
||||||
) : BaseColorScheme() {
|
) : BaseColorScheme() {
|
||||||
private val custom = CustomCompatColorScheme(context, seed, style)
|
|
||||||
|
|
||||||
override val darkScheme
|
private val contrastLevel: Double =
|
||||||
get() = custom.darkScheme
|
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 lightScheme
|
override val lightScheme: ColorScheme = dynamicColorScheme(
|
||||||
get() = custom.lightScheme
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CustomCompatColorScheme(
|
|
||||||
context: Context,
|
|
||||||
seed: Int,
|
|
||||||
style: PaletteStyle,
|
|
||||||
) : BaseColorScheme() {
|
|
||||||
override val lightScheme = dynamicColorScheme(
|
|
||||||
seedColor = Color(seed),
|
seedColor = Color(seed),
|
||||||
isDark = false,
|
isDark = false,
|
||||||
isAmoled = false,
|
isAmoled = false,
|
||||||
style = style,
|
style = style,
|
||||||
contrastLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
contrastLevel = contrastLevel
|
||||||
context.getSystemService<UiModeManager>()?.contrast?.toDouble() ?: Contrast.Default.value
|
|
||||||
} else {
|
|
||||||
Contrast.Default.value
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
override val darkScheme = dynamicColorScheme(
|
|
||||||
|
override val darkScheme: ColorScheme = dynamicColorScheme(
|
||||||
seedColor = Color(seed),
|
seedColor = Color(seed),
|
||||||
isDark = true,
|
isDark = true,
|
||||||
isAmoled = false,
|
isAmoled = false,
|
||||||
style = style,
|
style = style,
|
||||||
contrastLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
contrastLevel = contrastLevel
|
||||||
context.getSystemService<UiModeManager>()?.contrast?.toDouble() ?: Contrast.Default.value
|
|
||||||
} else {
|
|
||||||
Contrast.Default.value
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package io.legado.app.ui.theme
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.material3.ColorScheme
|
||||||
|
import androidx.compose.material3.dynamicDarkColorScheme
|
||||||
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import com.materialkolor.PaletteStyle
|
||||||
|
import io.legado.app.lib.theme.primaryColor
|
||||||
|
import io.legado.app.ui.theme.colorScheme.AugustColorScheme
|
||||||
|
import io.legado.app.ui.theme.colorScheme.CarlottaColorScheme
|
||||||
|
import io.legado.app.ui.theme.colorScheme.ElinkColorScheme
|
||||||
|
import io.legado.app.ui.theme.colorScheme.GRColorScheme
|
||||||
|
import io.legado.app.ui.theme.colorScheme.KoharuColorScheme
|
||||||
|
import io.legado.app.ui.theme.colorScheme.LemonColorScheme
|
||||||
|
import io.legado.app.ui.theme.colorScheme.MujikaColorScheme
|
||||||
|
import io.legado.app.ui.theme.colorScheme.PhoebeColorScheme
|
||||||
|
import io.legado.app.ui.theme.colorScheme.SoraColorScheme
|
||||||
|
import io.legado.app.ui.theme.colorScheme.TransparentColorScheme
|
||||||
|
import io.legado.app.ui.theme.colorScheme.WHColorScheme
|
||||||
|
import io.legado.app.ui.theme.colorScheme.YuukaColorScheme
|
||||||
|
|
||||||
|
object ThemeManager {
|
||||||
|
|
||||||
|
val colorSchemes: Map<AppThemeMode, BaseColorScheme> = mapOf(
|
||||||
|
AppThemeMode.GR to GRColorScheme,
|
||||||
|
AppThemeMode.Lemon to LemonColorScheme,
|
||||||
|
AppThemeMode.WH to WHColorScheme,
|
||||||
|
AppThemeMode.Elink to ElinkColorScheme,
|
||||||
|
AppThemeMode.Sora to SoraColorScheme,
|
||||||
|
AppThemeMode.August to AugustColorScheme,
|
||||||
|
AppThemeMode.Carlotta to CarlottaColorScheme,
|
||||||
|
AppThemeMode.Koharu to KoharuColorScheme,
|
||||||
|
AppThemeMode.Yuuka to YuukaColorScheme,
|
||||||
|
AppThemeMode.Phoebe to PhoebeColorScheme,
|
||||||
|
AppThemeMode.Mujika to MujikaColorScheme,
|
||||||
|
AppThemeMode.Transparent to TransparentColorScheme,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun getColorScheme(
|
||||||
|
mode: AppThemeMode,
|
||||||
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
|
isAmoled: Boolean,
|
||||||
|
isImageBg: Boolean,
|
||||||
|
forceOpaque: Boolean = false
|
||||||
|
): ColorScheme {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
val actualMode = if (forceOpaque && mode == AppThemeMode.Transparent) {
|
||||||
|
AppThemeMode.WH
|
||||||
|
} else {
|
||||||
|
mode
|
||||||
|
}
|
||||||
|
|
||||||
|
var scheme = when (actualMode) {
|
||||||
|
AppThemeMode.Dynamic -> {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(
|
||||||
|
context
|
||||||
|
)
|
||||||
|
} else GRColorScheme.getColorScheme(darkTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
AppThemeMode.CUSTOM -> {
|
||||||
|
CustomColorScheme(context, context.primaryColor, PaletteStyle.Vibrant)
|
||||||
|
.getColorScheme(darkTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> (colorSchemes[actualMode] ?: GRColorScheme).getColorScheme(darkTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (darkTheme && isAmoled) {
|
||||||
|
scheme = scheme.copy(
|
||||||
|
surface = Color.Black,
|
||||||
|
background = Color.Black,
|
||||||
|
surfaceContainerLow = Color(0xFF0A0A0A),
|
||||||
|
surfaceContainer = Color(0xFF121212)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (!forceOpaque && (isImageBg || actualMode == AppThemeMode.Transparent)) {
|
||||||
|
scheme.copy(
|
||||||
|
surface = Color.Transparent,
|
||||||
|
background = Color.Transparent,
|
||||||
|
surfaceContainerLow = Color.Transparent,
|
||||||
|
surfaceContainer = Color.Transparent
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
scheme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package io.legado.app.ui.theme
|
||||||
|
|
||||||
|
object ThemeResolver {
|
||||||
|
|
||||||
|
fun resolveThemeMode(value: String): AppThemeMode = when (value) {
|
||||||
|
"0" -> AppThemeMode.Dynamic
|
||||||
|
"1" -> AppThemeMode.GR
|
||||||
|
"2" -> AppThemeMode.Lemon
|
||||||
|
"3" -> AppThemeMode.WH
|
||||||
|
"4" -> AppThemeMode.Elink
|
||||||
|
"5" -> AppThemeMode.Sora
|
||||||
|
"6" -> AppThemeMode.August
|
||||||
|
"7" -> AppThemeMode.Carlotta
|
||||||
|
"8" -> AppThemeMode.Koharu
|
||||||
|
"9" -> AppThemeMode.Yuuka
|
||||||
|
"10" -> AppThemeMode.Phoebe
|
||||||
|
"11" -> AppThemeMode.Mujika
|
||||||
|
"12" -> AppThemeMode.CUSTOM
|
||||||
|
"13" -> AppThemeMode.Transparent
|
||||||
|
else -> AppThemeMode.Dynamic
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,37 +1,42 @@
|
|||||||
package io.legado.app.ui.theme
|
package io.legado.app.ui.theme
|
||||||
|
|
||||||
|
import io.legado.app.constant.PreferKey
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
|
import io.legado.app.utils.getPrefBoolean
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import splitties.init.appCtx
|
||||||
|
|
||||||
object ThemeState {
|
object ThemeState {
|
||||||
|
|
||||||
private val _themeMode = MutableStateFlow(resolveThemeMode(AppConfig.AppTheme.toString()))
|
private val _themeMode =
|
||||||
|
MutableStateFlow(ThemeResolver.resolveThemeMode(AppConfig.AppTheme.toString()))
|
||||||
val themeMode: StateFlow<AppThemeMode> = _themeMode.asStateFlow()
|
val themeMode: StateFlow<AppThemeMode> = _themeMode.asStateFlow()
|
||||||
|
|
||||||
fun updateThemeMode() {
|
private val _isPureBlack =
|
||||||
val newMode = resolveThemeMode(AppConfig.AppTheme.toString())
|
MutableStateFlow(appCtx.getPrefBoolean(PreferKey.pureBlack, false))
|
||||||
|
val isPureBlack: StateFlow<Boolean> = _isPureBlack.asStateFlow()
|
||||||
|
|
||||||
|
private val _hasImageBg =
|
||||||
|
MutableStateFlow(AppConfig.hasImageBg)
|
||||||
|
val hasImageBg: StateFlow<Boolean> = _hasImageBg.asStateFlow()
|
||||||
|
|
||||||
|
fun updateThemeMode(newMode: AppThemeMode) {
|
||||||
if (_themeMode.value != newMode) {
|
if (_themeMode.value != newMode) {
|
||||||
_themeMode.value = newMode
|
_themeMode.value = newMode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun resolveThemeMode(value: String): AppThemeMode = when (value) {
|
fun updatePureBlack(enabled: Boolean) {
|
||||||
"0" -> AppThemeMode.Dynamic
|
if (_isPureBlack.value != enabled) {
|
||||||
"1" -> AppThemeMode.GR
|
_isPureBlack.value = enabled
|
||||||
"2" -> AppThemeMode.Lemon
|
}
|
||||||
"3" -> AppThemeMode.WH
|
}
|
||||||
"4" -> AppThemeMode.Elink
|
|
||||||
"5" -> AppThemeMode.Sora
|
fun updateImageBg(enabled: Boolean) {
|
||||||
"6" -> AppThemeMode.August
|
if (_hasImageBg.value != enabled) {
|
||||||
"7" -> AppThemeMode.Carlotta
|
_hasImageBg.value = enabled
|
||||||
"8" -> AppThemeMode.Koharu
|
}
|
||||||
"9" -> AppThemeMode.Yuuka
|
}
|
||||||
"10" -> AppThemeMode.Phoebe
|
|
||||||
"11" -> AppThemeMode.Mujika
|
|
||||||
"12" -> AppThemeMode.CUSTOM
|
|
||||||
"13" -> AppThemeMode.Transparent
|
|
||||||
else -> AppThemeMode.Dynamic
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package io.legado.app.ui.theme
|
||||||
|
|
||||||
|
import io.legado.app.constant.PreferKey
|
||||||
|
import io.legado.app.help.config.AppConfig
|
||||||
|
import io.legado.app.utils.getPrefBoolean
|
||||||
|
import splitties.init.appCtx
|
||||||
|
|
||||||
|
object ThemeSyncer {
|
||||||
|
|
||||||
|
fun syncAll() {
|
||||||
|
syncThemeMode()
|
||||||
|
syncPureBlack()
|
||||||
|
syncImageBg()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun syncThemeMode() {
|
||||||
|
ThemeState.updateThemeMode(
|
||||||
|
ThemeResolver.resolveThemeMode(AppConfig.AppTheme.toString())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun syncPureBlack() {
|
||||||
|
ThemeState.updatePureBlack(appCtx.getPrefBoolean(PreferKey.pureBlack, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun syncImageBg() {
|
||||||
|
ThemeState.updateImageBg(AppConfig.hasImageBg)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package io.legado.app.ui.widget.components
|
package io.legado.app.ui.widget.components
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@@ -12,10 +11,10 @@ import androidx.compose.material3.CardDefaults
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
@@ -73,7 +72,7 @@ fun TextCard(
|
|||||||
Spacer(modifier = Modifier.width(spacing))
|
Spacer(modifier = Modifier.width(spacing))
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
AnimatedText(
|
||||||
text = text,
|
text = text,
|
||||||
style = textStyle,
|
style = textStyle,
|
||||||
color = contentColor,
|
color = contentColor,
|
||||||
|
|||||||
+20
-5
@@ -1,20 +1,35 @@
|
|||||||
package io.legado.app.ui.widget.components.exportComponents
|
package io.legado.app.ui.widget.components.exportComponents
|
||||||
|
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.InsertDriveFile
|
import androidx.compose.material.icons.automirrored.filled.InsertDriveFile
|
||||||
import androidx.compose.material.icons.filled.CloudUpload
|
import androidx.compose.material.icons.filled.CloudUpload
|
||||||
import androidx.compose.material.icons.filled.FolderOpen
|
import androidx.compose.material.icons.filled.FolderOpen
|
||||||
import androidx.compose.material.icons.filled.SaveAlt
|
import androidx.compose.material.icons.filled.SaveAlt
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.SheetState
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
|
import io.legado.app.ui.widget.components.modalBottomSheet.GlobalModalBottomSheet
|
||||||
|
|
||||||
enum class FilePickerSheetMode {
|
enum class FilePickerSheetMode {
|
||||||
DIR, FILE, EXPORT
|
DIR, FILE, EXPORT
|
||||||
@@ -31,10 +46,10 @@ fun FilePickerSheet(
|
|||||||
onUpload: (() -> Unit)? = null,
|
onUpload: (() -> Unit)? = null,
|
||||||
allowExtensions: Array<String>? = null,
|
allowExtensions: Array<String>? = null,
|
||||||
) {
|
) {
|
||||||
ModalBottomSheet(
|
GlobalModalBottomSheet(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
sheetState = sheetState,
|
sheetState = sheetState,
|
||||||
containerColor = MaterialTheme.colorScheme.surface,
|
containerColor = { it.surface }
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
package io.legado.app.ui.widget.components.modalBottomSheet
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.material3.ColorScheme
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
|
import androidx.compose.material3.MaterialExpressiveTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.MotionScheme
|
||||||
|
import androidx.compose.material3.Shapes
|
||||||
|
import androidx.compose.material3.SheetState
|
||||||
|
import androidx.compose.material3.Typography
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import io.legado.app.ui.theme.ThemeManager
|
||||||
|
import io.legado.app.ui.theme.ThemeState
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
@Composable
|
||||||
|
fun GlobalModalBottomSheet(
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
sheetState: SheetState = rememberModalBottomSheetState(),
|
||||||
|
containerColor: (ColorScheme) -> Color = { it.surfaceContainerLow },
|
||||||
|
content: @Composable ColumnScope.() -> Unit
|
||||||
|
) {
|
||||||
|
val themeMode by ThemeState.themeMode.collectAsState()
|
||||||
|
val isPureBlack by ThemeState.isPureBlack.collectAsState()
|
||||||
|
val hasImageBg by ThemeState.hasImageBg.collectAsState()
|
||||||
|
|
||||||
|
val colorScheme = ThemeManager.getColorScheme(
|
||||||
|
mode = themeMode,
|
||||||
|
isAmoled = isPureBlack,
|
||||||
|
isImageBg = hasImageBg,
|
||||||
|
forceOpaque = true
|
||||||
|
)
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
sheetState = sheetState,
|
||||||
|
containerColor = containerColor(colorScheme),
|
||||||
|
contentColor = colorScheme.onSurface
|
||||||
|
) {
|
||||||
|
MaterialExpressiveTheme(
|
||||||
|
colorScheme = colorScheme,
|
||||||
|
typography = Typography(),
|
||||||
|
motionScheme = MotionScheme.expressive(),
|
||||||
|
shapes = Shapes()
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.animateContentSize()
|
||||||
|
.then(modifier),
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user