[优化] 为Compose界面实现了View系统的主题系统

This commit is contained in:
HapeLee
2025-12-29 02:26:21 +08:00
parent 925c7ad913
commit 8e56c49538
13 changed files with 1375 additions and 102 deletions
@@ -19,6 +19,8 @@ import io.legado.app.constant.Theme
import io.legado.app.help.config.AppConfig
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.ThemeState
import io.legado.app.utils.disableAutoFill
import io.legado.app.utils.fullScreen
import io.legado.app.utils.getPrefString
@@ -39,7 +41,6 @@ abstract class BaseComposeActivity(
protected abstract fun Content()
override fun onCreate(savedInstanceState: Bundle?) {
initTheme()
window.decorView.disableAutoFill()
AppContextWrapper.applyLocaleAndFont(this)
@@ -49,7 +50,7 @@ abstract class BaseComposeActivity(
// Compose 入口
setContent {
AppTheme {
Surface(color = MaterialTheme.colorScheme.background) {
Surface() {
Content()
}
}
@@ -84,64 +85,9 @@ abstract class BaseComposeActivity(
open fun observeLiveBus() {
observeEvent<String>(EventBus.RECREATE) {
ThemeState.updateThemeMode()
recreate()
}
}
open fun initTheme() {
when (getPrefString("app_theme", "0")) {
"0" -> {
DynamicColors.applyToActivitiesIfAvailable(application)
}
"1" -> setTheme(R.style.Theme_Base_GR)
"2" -> setTheme(R.style.Theme_Base_Lemon)
"3" -> setTheme(R.style.Theme_Base_WH)
"4" -> setTheme(R.style.Theme_Base_Elink)
"5" -> setTheme(R.style.Theme_Base_Sora)
"6" -> setTheme(R.style.Theme_Base_August)
"7" -> setTheme(R.style.Theme_Base_Carlotta)
"8" -> setTheme(R.style.Theme_Base_Koharu)
"9" -> setTheme(R.style.Theme_Base_Yuuka)
"10" -> setTheme(R.style.Theme_Base_Phoebe)
"11" -> setTheme(R.style.Theme_Base_Mujika)
"12" -> {
if (AppConfig.customMode == "accent")
setTheme(R.style.ThemeOverlay_WhiteBackground)
val colorImagePath = getPrefString(PreferKey.colorImage)
if (!colorImagePath.isNullOrBlank()) {
val file = File(colorImagePath)
if (file.exists()) {
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
if (bitmap != null) {
val colorAccuracy = true
val targetWidth = if (colorAccuracy) (bitmap.width / 4).coerceAtMost(256) else 16
val targetHeight = if (colorAccuracy) (bitmap.height / 4).coerceAtMost(256) else 16
val scaledBitmap = bitmap.scale(targetWidth, targetHeight, false)
val options = DynamicColorsOptions.Builder()
.setContentBasedSource(scaledBitmap)
.build()
DynamicColors.applyToActivitiesIfAvailable(application, options)
bitmap.recycle()
}
}
}else{
DynamicColors.applyToActivitiesIfAvailable(
application,
DynamicColorsOptions.Builder()
.setContentBasedSource(application.primaryColor)
.build()
)
}
}
"13" -> setTheme(R.style.AppTheme_Transparent)
}
if (AppConfig.pureBlack)
setTheme(R.style.ThemeOverlay_PureBlack)
}
}
@@ -1,39 +0,0 @@
package io.legado.app.base
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.foundation.isSystemInDarkTheme
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val context = LocalContext.current
val motionScheme = MotionScheme.expressive()
val colorScheme = when {
dynamicColor && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S ->
if (darkTheme) dynamicDarkColorScheme(context)
else dynamicLightColorScheme(context)
darkTheme -> darkColorScheme()
else -> lightColorScheme()
}
MaterialExpressiveTheme(
colorScheme = colorScheme,
typography = AppTypography,
motionScheme = motionScheme,
shapes = AppShapes,
content = content
)
}
// Material3 Typography
val AppTypography = Typography()
// Material3 Shapes
val AppShapes = Shapes()
@@ -12,6 +12,7 @@ import io.legado.app.ui.book.bookmark.AllBookmarkViewModel
import io.legado.app.ui.book.explore.ExploreShowViewModel
import io.legado.app.ui.book.readRecord.ReadRecordViewModel
import io.legado.app.ui.book.searchContent.SearchContentViewModel
import io.legado.app.ui.main.my.MyViewModel
import io.legado.app.ui.replace.ReplaceRuleViewModel
import io.legado.app.ui.replace.edit.ReplaceEditViewModel
import org.koin.android.ext.koin.androidApplication
@@ -49,4 +50,6 @@ val appModule = module {
single { SearchContentRepository() }
single { BookRepository() }
viewModel { SearchContentViewModel(get(), get()) }
viewModel { MyViewModel(get()) }
}
@@ -26,6 +26,7 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
val isCronet = appCtx.getPrefBoolean(PreferKey.cronet)
var useAntiAlias = appCtx.getPrefBoolean(PreferKey.antiAlias)
var userAgent: String = getPrefUserAgent()
var isEInkMode = appCtx.getPrefString("app_theme", "0") == "4"
var isTransparent = appCtx.getPrefString("app_theme", "0") == "13"
var customMode = appCtx.getPrefString(PreferKey.customMode)
@@ -51,6 +52,7 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
var mangaClickActionBR = appCtx.getPrefInt(PreferKey.mangaClickActionBR, 1)
var themeMode = appCtx.getPrefString(PreferKey.themeMode, "0")
var AppTheme = appCtx.getPrefString(PreferKey.appTheme, "0")
var useDefaultCover = appCtx.getPrefBoolean(PreferKey.useDefaultCover, false)
var optimizeRender = CanvasRecorderFactory.isSupport
&& appCtx.getPrefBoolean(PreferKey.optimizeRender, false)
@@ -59,6 +61,10 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
when (key) {
PreferKey.appTheme -> {
AppTheme = appCtx.getPrefString(PreferKey.appTheme, "0")
}
PreferKey.themeMode -> {
themeMode = appCtx.getPrefString(PreferKey.themeMode, "0")
}
@@ -4,7 +4,7 @@ import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import io.legado.app.base.AppTheme
import io.legado.app.ui.theme.AppTheme
import io.legado.app.base.BaseComposeActivity
import io.legado.app.ui.book.changecover.ChangeCoverDialog
@@ -3,7 +3,7 @@ package io.legado.app.ui.book.readRecord
import android.os.Bundle
import androidx.compose.runtime.*
import androidx.lifecycle.lifecycleScope
import io.legado.app.base.AppTheme
import io.legado.app.ui.theme.AppTheme
import io.legado.app.data.appDb
import io.legado.app.utils.startActivityForBook
import kotlinx.coroutines.Dispatchers
@@ -2,7 +2,7 @@ package io.legado.app.ui.book.searchContent
import android.os.Bundle
import androidx.compose.runtime.Composable
import io.legado.app.base.AppTheme
import io.legado.app.ui.theme.AppTheme
import io.legado.app.base.BaseComposeActivity
class SearchContentActivity : BaseComposeActivity() {
@@ -4,7 +4,7 @@ import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.Composable
import io.legado.app.base.AppTheme
import io.legado.app.ui.theme.AppTheme
import io.legado.app.base.BaseComposeActivity
class ReplaceRuleActivity : BaseComposeActivity() {
@@ -4,7 +4,7 @@ import android.content.Context
import android.content.Intent
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import io.legado.app.base.AppTheme
import io.legado.app.ui.theme.AppTheme
import io.legado.app.base.BaseComposeActivity
/**
@@ -0,0 +1,97 @@
package io.legado.app.ui.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialExpressiveTheme
import androidx.compose.material3.MotionScheme
import androidx.compose.material3.Shapes
import androidx.compose.material3.Typography
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import io.legado.app.help.config.AppConfig
import io.legado.app.ui.theme.AppColorSchemes.AugustDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.AugustLightColorScheme
import io.legado.app.ui.theme.AppColorSchemes.CarlottaDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.CarlottaLightColorScheme
import io.legado.app.ui.theme.AppColorSchemes.ElinkDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.ElinkLightColorScheme
import io.legado.app.ui.theme.AppColorSchemes.GRDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.GRLightColorScheme
import io.legado.app.ui.theme.AppColorSchemes.KoharuDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.KoharuLightColorScheme
import io.legado.app.ui.theme.AppColorSchemes.LemonDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.LemonLightColorScheme
import io.legado.app.ui.theme.AppColorSchemes.MujikaDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.MujikaLightColorScheme
import io.legado.app.ui.theme.AppColorSchemes.PhoebeDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.PhoebeLightColorScheme
import io.legado.app.ui.theme.AppColorSchemes.SoraDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.SoraLightColorScheme
import io.legado.app.ui.theme.AppColorSchemes.TransparentDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.TransparentLightColorScheme
import io.legado.app.ui.theme.AppColorSchemes.WHDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.WHLightColorScheme
import io.legado.app.ui.theme.AppColorSchemes.YuukaDarkColorScheme
import io.legado.app.ui.theme.AppColorSchemes.YuukaLightColorScheme
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val context = LocalContext.current
val motionScheme = MotionScheme.expressive()
val appThemeMode by ThemeState.themeMode.collectAsState()
var colorScheme = when (appThemeMode) {
AppThemeMode.Dynamic, AppThemeMode.ContentBased -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
} else {
if (darkTheme) darkColorScheme() else lightColorScheme()
}
}
AppThemeMode.GR -> if (darkTheme) GRDarkColorScheme else GRLightColorScheme
AppThemeMode.Lemon -> if (darkTheme) LemonDarkColorScheme else LemonLightColorScheme
AppThemeMode.WH -> if (darkTheme) WHDarkColorScheme else WHLightColorScheme
AppThemeMode.Elink -> if (darkTheme) ElinkDarkColorScheme else ElinkLightColorScheme
AppThemeMode.Sora -> if (darkTheme) SoraDarkColorScheme else SoraLightColorScheme
AppThemeMode.August -> if (darkTheme) AugustDarkColorScheme else AugustLightColorScheme
AppThemeMode.Carlotta -> if (darkTheme) CarlottaDarkColorScheme else CarlottaLightColorScheme
AppThemeMode.Koharu -> if (darkTheme) KoharuDarkColorScheme else KoharuLightColorScheme
AppThemeMode.Yuuka -> if (darkTheme) YuukaDarkColorScheme else YuukaLightColorScheme
AppThemeMode.Phoebe -> if (darkTheme) PhoebeDarkColorScheme else PhoebeLightColorScheme
AppThemeMode.Mujika -> if (darkTheme) MujikaDarkColorScheme else MujikaLightColorScheme
AppThemeMode.Transparent -> if (darkTheme) TransparentDarkColorScheme else TransparentLightColorScheme
}
if (darkTheme && AppConfig.pureBlack) {
colorScheme = colorScheme.copy(
background = Color.Black,
surface = Color.Black
)
}
MaterialExpressiveTheme(
colorScheme = colorScheme,
typography = AppTypography,
motionScheme = motionScheme,
shapes = AppShapes,
content = content
)
}
// Material3 Typography
val AppTypography = Typography()
// Material3 Shapes
val AppShapes = Shapes()
@@ -0,0 +1,36 @@
package io.legado.app.ui.theme
enum class AppThemeMode {
Dynamic,
GR,
Lemon,
WH,
Elink,
Sora,
August,
Carlotta,
Koharu,
Yuuka,
Phoebe,
Mujika,
ContentBased,
Transparent
}
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.ContentBased
"13" -> AppThemeMode.Transparent
else -> AppThemeMode.Dynamic
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,16 @@
package io.legado.app.ui.theme
import io.legado.app.help.config.AppConfig
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
object ThemeState {
private val _themeMode =
MutableStateFlow(resolveThemeMode(AppConfig.AppTheme.toString()))
val themeMode: StateFlow<AppThemeMode> = _themeMode
fun updateThemeMode() {
_themeMode.value = resolveThemeMode(AppConfig.AppTheme.toString())
}
}