[优化] 优化了Compose的主题系统,现在可以处理背景图片、纯黑暗色模式等情况

This commit is contained in:
HapeLee
2026-01-10 03:01:21 +08:00
parent c4345d4945
commit e25dae61c2
14 changed files with 309 additions and 143 deletions
@@ -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,
@@ -67,19 +34,4 @@ fun AppTheme(
shapes = Shapes(), shapes = Shapes(),
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,
@@ -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
@@ -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
)
}
}
}