[优化] 一些主题优化

This commit is contained in:
HapeLee committed 2026-02-26 01:31:02 +08:00
1 parent fb020356b6
commit 0250001a27
45 files changed
+361 -327

No files matched your search

+3 -4
View File
@@ -10,7 +10,6 @@ import android.content.res.Configuration
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.os.Build import android.os.Build
import androidx.core.graphics.scale import androidx.core.graphics.scale
import coil.ImageLoader
import com.github.liuyueyi.quick.transfer.constants.TransType import com.github.liuyueyi.quick.transfer.constants.TransType
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import com.google.android.material.color.DynamicColorsOptions import com.google.android.material.color.DynamicColorsOptions
@@ -44,9 +43,9 @@ import io.legado.app.help.LifecycleHelp
import io.legado.app.help.RuleBigDataHelp import io.legado.app.help.RuleBigDataHelp
import io.legado.app.help.book.BookHelp import io.legado.app.help.book.BookHelp
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.OldThemeConfig.applyDayNightInit
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.config.ThemeConfig.applyDayNightInit
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.http.Cronet import io.legado.app.help.http.Cronet
import io.legado.app.help.http.ObsoleteUrlFactory import io.legado.app.help.http.ObsoleteUrlFactory
@@ -155,7 +154,7 @@ class App : Application() {
BookHelp.clearInvalidCache() BookHelp.clearInvalidCache()
Backup.clearCache() Backup.clearCache()
ReadBookConfig.clearBgAndCache() ReadBookConfig.clearBgAndCache()
ThemeConfig.clearBg() OldThemeConfig.clearBg()
//初始化简繁转换引擎 //初始化简繁转换引擎
when (AppConfig.chineseConverterType) { when (AppConfig.chineseConverterType) {
1 -> { 1 -> {
@@ -27,9 +27,8 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey 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.OldThemeConfig
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.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
@@ -232,7 +231,7 @@ abstract class BaseActivity<VB : ViewBinding>(
open fun upBackgroundImage() { open fun upBackgroundImage() {
if (imageBg) { if (imageBg) {
try { try {
ThemeConfig.getBgImage(this, windowManager.windowSize)?.let { OldThemeConfig.getBgImage(this, windowManager.windowSize)?.let {
window.decorView.background = it.toDrawable(resources) window.decorView.background = it.toDrawable(resources)
} }
} catch (e: OutOfMemoryError) { } catch (e: OutOfMemoryError) {
@@ -245,7 +244,6 @@ abstract class BaseActivity<VB : ViewBinding>(
open fun observeLiveBus() { open fun observeLiveBus() {
observeEvent<String>(EventBus.RECREATE) { observeEvent<String>(EventBus.RECREATE) {
ThemeSyncer.syncAll()
recreate() recreate()
} }
} }
@@ -9,9 +9,8 @@ import androidx.core.graphics.drawable.toDrawable
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.Theme import io.legado.app.constant.Theme
import io.legado.app.help.config.ThemeConfig import io.legado.app.help.config.OldThemeConfig
import io.legado.app.ui.theme.AppTheme import io.legado.app.ui.theme.AppTheme
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.observeEvent import io.legado.app.utils.observeEvent
@@ -66,7 +65,7 @@ abstract class BaseComposeActivity(
open fun upBackgroundImage() { open fun upBackgroundImage() {
try { try {
ThemeConfig.getBgImage(this, windowManager.windowSize)?.let { OldThemeConfig.getBgImage(this, windowManager.windowSize)?.let {
window.setBackgroundDrawable(it.toDrawable(resources)) window.setBackgroundDrawable(it.toDrawable(resources))
} }
} catch (_: Exception) {} } catch (_: Exception) {}
@@ -74,7 +73,6 @@ abstract class BaseComposeActivity(
open fun observeLiveBus() { open fun observeLiveBus() {
observeEvent<String>(EventBus.RECREATE) { observeEvent<String>(EventBus.RECREATE) {
ThemeSyncer.syncAll()
recreate() recreate()
} }
} }
@@ -181,6 +181,7 @@ object PreferKey {
const val composeEngine = "composeEngine" const val composeEngine = "composeEngine"
const val containerOpacity = "containerOpacity" const val containerOpacity = "containerOpacity"
const val enableBlur = "enableBlur" const val enableBlur = "enableBlur"
const val enableProgressiveBlur = "enableProgressiveBlur"
const val cBackground = "colorBackground" const val cBackground = "colorBackground"
const val cBBackground = "colorBottomBackground" const val cBBackground = "colorBottomBackground"
const val bgImage = "backgroundImage" const val bgImage = "backgroundImage"
@@ -8,8 +8,8 @@ import io.legado.app.data.entities.KeyboardAssist
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.data.entities.TxtTocRule import io.legado.app.data.entities.TxtTocRule
import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.LocalConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.BookCover import io.legado.app.model.BookCover
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
@@ -70,12 +70,12 @@ object DefaultData {
GSON.fromJsonArray<TxtTocRule>(json).getOrNull() ?: emptyList() GSON.fromJsonArray<TxtTocRule>(json).getOrNull() ?: emptyList()
} }
val themeConfigs: List<ThemeConfig.Config> by lazy { val themeConfigs: List<OldThemeConfig.Config> by lazy {
val json = String( val json = String(
appCtx.assets.open("defaultData${File.separator}${ThemeConfig.configFileName}") appCtx.assets.open("defaultData${File.separator}${OldThemeConfig.configFileName}")
.readBytes() .readBytes()
) )
GSON.fromJsonArray<ThemeConfig.Config>(json).getOrNull() ?: emptyList() GSON.fromJsonArray<OldThemeConfig.Config>(json).getOrNull() ?: emptyList()
} }
val rssSources: List<RssSource> by lazy { val rssSources: List<RssSource> by lazy {
@@ -15,8 +15,8 @@ import io.legado.app.constant.AppPattern
import io.legado.app.data.entities.BaseSource import io.legado.app.data.entities.BaseSource
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.http.BackstageWebView import io.legado.app.help.http.BackstageWebView
import io.legado.app.help.http.CookieManager.cookieJarHeader import io.legado.app.help.http.CookieManager.cookieJarHeader
import io.legado.app.help.http.CookieStore import io.legado.app.help.http.CookieStore
@@ -1203,12 +1203,12 @@ interface JsExtensions : JsEncodeUtils {
*/ */
@JavascriptInterface @JavascriptInterface
fun getThemeConfig(): String { fun getThemeConfig(): String {
val themeConfig = ThemeConfig.getDurConfig(appCtx) val themeConfig = OldThemeConfig.getDurConfig(appCtx)
return GSON.toJson(themeConfig) return GSON.toJson(themeConfig)
} }
fun getThemeConfigMap(): Map<String, Any?> { fun getThemeConfigMap(): Map<String, Any?> {
return ThemeConfig.getDurConfig(appCtx).toMap() return OldThemeConfig.getDurConfig(appCtx).toMap()
} }
} }
@@ -32,7 +32,7 @@ import splitties.init.appCtx
import java.io.File import java.io.File
@Keep @Keep
object ThemeConfig { object OldThemeConfig {
const val configFileName = "themeConfig.json" const val configFileName = "themeConfig.json"
val configFilePath = FileUtils.getPath(appCtx.filesDir, configFileName) val configFilePath = FileUtils.getPath(appCtx.filesDir, configFileName)
@@ -13,8 +13,8 @@ import io.legado.app.help.AppWebDav
import io.legado.app.help.DirectLinkUpload import io.legado.app.help.DirectLinkUpload
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.LocalConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.BookCover import io.legado.app.model.BookCover
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils
@@ -82,7 +82,7 @@ object Backup {
DirectLinkUpload.ruleFileName, DirectLinkUpload.ruleFileName,
ReadBookConfig.configFileName, ReadBookConfig.configFileName,
ReadBookConfig.shareConfigFileName, ReadBookConfig.shareConfigFileName,
ThemeConfig.configFileName, OldThemeConfig.configFileName,
BookCover.configFileName, BookCover.configFileName,
"config.xml" "config.xml"
) )
@@ -169,8 +169,8 @@ object Backup {
FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.shareConfigFileName) FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.shareConfigFileName)
.writeText(it) .writeText(it)
} }
GSON.toJson(ThemeConfig.configList).let { GSON.toJson(OldThemeConfig.configList).let {
FileUtils.createFileIfNotExist(backupPath + File.separator + ThemeConfig.configFileName) FileUtils.createFileIfNotExist(backupPath + File.separator + OldThemeConfig.configFileName)
.writeText(it) .writeText(it)
} }
DirectLinkUpload.getConfig()?.let { DirectLinkUpload.getConfig()?.let {
@@ -33,8 +33,8 @@ import io.legado.app.help.LauncherIconHelp
import io.legado.app.help.book.isLocal import io.legado.app.help.book.isLocal
import io.legado.app.help.book.upType import io.legado.app.help.book.upType
import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.LocalConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.model.BookCover import io.legado.app.model.BookCover
import io.legado.app.model.localBook.LocalBook import io.legado.app.model.localBook.LocalBook
import io.legado.app.utils.ACache import io.legado.app.utils.ACache
@@ -216,12 +216,12 @@ object Restore {
AppLog.put("恢复直链上传出错\n${it.localizedMessage}", it) AppLog.put("恢复直链上传出错\n${it.localizedMessage}", it)
} }
//恢复主题配置 //恢复主题配置
File(path, ThemeConfig.configFileName).takeIf { File(path, OldThemeConfig.configFileName).takeIf {
it.exists() it.exists()
}?.runCatching { }?.runCatching {
FileUtils.delete(ThemeConfig.configFilePath) FileUtils.delete(OldThemeConfig.configFilePath)
copyTo(File(ThemeConfig.configFilePath)) copyTo(File(OldThemeConfig.configFilePath))
ThemeConfig.upConfig() OldThemeConfig.upConfig()
}?.onFailure { }?.onFailure {
AppLog.put("恢复主题出错\n${it.localizedMessage}", it) AppLog.put("恢复主题出错\n${it.localizedMessage}", it)
} }
@@ -301,7 +301,7 @@ object Restore {
if (!BuildConfig.DEBUG) { if (!BuildConfig.DEBUG) {
LauncherIconHelp.changeIcon(appCtx.getPrefString(PreferKey.launcherIcon)) LauncherIconHelp.changeIcon(appCtx.getPrefString(PreferKey.launcherIcon))
} }
ThemeConfig.applyDayNight(appCtx) OldThemeConfig.applyDayNight(appCtx)
} }
} }
@@ -21,7 +21,7 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.ui.theme.ThemeSyncer import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.utils.getPrefString import io.legado.app.utils.getPrefString
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
import io.legado.app.utils.restart import io.legado.app.utils.restart
@@ -91,13 +91,13 @@ class ThemeCardPreference(context: Context, attrs: AttributeSet) : Preference(co
return@setOnClickListener return@setOnClickListener
} else { } else {
AppConfig.containerOpacity = 0 AppConfig.containerOpacity = 0
ThemeSyncer.syncContainerOpacity()
} }
} }
val oldValue = currentValue val oldValue = currentValue
currentValue = value currentValue = value
persistString(value) persistString(value)
callChangeListener(value) callChangeListener(value)
ThemeConfig.appTheme = value
notifyDataSetChanged() notifyDataSetChanged()
val isDynamicSwitch = (oldValue == "12" || value == "12") val isDynamicSwitch = (oldValue == "12" || value == "12")
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
@@ -7,7 +7,7 @@ import android.util.AttributeSet
import androidx.preference.PreferenceViewHolder import androidx.preference.PreferenceViewHolder
import com.google.android.material.button.MaterialButtonToggleGroup import com.google.android.material.button.MaterialButtonToggleGroup
import io.legado.app.R import io.legado.app.R
import io.legado.app.help.config.ThemeConfig import io.legado.app.help.config.OldThemeConfig
class ThemeModePreference(context: Context, attrs: AttributeSet) : Preference(context, attrs) { class ThemeModePreference(context: Context, attrs: AttributeSet) : Preference(context, attrs) {
@@ -59,7 +59,7 @@ class ThemeModePreference(context: Context, attrs: AttributeSet) : Preference(co
persistString(newValue) persistString(newValue)
callChangeListener(newValue) callChangeListener(newValue)
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
ThemeConfig.applyDayNight(context) OldThemeConfig.applyDayNight(context)
}, 300) }, 300)
} }
} }
@@ -35,7 +35,7 @@ import io.legado.app.R
import io.legado.app.constant.AppConst.appInfo import io.legado.app.constant.AppConst.appInfo
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SplicedColumnGroup import io.legado.app.ui.widget.components.SplicedColumnGroup
import io.legado.app.ui.widget.components.button.SmallTopBarButton import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.settingItem.SettingItem import io.legado.app.ui.widget.components.settingItem.SettingItem
@@ -61,7 +61,7 @@ fun AboutScreen(
GlassMediumFlexibleTopAppBar( GlassMediumFlexibleTopAppBar(
title = { Text(stringResource(R.string.about)) }, title = { Text(stringResource(R.string.about)) },
navigationIcon = { navigationIcon = {
SmallTopBarButton(onClick = onBack) TopbarNavigationButton(onClick = onBack)
}, },
scrollBehavior = scrollBehavior scrollBehavior = scrollBehavior
) )
@@ -14,7 +14,7 @@ import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.databinding.DialogRecyclerViewBinding import io.legado.app.databinding.DialogRecyclerViewBinding
import io.legado.app.databinding.ItemSourceImportBinding import io.legado.app.databinding.ItemSourceImportBinding
import io.legado.app.help.config.ThemeConfig import io.legado.app.help.config.OldThemeConfig
//import io.legado.app.lib.theme.primaryColor //import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.dialog.CodeDialog import io.legado.app.ui.widget.dialog.CodeDialog
import io.legado.app.ui.widget.dialog.WaitDialog import io.legado.app.ui.widget.dialog.WaitDialog
@@ -126,7 +126,7 @@ class ImportThemeDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_recycl
} }
inner class SourcesAdapter(context: Context) : inner class SourcesAdapter(context: Context) :
RecyclerAdapter<ThemeConfig.Config, ItemSourceImportBinding>(context) { RecyclerAdapter<OldThemeConfig.Config, ItemSourceImportBinding>(context) {
override fun getViewBinding(parent: ViewGroup): ItemSourceImportBinding { override fun getViewBinding(parent: ViewGroup): ItemSourceImportBinding {
return ItemSourceImportBinding.inflate(inflater, parent, false) return ItemSourceImportBinding.inflate(inflater, parent, false)
@@ -135,7 +135,7 @@ class ImportThemeDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_recycl
override fun convert( override fun convert(
holder: ItemViewHolder, holder: ItemViewHolder,
binding: ItemSourceImportBinding, binding: ItemSourceImportBinding,
item: ThemeConfig.Config, item: OldThemeConfig.Config,
payloads: MutableList<Any> payloads: MutableList<Any>
) { ) {
binding.apply { binding.apply {
@@ -8,12 +8,19 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppLog import io.legado.app.constant.AppLog
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.config.ThemeConfig import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.http.decompressed import io.legado.app.help.http.decompressed
import io.legado.app.help.http.newCallResponseBody import io.legado.app.help.http.newCallResponseBody
import io.legado.app.help.http.okHttpClient import io.legado.app.help.http.okHttpClient
import io.legado.app.help.http.text import io.legado.app.help.http.text
import io.legado.app.utils.* import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.isJsonArray
import io.legado.app.utils.isJsonObject
import io.legado.app.utils.isUri
import io.legado.app.utils.readText
import splitties.init.appCtx import splitties.init.appCtx
class ImportThemeViewModel(app: Application) : BaseViewModel(app) { class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
@@ -21,8 +28,8 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
val errorLiveData = MutableLiveData<String>() val errorLiveData = MutableLiveData<String>()
val successLiveData = MutableLiveData<Int>() val successLiveData = MutableLiveData<Int>()
val allSources = arrayListOf<ThemeConfig.Config>() val allSources = arrayListOf<OldThemeConfig.Config>()
val checkSources = arrayListOf<ThemeConfig.Config?>() val checkSources = arrayListOf<OldThemeConfig.Config?>()
val selectStatus = arrayListOf<Boolean>() val selectStatus = arrayListOf<Boolean>()
val isSelectAll: Boolean val isSelectAll: Boolean
@@ -50,7 +57,7 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
execute { execute {
selectStatus.forEachIndexed { index, b -> selectStatus.forEachIndexed { index, b ->
if (b) { if (b) {
ThemeConfig.addConfig(allSources[index]) OldThemeConfig.addConfig(allSources[index])
} }
} }
}.onFinally { }.onFinally {
@@ -72,11 +79,12 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
private suspend fun importSourceAwait(text: String) { private suspend fun importSourceAwait(text: String) {
when { when {
text.isJsonObject() -> { text.isJsonObject() -> {
GSON.fromJsonObject<ThemeConfig.Config>(text).getOrThrow().let { GSON.fromJsonObject<OldThemeConfig.Config>(text).getOrThrow().let {
allSources.add(it) allSources.add(it)
} }
} }
text.isJsonArray() -> GSON.fromJsonArray<ThemeConfig.Config>(text).getOrThrow()
text.isJsonArray() -> GSON.fromJsonArray<OldThemeConfig.Config>(text).getOrThrow()
.let { items -> .let { items ->
allSources.addAll(items) allSources.addAll(items)
} }
@@ -106,7 +114,7 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
private fun comparisonSource() { private fun comparisonSource() {
execute { execute {
allSources.forEach { config -> allSources.forEach { config ->
val source = ThemeConfig.configList.find { val source = OldThemeConfig.configList.find {
it.themeName == config.themeName it.themeName == config.themeName
} }
checkSources.add(source) checkSources.add(source)
@@ -22,8 +22,6 @@ import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.UnfoldLess import androidx.compose.material.icons.filled.UnfoldLess
import androidx.compose.material.icons.filled.UnfoldMore import androidx.compose.material.icons.filled.UnfoldMore
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@@ -48,9 +46,11 @@ import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SearchBarSection import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.bookmark.BookmarkEditSheet import io.legado.app.ui.widget.components.bookmark.BookmarkEditSheet
import io.legado.app.ui.widget.components.bookmark.BookmarkItem import io.legado.app.ui.widget.components.bookmark.BookmarkItem
import io.legado.app.ui.widget.components.button.SmallTopBarButton import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
import io.legado.app.ui.widget.components.lazylist.Scroller import io.legado.app.ui.widget.components.lazylist.Scroller
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class, @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class,
@@ -105,7 +105,7 @@ fun AllBookmarkScreen(
}, },
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
navigationIcon = { navigationIcon = {
SmallTopBarButton(onClick = onBack) TopbarNavigationButton(onClick = onBack)
}, },
actions = { actions = {
if (bookmarksGrouped.isNotEmpty()) { if (bookmarksGrouped.isNotEmpty()) {
@@ -127,11 +127,11 @@ fun AllBookmarkScreen(
IconButton(onClick = { showMenu = true }) { IconButton(onClick = { showMenu = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "Menu") Icon(Icons.Default.MoreVert, contentDescription = "Menu")
} }
DropdownMenu( RoundDropdownMenu(
expanded = showMenu, expanded = showMenu,
onDismissRequest = { showMenu = false } onDismissRequest = { showMenu = false }
) { ) {
DropdownMenuItem( RoundDropdownMenuItem(
text = { Text("导出 JSON") }, text = { Text("导出 JSON") },
onClick = { onClick = {
showMenu = false showMenu = false
@@ -139,7 +139,7 @@ fun AllBookmarkScreen(
exportLauncher.launch(null) exportLauncher.launch(null)
} }
) )
DropdownMenuItem( RoundDropdownMenuItem(
text = { Text("导出 Markdown") }, text = { Text("导出 Markdown") },
onClick = { onClick = {
showMenu = false showMenu = false
@@ -53,6 +53,7 @@ class ExploreShowViewModel(
private val _rawBooks = MutableStateFlow<List<SearchBook>>(emptyList()) private val _rawBooks = MutableStateFlow<List<SearchBook>>(emptyList())
private val _filterState = MutableStateFlow(BookFilterState.fromId(AppConfig.exploreFilterState)) private val _filterState = MutableStateFlow(BookFilterState.fromId(AppConfig.exploreFilterState))
private val _isLoading = MutableStateFlow(false) private val _isLoading = MutableStateFlow(false)
private val _isRefreshing = MutableStateFlow(false)
private val _errorMsg = MutableStateFlow<String?>(null) private val _errorMsg = MutableStateFlow<String?>(null)
private var bookSource: BookSource? = null private var bookSource: BookSource? = null
private var exploreUrl: String? = null private var exploreUrl: String? = null
@@ -68,10 +69,14 @@ class ExploreShowViewModel(
private val _gridCount = MutableStateFlow(appCtx.exploreLayoutGrid) private val _gridCount = MutableStateFlow(appCtx.exploreLayoutGrid)
val gridCount = _gridCount.asStateFlow() val gridCount = _gridCount.asStateFlow()
val isEnd: StateFlow<Boolean> = _isEndStateFlow.asStateFlow() val isEnd: StateFlow<Boolean> = _isEndStateFlow.asStateFlow()
val isRefreshing = _isRefreshing.asStateFlow()
fun saveGridCount(value: Int) { fun saveGridCount(value: Int) {
appCtx.exploreLayoutGrid = value appCtx.exploreLayoutGrid = value
_gridCount.value = value _gridCount.value = value
} }
val uiBooks = combine( val uiBooks = combine(
_rawBooks, _rawBooks,
_filterState, _filterState,
@@ -50,8 +50,8 @@ import io.legado.app.R
import io.legado.app.ui.book.changecover.ChangeCoverDialog import io.legado.app.ui.book.changecover.ChangeCoverDialog
import io.legado.app.ui.widget.components.Cover import io.legado.app.ui.widget.components.Cover
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.button.SmallTopBarButton
import io.legado.app.ui.widget.components.button.TopBarButtonVariant import io.legado.app.ui.widget.components.button.TopBarButtonVariant
import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.utils.SelectImageContract import io.legado.app.utils.SelectImageContract
import io.legado.app.utils.launch import io.legado.app.utils.launch
import io.legado.app.utils.showDialogFragment import io.legado.app.utils.showDialogFragment
@@ -72,13 +72,13 @@ fun BookInfoEditScreen(
GlassMediumFlexibleTopAppBar( GlassMediumFlexibleTopAppBar(
title = { Text(text = stringResource(id = R.string.book_info_edit)) }, title = { Text(text = stringResource(id = R.string.book_info_edit)) },
navigationIcon = { navigationIcon = {
SmallTopBarButton( TopbarNavigationButton(
onClick = onBack, onClick = onBack,
style = TopBarButtonVariant.Outlined style = TopBarButtonVariant.Outlined
) )
}, },
actions = { actions = {
SmallTopBarButton( TopbarNavigationButton(
onClick = { viewModel.save(onSave) }, onClick = { viewModel.save(onSave) },
imageVector = Icons.Default.Save imageVector = Icons.Default.Save
) )
@@ -33,8 +33,8 @@ import io.legado.app.constant.PreferKey
import io.legado.app.databinding.ViewReadMenuBinding import io.legado.app.databinding.ViewReadMenuBinding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.LocalConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.source.getSourceType import io.legado.app.help.source.getSourceType
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
@@ -693,7 +693,7 @@ class ReadMenu @JvmOverloads constructor(
description = context.getString(R.string.day_night_switch), description = context.getString(R.string.day_night_switch),
onClick = { onClick = {
AppConfig.isNightTheme = !AppConfig.isNightTheme AppConfig.isNightTheme = !AppConfig.isNightTheme
ThemeConfig.applyDayNight(context) OldThemeConfig.applyDayNight(context)
buttonMap["theme"]?.setIconResource( buttonMap["theme"]?.setIconResource(
if (AppConfig.isNightTheme) R.drawable.ic_daytime else R.drawable.ic_brightness if (AppConfig.isNightTheme) R.drawable.ic_daytime else R.drawable.ic_brightness
) )
@@ -15,8 +15,8 @@ import io.legado.app.constant.EventBus
import io.legado.app.databinding.DialogReadBookStyleBinding import io.legado.app.databinding.DialogReadBookStyleBinding
import io.legado.app.databinding.ItemReadStyleBinding import io.legado.app.databinding.ItemReadStyleBinding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.OldThemeConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.model.ReadBook import io.legado.app.model.ReadBook
import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.book.read.ReadBookActivity
@@ -121,7 +121,7 @@ class ReadStyleDialog : BaseBottomSheetDialogFragment(R.layout.dialog_read_book_
} }
tvDayNight.setOnClickListener { tvDayNight.setOnClickListener {
AppConfig.isNightTheme = !AppConfig.isNightTheme AppConfig.isNightTheme = !AppConfig.isNightTheme
ThemeConfig.applyDayNight(requireContext()) OldThemeConfig.applyDayNight(requireContext())
} }
// rgPageAnim.setOnCheckedChangeListener { _, checkedId -> // rgPageAnim.setOnCheckedChangeListener { _, checkedId ->
// ReadBook.book?.setPageAnim(-1) // ReadBook.book?.setPageAnim(-1)
@@ -47,6 +47,7 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -87,7 +88,7 @@ import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SearchBarSection import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.SectionHeader import io.legado.app.ui.widget.components.SectionHeader
import io.legado.app.ui.widget.components.button.AlertButton import io.legado.app.ui.widget.components.button.AlertButton
import io.legado.app.ui.widget.components.button.SmallTopBarButton import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarTopBar import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarTopBar
import io.legado.app.ui.widget.components.heatmap.HeatmapConfig import io.legado.app.ui.widget.components.heatmap.HeatmapConfig
import io.legado.app.ui.widget.components.heatmap.HeatmapLegend import io.legado.app.ui.widget.components.heatmap.HeatmapLegend
@@ -98,6 +99,7 @@ import io.legado.app.ui.widget.components.heatmap.WeekdayLabelsColumn
import io.legado.app.ui.widget.components.heatmap.rememberDateRange import io.legado.app.ui.widget.components.heatmap.rememberDateRange
import io.legado.app.ui.widget.components.heatmap.rememberDaysInRange import io.legado.app.ui.widget.components.heatmap.rememberDaysInRange
import io.legado.app.ui.widget.components.heatmap.rememberWeeks import io.legado.app.ui.widget.components.heatmap.rememberWeeks
import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
import io.legado.app.ui.widget.components.swipe.SwipeAction import io.legado.app.ui.widget.components.swipe.SwipeAction
import io.legado.app.ui.widget.components.swipe.SwipeActionContainer import io.legado.app.ui.widget.components.swipe.SwipeActionContainer
import io.legado.app.utils.StringUtils.formatFriendlyDate import io.legado.app.utils.StringUtils.formatFriendlyDate
@@ -163,7 +165,7 @@ fun ReadRecordScreen(
) )
}, },
navigationIcon = { navigationIcon = {
SmallTopBarButton(onClick = onBackClick) TopbarNavigationButton(onClick = onBackClick)
}, },
actions = { actions = {
IconButton(onClick = { IconButton(onClick = {
@@ -318,8 +320,8 @@ fun ReadRecordScreen(
if (showCalendar) { if (showCalendar) {
val sheetState = val sheetState =
androidx.compose.material3.rememberModalBottomSheetState(skipPartiallyExpanded = true) rememberModalBottomSheetState(skipPartiallyExpanded = true)
androidx.compose.material3.ModalBottomSheet( GlassModalBottomSheet(
onDismissRequest = { showCalendar = false }, onDismissRequest = { showCalendar = false },
sheetState = sheetState sheetState = sheetState
) { ) {
@@ -58,7 +58,7 @@ import io.legado.app.ui.widget.components.EmptyMessageView
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SearchBarSection import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.button.AnimatedActionButton import io.legado.app.ui.widget.components.button.AnimatedActionButton
import io.legado.app.ui.widget.components.button.SmallTopBarButton import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.card.TextCard import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@@ -141,7 +141,7 @@ fun SearchContentScreen(
) )
}, },
navigationIcon = { navigationIcon = {
SmallTopBarButton(onClick = onBack) TopbarNavigationButton(onClick = onBack)
}, },
actions = { actions = {
Row( Row(
@@ -294,33 +294,33 @@ fun TocScreen(
when (pagerState.currentPage) { when (pagerState.currentPage) {
0 -> { 0 -> {
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "使用替换规则", text = { Text("使用替换规则") },
trailingIcon = { trailingIcon = {
Checkbox(checked = useReplace, onCheckedChange = null) Checkbox(checked = useReplace, onCheckedChange = null)
}, },
onClick = { viewModel.toggleUseReplace() } onClick = { viewModel.toggleUseReplace() }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "显示字数", text = { Text("显示字数") },
trailingIcon = { trailingIcon = {
Checkbox(checked = showWordCount, onCheckedChange = null) Checkbox(checked = showWordCount, onCheckedChange = null)
}, },
onClick = { viewModel.toggleShowWordCount() } onClick = { viewModel.toggleShowWordCount() }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "反转目录", text = { Text("反转目录") },
onClick = { viewModel.reverseToc() } onClick = { viewModel.reverseToc() }
) )
PillDivider() PillDivider()
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "替换规则", text = { Text("替换规则") },
onClick = { onClick = {
onOpenReplaceRule(null) onOpenReplaceRule(null)
dismiss() dismiss()
} }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "新建替换规则", text = { Text("新建替换规则") },
onClick = { onClick = {
val scopes = mutableListOf<String>() val scopes = mutableListOf<String>()
book?.name?.let { scopes.add(it) } book?.name?.let { scopes.add(it) }
@@ -340,7 +340,7 @@ fun TocScreen(
if (book?.isLocal == true) { if (book?.isLocal == true) {
PillHeaderDivider(title = "本地书籍选项") PillHeaderDivider(title = "本地书籍选项")
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "本地书籍目录规则", text = { Text("本地书籍目录规则") },
onClick = { onClick = {
val intent = val intent =
Intent(context, TxtTocRuleActivity::class.java).apply { Intent(context, TxtTocRuleActivity::class.java).apply {
@@ -351,7 +351,7 @@ fun TocScreen(
} }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "拆分超长章节", text = { Text("拆分超长章节") },
trailingIcon = { trailingIcon = {
Checkbox( Checkbox(
checked = viewModel.isSplitLongChapter, checked = viewModel.isSplitLongChapter,
@@ -368,7 +368,7 @@ fun TocScreen(
else -> { else -> {
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "导出书签为JSON", text = { Text("导出书签为JSON") },
onClick = { onClick = {
val dateFormat = SimpleDateFormat( val dateFormat = SimpleDateFormat(
"yyyyMMdd_HHmm", "yyyyMMdd_HHmm",
@@ -380,7 +380,7 @@ fun TocScreen(
} }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "导出书签为MarkDown", text = { Text("导出书签为MarkDown") },
onClick = { onClick = {
val dateFormat = SimpleDateFormat( val dateFormat = SimpleDateFormat(
"yyyyMMdd_HHmm", "yyyyMMdd_HHmm",
@@ -445,13 +445,13 @@ fun TocScreen(
onDismissRequest = { showVolumeMenu = false } onDismissRequest = { showVolumeMenu = false }
) { ) {
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "展开所有卷", text = { Text("展开所有卷") },
onClick = { onClick = {
viewModel.expandAllVolumes(); showVolumeMenu = false viewModel.expandAllVolumes(); showVolumeMenu = false
} }
) )
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = "收起所有卷", text = { Text("收起所有卷") },
onClick = { onClick = {
viewModel.collapseAllVolumes(); showVolumeMenu = false viewModel.collapseAllVolumes(); showVolumeMenu = false
} }
@@ -463,7 +463,7 @@ fun TocScreen(
PillHeaderDivider(title = "快速跳转") PillHeaderDivider(title = "快速跳转")
volumeItems.forEach { uiItem -> volumeItems.forEach { uiItem ->
RoundDropdownMenuItem( RoundDropdownMenuItem(
text = uiItem.title, text = { Text(uiItem.title) },
onClick = { onClick = {
scope.launch { scope.launch {
val targetIndex = val targetIndex =
@@ -22,7 +22,7 @@ import io.legado.app.databinding.DialogEditTextBinding
import io.legado.app.databinding.DialogImageBlurringBinding import io.legado.app.databinding.DialogImageBlurringBinding
import io.legado.app.help.LauncherIconHelp import io.legado.app.help.LauncherIconHelp
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.OldThemeConfig
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.selector import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.prefs.ColorPreference import io.legado.app.lib.prefs.ColorPreference
@@ -32,7 +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.config.themeConfig.ThemeConfig
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
@@ -181,13 +181,20 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
} }
PreferKey.paletteStyle, PreferKey.pureBlack, PreferKey.enableBlur, PreferKey.swipeAnimation -> { PreferKey.paletteStyle, PreferKey.pureBlack, PreferKey.swipeAnimation -> {
ThemeSyncer.syncAll()
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
recreateActivities() recreateActivities()
}, 100) }, 100)
} }
PreferKey.enableBlur -> {
ThemeConfig.enableBlur = PreferKey.enableBlur.toBoolean()
}
PreferKey.enableProgressiveBlur -> {
ThemeConfig.enableProgressiveBlur = PreferKey.enableProgressiveBlur.toBoolean()
}
PreferKey.customMode -> handleRestartRequired() PreferKey.customMode -> handleRestartRequired()
PreferKey.isPredictiveBackEnabled -> { PreferKey.isPredictiveBackEnabled -> {
@@ -263,17 +270,15 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
.setTitle(getString(R.string.container_opacity)) .setTitle(getString(R.string.container_opacity))
.setMaxValue(100) .setMaxValue(100)
.setMinValue(0) .setMinValue(0)
.setValue(AppConfig.containerOpacity) .setValue(ThemeConfig.containerOpacity)
.setCustomButton((R.string.btn_default_s)) { .setCustomButton((R.string.btn_default_s)) {
AppConfig.containerOpacity = 100 ThemeConfig.containerOpacity = 100
ThemeSyncer.syncContainerOpacity()
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
upPreferenceSummary(PreferKey.containerOpacity) upPreferenceSummary(PreferKey.containerOpacity)
}, 100) }, 100)
} }
.show { .show {
AppConfig.containerOpacity = it ThemeConfig.containerOpacity = it
ThemeSyncer.syncContainerOpacity()
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed({
upPreferenceSummary(PreferKey.containerOpacity) upPreferenceSummary(PreferKey.containerOpacity)
}, 100) }, 100)
@@ -305,11 +310,11 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
alertBinding.editView.text?.toString()?.let { themeName -> alertBinding.editView.text?.toString()?.let { themeName ->
when (key) { when (key) {
"saveDayTheme" -> { "saveDayTheme" -> {
ThemeConfig.saveDayTheme(requireContext(), themeName) OldThemeConfig.saveDayTheme(requireContext(), themeName)
} }
"saveNightTheme" -> { "saveNightTheme" -> {
ThemeConfig.saveNightTheme(requireContext(), themeName) OldThemeConfig.saveNightTheme(requireContext(), themeName)
} }
} }
} }
@@ -2,7 +2,6 @@ package io.legado.app.ui.config
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -15,11 +14,10 @@ import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.databinding.DialogRecyclerViewBinding import io.legado.app.databinding.DialogRecyclerViewBinding
import io.legado.app.databinding.ItemThemeConfigBinding import io.legado.app.databinding.ItemThemeConfigBinding
import io.legado.app.help.config.ThemeConfig import io.legado.app.help.config.OldThemeConfig
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
//import io.legado.app.lib.theme.primaryColor //import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.* import io.legado.app.utils.*
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
@@ -54,14 +52,14 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
} }
fun initData() { fun initData() {
adapter.setItems(ThemeConfig.configList) adapter.setItems(OldThemeConfig.configList)
} }
override fun onMenuItemClick(item: MenuItem?): Boolean { override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) { when (item?.itemId) {
R.id.menu_import -> { R.id.menu_import -> {
requireContext().getClipText()?.let { requireContext().getClipText()?.let {
if (ThemeConfig.addConfig(it)) { if (OldThemeConfig.addConfig(it)) {
initData() initData()
} else { } else {
toastOnUi("格式不对,添加失败") toastOnUi("格式不对,添加失败")
@@ -75,7 +73,7 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
fun delete(index: Int) { fun delete(index: Int) {
alert(R.string.delete, R.string.sure_del) { alert(R.string.delete, R.string.sure_del) {
yesButton { yesButton {
ThemeConfig.delConfig(index) OldThemeConfig.delConfig(index)
initData() initData()
} }
noButton() noButton()
@@ -83,12 +81,12 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
} }
fun share(index: Int) { fun share(index: Int) {
val json = GSON.toJson(ThemeConfig.configList[index]) val json = GSON.toJson(OldThemeConfig.configList[index])
requireContext().share(json, "主题分享") requireContext().share(json, "主题分享")
} }
inner class Adapter(context: Context) : inner class Adapter(context: Context) :
RecyclerAdapter<ThemeConfig.Config, ItemThemeConfigBinding>(context) { RecyclerAdapter<OldThemeConfig.Config, ItemThemeConfigBinding>(context) {
override fun getViewBinding(parent: ViewGroup): ItemThemeConfigBinding { override fun getViewBinding(parent: ViewGroup): ItemThemeConfigBinding {
return ItemThemeConfigBinding.inflate(inflater, parent, false) return ItemThemeConfigBinding.inflate(inflater, parent, false)
@@ -97,7 +95,7 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
override fun convert( override fun convert(
holder: ItemViewHolder, holder: ItemViewHolder,
binding: ItemThemeConfigBinding, binding: ItemThemeConfigBinding,
item: ThemeConfig.Config, item: OldThemeConfig.Config,
payloads: MutableList<Any> payloads: MutableList<Any>
) { ) {
binding.apply { binding.apply {
@@ -113,7 +111,10 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
override fun registerListener(holder: ItemViewHolder, binding: ItemThemeConfigBinding) { override fun registerListener(holder: ItemViewHolder, binding: ItemThemeConfigBinding) {
binding.apply { binding.apply {
root.setOnClickListener { root.setOnClickListener {
ThemeConfig.applyConfig(context, ThemeConfig.configList[holder.layoutPosition]) OldThemeConfig.applyConfig(
context,
OldThemeConfig.configList[holder.layoutPosition]
)
} }
ivShare.setOnClickListener { ivShare.setOnClickListener {
share(holder.layoutPosition) share(holder.layoutPosition)
@@ -34,7 +34,7 @@ import io.legado.app.R
import io.legado.app.service.WebService import io.legado.app.service.WebService
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SplicedColumnGroup import io.legado.app.ui.widget.components.SplicedColumnGroup
import io.legado.app.ui.widget.components.button.SmallTopBarButton import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.exportComponents.FilePickerSheet import io.legado.app.ui.widget.components.exportComponents.FilePickerSheet
import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem
@@ -89,7 +89,7 @@ fun OtherConfigScreen(
}, },
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
navigationIcon = { navigationIcon = {
SmallTopBarButton(onClick = onBackClick) TopbarNavigationButton(onClick = onBackClick)
} }
) )
} }
@@ -0,0 +1,31 @@
package io.legado.app.ui.config.themeConfig
import io.legado.app.constant.PreferKey
import io.legado.app.ui.config.prefDelegate
object ThemeConfig {
var containerOpacity by prefDelegate(PreferKey.containerOpacity, 100)
var enableBlur by prefDelegate(PreferKey.enableBlur, false)
var enableProgressiveBlur by prefDelegate(PreferKey.enableProgressiveBlur, true)
var paletteStyle by prefDelegate(PreferKey.paletteStyle, "tonalSpot")
var appTheme by prefDelegate(PreferKey.appTheme, "0")
var isPureBlack by prefDelegate(PreferKey.pureBlack, false)
var bgImageLight by prefDelegate<String?>(PreferKey.bgImage, null)
var bgImageDark by prefDelegate<String?>(PreferKey.bgImageN, null)
fun hasImageBg(isDark: Boolean): Boolean {
return if (isDark) {
!bgImageDark.isNullOrEmpty()
} else {
!bgImageLight.isNullOrEmpty()
}
}
}
@@ -73,6 +73,7 @@ import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.rules.RuleListScaffold import io.legado.app.ui.widget.components.rules.RuleListScaffold
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@@ -387,35 +388,35 @@ fun ReplaceRuleScreen(
}, },
snackbarHostState = snackbarHostState, snackbarHostState = snackbarHostState,
dropDownMenuContent = { dismiss -> dropDownMenuContent = { dismiss ->
DropdownMenuItem( RoundDropdownMenuItem(
text = { Text("在线导入") }, text = { Text("在线导入") },
onClick = { onClick = {
dismiss() dismiss()
showUrlInput = true // 触发输入框 showUrlInput = true // 触发输入框
} }
) )
DropdownMenuItem( RoundDropdownMenuItem(
text = { Text("本地导入") }, text = { Text("本地导入") },
onClick = { importDoc.launch(arrayOf("text/plain", "application/json")); dismiss() } onClick = { importDoc.launch(arrayOf("text/plain", "application/json")); dismiss() }
) )
DropdownMenuItem( RoundDropdownMenuItem(
text = { Text("分组管理") }, text = { Text("分组管理") },
onClick = { showGroupManageSheet = true; dismiss() } onClick = { showGroupManageSheet = true; dismiss() }
) )
DropdownMenuItem( RoundDropdownMenuItem(
text = { Text("帮助") }, text = { Text("帮助") },
onClick = { /*TODO*/ dismiss() } onClick = { /*TODO*/ dismiss() }
) )
HorizontalDivider() HorizontalDivider()
DropdownMenuItem( RoundDropdownMenuItem(
text = { Text("旧的在前") }, text = { Text("旧的在前") },
onClick = { viewModel.setSortMode("asc"); dismiss() } onClick = { viewModel.setSortMode("asc"); dismiss() }
) )
DropdownMenuItem( RoundDropdownMenuItem(
text = { Text("新的在前") }, text = { Text("新的在前") },
onClick = { viewModel.setSortMode("desc"); dismiss() } onClick = { viewModel.setSortMode("desc"); dismiss() }
) )
DropdownMenuItem( RoundDropdownMenuItem(
text = { Text("名称升序") }, text = { Text("名称升序") },
onClick = { onClick = {
viewModel.setSortMode("name_asc") viewModel.setSortMode("name_asc")
@@ -425,7 +426,7 @@ fun ReplaceRuleScreen(
} }
} }
) )
DropdownMenuItem( RoundDropdownMenuItem(
text = { Text("名称降序") }, text = { Text("名称降序") },
onClick = { onClick = {
viewModel.setSortMode("name_desc") viewModel.setSortMode("name_desc")
@@ -81,7 +81,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.button.AlertButton import io.legado.app.ui.widget.components.button.AlertButton
import io.legado.app.ui.widget.components.button.SmallTopBarButton import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@Composable @Composable
@@ -121,7 +121,7 @@ fun ReplaceEditScreen(
GlassMediumFlexibleTopAppBar( GlassMediumFlexibleTopAppBar(
title = { Text(if (state.id > 0) "编辑替换规则" else "新增替换规则") }, title = { Text(if (state.id > 0) "编辑替换规则" else "新增替换规则") },
navigationIcon = { navigationIcon = {
SmallTopBarButton(onClick = onBack) TopbarNavigationButton(onClick = onBack)
}, },
actions = { actions = {
AnimatedVisibility( AnimatedVisibility(
@@ -7,8 +7,9 @@ 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.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember
import androidx.compose.runtime.getValue import androidx.compose.ui.platform.LocalContext
import io.legado.app.ui.config.themeConfig.ThemeConfig
@OptIn(ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
@@ -16,18 +17,23 @@ fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(), darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val appThemeMode by ThemeState.themeMode.collectAsState() val context = LocalContext.current
val isPureBlack by ThemeState.isPureBlack.collectAsState() val appThemeMode = ThemeResolver.resolveThemeMode(ThemeConfig.appTheme)
val hasImageBg by ThemeState.hasImageBg.collectAsState() val isPureBlack = ThemeConfig.isPureBlack
val paletteStyle by ThemeState.paletteStyle.collectAsState() val hasImageBg = ThemeConfig.hasImageBg(darkTheme)
val paletteStyle = ThemeConfig.paletteStyle
val colorScheme = ThemeManager.getColorScheme( val colorScheme =
mode = appThemeMode, remember(context, appThemeMode, darkTheme, isPureBlack, hasImageBg, paletteStyle) {
darkTheme = darkTheme, ThemeManager.getColorScheme(
isAmoled = isPureBlack, context = context,
isImageBg = hasImageBg, mode = appThemeMode,
paletteStyle = paletteStyle darkTheme = darkTheme,
) isAmoled = isPureBlack,
isImageBg = hasImageBg,
paletteStyle = paletteStyle
)
}
MaterialExpressiveTheme( MaterialExpressiveTheme(
colorScheme = colorScheme, colorScheme = colorScheme,
@@ -36,5 +42,4 @@ fun AppTheme(
shapes = Shapes(), shapes = Shapes(),
content = content content = content
) )
} }
@@ -1,15 +1,11 @@
package io.legado.app.ui.theme 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.ReadOnlyComposable
abstract class BaseColorScheme { abstract class BaseColorScheme {
abstract val lightScheme: ColorScheme abstract val lightScheme: ColorScheme
abstract val darkScheme: ColorScheme abstract val darkScheme: ColorScheme
@Composable
@ReadOnlyComposable
fun getColorScheme(darkTheme: Boolean): ColorScheme { fun getColorScheme(darkTheme: Boolean): ColorScheme {
return if (darkTheme) darkScheme else lightScheme return if (darkTheme) darkScheme else lightScheme
} }
@@ -0,0 +1,31 @@
package io.legado.app.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.theme.ThemeManager.getColorScheme
@Composable
fun rememberOpaqueColorScheme(): ColorScheme {
val context = LocalContext.current
val isDark = isSystemInDarkTheme()
val appThemeMode = ThemeResolver.resolveThemeMode(ThemeConfig.appTheme)
val isPureBlack = ThemeConfig.isPureBlack
val hasImageBg = ThemeConfig.hasImageBg(isDark)
val paletteStyle = ThemeConfig.paletteStyle
return remember(context, appThemeMode, isDark, isPureBlack, hasImageBg, paletteStyle) {
getColorScheme(
context = context,
mode = appThemeMode,
darkTheme = isDark,
isAmoled = isPureBlack,
isImageBg = hasImageBg,
paletteStyle = paletteStyle,
forceOpaque = true
)
}
}
@@ -1,13 +1,11 @@
package io.legado.app.ui.theme package io.legado.app.ui.theme
import android.content.Context
import android.os.Build import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme import androidx.compose.material3.ColorScheme
import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.theme.ThemeResolver.resolvePaletteStyle import io.legado.app.ui.theme.ThemeResolver.resolvePaletteStyle
import io.legado.app.ui.theme.colorScheme.AugustColorScheme import io.legado.app.ui.theme.colorScheme.AugustColorScheme
@@ -40,16 +38,17 @@ object ThemeManager {
AppThemeMode.Transparent to TransparentColorScheme, AppThemeMode.Transparent to TransparentColorScheme,
) )
@Composable
fun getColorScheme( fun getColorScheme(
context: Context,
mode: AppThemeMode, mode: AppThemeMode,
darkTheme: Boolean = isSystemInDarkTheme(), darkTheme: Boolean,
isAmoled: Boolean, isAmoled: Boolean,
isImageBg: Boolean, isImageBg: Boolean,
paletteStyle: String?, paletteStyle: String?,
forceOpaque: Boolean = false forceOpaque: Boolean = false
): ColorScheme { ): ColorScheme {
val context = LocalContext.current
val style = resolvePaletteStyle(paletteStyle) val style = resolvePaletteStyle(paletteStyle)
val actualMode = if (forceOpaque && mode == AppThemeMode.Transparent) { val actualMode = if (forceOpaque && mode == AppThemeMode.Transparent) {
AppThemeMode.WH AppThemeMode.WH
@@ -1,73 +0,0 @@
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 kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import splitties.init.appCtx
object ThemeState {
private val _themeMode =
MutableStateFlow(ThemeResolver.resolveThemeMode(AppConfig.AppTheme.toString()))
val themeMode: StateFlow<AppThemeMode> = _themeMode.asStateFlow()
private val _isPureBlack =
MutableStateFlow(appCtx.getPrefBoolean(PreferKey.pureBlack, false))
val isPureBlack: StateFlow<Boolean> = _isPureBlack.asStateFlow()
private val _hasImageBg =
MutableStateFlow(AppConfig.hasImageBg)
val hasImageBg: StateFlow<Boolean> = _hasImageBg.asStateFlow()
private val _paletteStyle =
MutableStateFlow(AppConfig.paletteStyle)
val paletteStyle: StateFlow<String?> = _paletteStyle.asStateFlow()
private val _enableBlur =
MutableStateFlow(AppConfig.enableBlur)
val enableBlur: StateFlow<Boolean> = _enableBlur.asStateFlow()
private val _containerOpacity =
MutableStateFlow(AppConfig.containerOpacity)
val containerOpacity: StateFlow<Int> = _containerOpacity
fun updateThemeMode(newMode: AppThemeMode) {
if (_themeMode.value != newMode) {
_themeMode.value = newMode
}
}
fun updatePureBlack(enabled: Boolean) {
if (_isPureBlack.value != enabled) {
_isPureBlack.value = enabled
}
}
fun updateImageBg(enabled: Boolean) {
if (_hasImageBg.value != enabled) {
_hasImageBg.value = enabled
}
}
fun updatePaletteStyle(style: String?) {
if (_paletteStyle.value != style) {
_paletteStyle.value = style
}
}
fun updateEnableBlur(enabled: Boolean) {
if (_enableBlur.value != enabled) {
_enableBlur.value = enabled
}
}
fun updateContainerOpacity(opacity: Int) {
if (_containerOpacity.value != opacity) {
_containerOpacity.value = opacity
}
}
}
@@ -1,45 +0,0 @@
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()
syncPaletteStyle()
syncEnableBlur()
syncContainerOpacity()
}
fun syncThemeMode() {
ThemeState.updateThemeMode(
ThemeResolver.resolveThemeMode(AppConfig.AppTheme.toString())
)
}
fun syncPureBlack() {
ThemeState.updatePureBlack(appCtx.getPrefBoolean(PreferKey.pureBlack, false))
}
fun syncImageBg() {
ThemeState.updateImageBg(AppConfig.hasImageBg)
}
fun syncPaletteStyle() {
ThemeState.updatePaletteStyle(AppConfig.paletteStyle)
}
fun syncEnableBlur() {
ThemeState.updateEnableBlur(AppConfig.enableBlur)
}
fun syncContainerOpacity() {
ThemeState.updateContainerOpacity(AppConfig.containerOpacity)
}
}
@@ -5,14 +5,13 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MediumFlexibleTopAppBar import androidx.compose.material3.MediumFlexibleTopAppBar
import androidx.compose.material3.TopAppBarColors
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import io.legado.app.ui.theme.ThemeState import io.legado.app.ui.config.themeConfig.ThemeConfig
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
@@ -22,30 +21,9 @@ fun GlassMediumFlexibleTopAppBar(
subtitle: (@Composable () -> Unit)? = null, subtitle: (@Composable () -> Unit)? = null,
scrollBehavior: TopAppBarScrollBehavior? = null, scrollBehavior: TopAppBarScrollBehavior? = null,
navigationIcon: @Composable () -> Unit = {}, navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {} actions: @Composable RowScope.() -> Unit = {},
colors: TopAppBarColors = GlassTopAppBarDefaults.glassColors()
) { ) {
val opacityValue by ThemeState.containerOpacity.collectAsState()
val enableBlur by ThemeState.enableBlur.collectAsState()
val alpha = opacityValue / 100f
val containerColor = if (enableBlur) {
Color.Transparent
} else {
MaterialTheme.colorScheme.surface.copy(alpha = alpha)
}
val scrolledContainerColor = if (enableBlur) {
Color.Transparent
} else {
MaterialTheme.colorScheme.surfaceContainer.copy(alpha = alpha)
}
val appBarColors = TopAppBarDefaults.topAppBarColors(
containerColor = containerColor,
scrolledContainerColor = scrolledContainerColor
)
MediumFlexibleTopAppBar( MediumFlexibleTopAppBar(
modifier = modifier, modifier = modifier,
title = title, title = title,
@@ -53,7 +31,46 @@ fun GlassMediumFlexibleTopAppBar(
navigationIcon = navigationIcon, navigationIcon = navigationIcon,
actions = actions, actions = actions,
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
colors = appBarColors colors = colors
) )
}
object GlassTopAppBarDefaults {
@Composable
fun glassColors(): TopAppBarColors {
val alpha = ThemeConfig.containerOpacity / 100f
val enableBlur = ThemeConfig.enableBlur
val containerColor = if (enableBlur) {
MaterialTheme.colorScheme.surface
} else {
MaterialTheme.colorScheme.surface.copy(alpha = alpha)
}
val scrolledContainerColor = if (enableBlur) {
MaterialTheme.colorScheme.surface.copy(alpha = 0f)
} else {
MaterialTheme.colorScheme.surfaceContainer
}
return TopAppBarDefaults.topAppBarColors(
containerColor = containerColor,
scrolledContainerColor = scrolledContainerColor
)
}
@Composable
fun controlContainerColor(): Color {
val enableBlur = ThemeConfig.enableBlur
val baseColor = MaterialTheme.colorScheme.surfaceContainerHigh
return if (enableBlur) {
val alpha = 0.6f
baseColor.copy(alpha = alpha)
} else {
baseColor.copy(alpha = ThemeConfig.containerOpacity / 100f)
}
}
} }
@@ -17,31 +17,26 @@ import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedIconButton import androidx.compose.material3.OutlinedIconButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
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.unit.dp import androidx.compose.ui.unit.dp
import io.legado.app.ui.theme.ThemeState import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
enum class TopBarButtonVariant { enum class TopBarButtonVariant {
Filled, Outlined, Icon Filled, Outlined, Icon
} }
@Composable @Composable
fun SmallTopBarButton( fun TopBarButton(
onClick: () -> Unit, onClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
imageVector: ImageVector = Icons.AutoMirrored.Filled.ArrowBack, imageVector: ImageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription: String? = null, contentDescription: String? = null,
style: TopBarButtonVariant = TopBarButtonVariant.Filled style: TopBarButtonVariant = TopBarButtonVariant.Filled
) { ) {
val themeOpacity by ThemeState.containerOpacity.collectAsState()
val baseColor = MaterialTheme.colorScheme.surfaceContainerHigh
val glassColor = baseColor.copy(alpha = (themeOpacity / 100f).coerceAtLeast(0.6f))
val commonModifier = modifier val commonModifier = modifier
.padding(horizontal = 12.dp)
.size(36.dp) .size(36.dp)
when (style) { when (style) {
@@ -50,7 +45,7 @@ fun SmallTopBarButton(
onClick = onClick, onClick = onClick,
modifier = commonModifier, modifier = commonModifier,
colors = IconButtonDefaults.filledTonalIconButtonColors( colors = IconButtonDefaults.filledTonalIconButtonColors(
containerColor = glassColor, containerColor = GlassTopAppBarDefaults.controlContainerColor(),
contentColor = MaterialTheme.colorScheme.onSurface contentColor = MaterialTheme.colorScheme.onSurface
) )
) { ) {
@@ -79,6 +74,50 @@ fun SmallTopBarButton(
} }
} }
@Composable
fun TopbarNavigationButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
imageVector: ImageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription: String? = "返回",
style: TopBarButtonVariant = TopBarButtonVariant.Filled
) {
TopBarButton(
onClick = onClick,
imageVector = imageVector,
contentDescription = contentDescription,
modifier = modifier.padding(start = 12.dp, end = 4.dp),
style = style
)
}
@Composable
fun TopBarActionButton(
onClick: () -> Unit,
imageVector: ImageVector,
contentDescription: String?,
modifier: Modifier = Modifier
) {
val enableProgressive = ThemeConfig.enableProgressiveBlur
if (enableProgressive) {
TopBarButton(
onClick = onClick,
imageVector = imageVector,
contentDescription = contentDescription,
modifier = modifier.padding(end = 12.dp),
style = TopBarButtonVariant.Filled
)
} else {
IconButton(onClick = onClick) {
Icon(
imageVector = imageVector,
contentDescription = contentDescription
)
}
}
}
@Composable @Composable
private fun SmallIcon(imageVector: ImageVector, contentDescription: String?) { private fun SmallIcon(imageVector: ImageVector, contentDescription: String?) {
AnimatedContent( AnimatedContent(
@@ -7,12 +7,10 @@ import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CardElevation import androidx.compose.material3.CardElevation
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import io.legado.app.ui.theme.ThemeState import io.legado.app.ui.config.themeConfig.ThemeConfig
@Composable @Composable
fun GlassCard( fun GlassCard(
@@ -24,7 +22,7 @@ fun GlassCard(
border: BorderStroke? = null, border: BorderStroke? = null,
content: @Composable ColumnScope.() -> Unit content: @Composable ColumnScope.() -> Unit
) { ) {
val opacity by ThemeState.containerOpacity.collectAsState() val opacity = ThemeConfig.containerOpacity
val cardColors = CardDefaults.cardColors( val cardColors = CardDefaults.cardColors(
containerColor = color.copy(alpha = opacity / 100f) containerColor = color.copy(alpha = opacity / 100f)
) )
@@ -4,13 +4,21 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialExpressiveTheme
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MotionScheme
import androidx.compose.material3.Shapes
import androidx.compose.material3.Typography
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.legado.app.ui.theme.rememberOpaqueColorScheme
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
fun RoundDropdownMenu( fun RoundDropdownMenu(
expanded: Boolean, expanded: Boolean,
@@ -21,17 +29,27 @@ fun RoundDropdownMenu(
verticalSpacing: Dp = 8.dp, verticalSpacing: Dp = 8.dp,
content: @Composable ColumnScope.(dismiss: () -> Unit) -> Unit content: @Composable ColumnScope.(dismiss: () -> Unit) -> Unit
) { ) {
val colorScheme = rememberOpaqueColorScheme()
DropdownMenu( DropdownMenu(
expanded = expanded, expanded = expanded,
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
modifier = modifier, modifier = modifier,
shape = shape, shape = shape,
shadowElevation = shadowElevation shadowElevation = shadowElevation,
containerColor = colorScheme.surfaceContainerLow
) { ) {
Column( MaterialExpressiveTheme(
verticalArrangement = Arrangement.spacedBy(verticalSpacing) colorScheme = colorScheme,
typography = Typography(),
motionScheme = MotionScheme.expressive(),
shapes = Shapes()
) { ) {
content(onDismissRequest) Column(
verticalArrangement = Arrangement.spacedBy(verticalSpacing)
) {
content(onDismissRequest)
}
} }
} }
} }
@@ -4,26 +4,28 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.legado.app.ui.theme.rememberOpaqueColorScheme
@Composable @Composable
fun RoundDropdownMenuItem( fun RoundDropdownMenuItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
text: String, text: @Composable () -> Unit,
onClick: () -> Unit, onClick: () -> Unit,
trailingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null,
) { ) {
val colorScheme = rememberOpaqueColorScheme()
DropdownMenuItem( DropdownMenuItem(
modifier = modifier modifier = modifier
.padding(horizontal = 8.dp) .padding(horizontal = 8.dp)
.clip(MaterialTheme.shapes.small) .clip(MaterialTheme.shapes.small)
.background(MaterialTheme.colorScheme.surface), .background(colorScheme.surface),
text = { Text(text) }, text = text,
trailingIcon = trailingIcon, trailingIcon = trailingIcon,
onClick = onClick, onClick = onClick
) )
} }
@@ -15,12 +15,9 @@ import androidx.compose.material3.SheetState
import androidx.compose.material3.Typography import androidx.compose.material3.Typography
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import io.legado.app.ui.theme.ThemeManager import io.legado.app.ui.theme.rememberOpaqueColorScheme
import io.legado.app.ui.theme.ThemeState
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
@@ -31,18 +28,7 @@ fun GlassModalBottomSheet(
containerColor: (ColorScheme) -> Color = { it.surfaceContainerLow }, containerColor: (ColorScheme) -> Color = { it.surfaceContainerLow },
content: @Composable ColumnScope.() -> Unit content: @Composable ColumnScope.() -> Unit
) { ) {
val themeMode by ThemeState.themeMode.collectAsState() val colorScheme = rememberOpaqueColorScheme()
val isPureBlack by ThemeState.isPureBlack.collectAsState()
val hasImageBg by ThemeState.hasImageBg.collectAsState()
val paletteStyle by ThemeState.paletteStyle.collectAsState()
val colorScheme = ThemeManager.getColorScheme(
mode = themeMode,
isAmoled = isPureBlack,
isImageBg = hasImageBg,
forceOpaque = true,
paletteStyle = paletteStyle
)
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
@@ -12,6 +12,7 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.SwipeToDismissBox import androidx.compose.material3.SwipeToDismissBox
import androidx.compose.material3.SwipeToDismissBoxState
import androidx.compose.material3.SwipeToDismissBoxValue import androidx.compose.material3.SwipeToDismissBoxValue
import androidx.compose.material3.contentColorFor import androidx.compose.material3.contentColorFor
import androidx.compose.material3.rememberSwipeToDismissBoxState import androidx.compose.material3.rememberSwipeToDismissBoxState
@@ -79,8 +80,8 @@ fun SwipeActionContainer(
SwipeToDismissBoxValue.StartToEnd -> { SwipeToDismissBoxValue.StartToEnd -> {
startAction?.let { action -> startAction?.let { action ->
SwipeBackground( SwipeBackground(
dismissState = dismissState,
action = action, action = action,
progress = progress,
alignStart = true alignStart = true
) )
} }
@@ -89,8 +90,8 @@ fun SwipeActionContainer(
SwipeToDismissBoxValue.EndToStart -> { SwipeToDismissBoxValue.EndToStart -> {
endAction?.let { action -> endAction?.let { action ->
SwipeBackground( SwipeBackground(
dismissState = dismissState,
action = action, action = action,
progress = progress,
alignStart = false alignStart = false
) )
} }
@@ -114,10 +115,13 @@ fun SwipeActionContainer(
@Composable @Composable
private fun SwipeBackground( private fun SwipeBackground(
action: SwipeAction, action: SwipeAction,
progress: Float, dismissState: SwipeToDismissBoxState,
alignStart: Boolean alignStart: Boolean
) { ) {
val isThresholdReached = progress > 0.6f val isThresholdReached = dismissState.targetValue == if (alignStart)
SwipeToDismissBoxValue.StartToEnd
else
SwipeToDismissBoxValue.EndToStart
val backgroundColor by animateColorAsState( val backgroundColor by animateColorAsState(
targetValue = if (isThresholdReached) targetValue = if (isThresholdReached)
@@ -128,8 +132,8 @@ private fun SwipeBackground(
) )
val iconScale by animateFloatAsState( val iconScale by animateFloatAsState(
targetValue = if (isThresholdReached) 1.3f targetValue = if (isThresholdReached) 1.2f
else progress.coerceIn(0.5f, 1f), else 1f,
label = "iconScale" label = "iconScale"
) )
@@ -29,7 +29,7 @@ import io.legado.app.ui.widget.components.AdaptiveAnimatedText
import io.legado.app.ui.widget.components.AnimatedTextLine import io.legado.app.ui.widget.components.AnimatedTextLine
import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
import io.legado.app.ui.widget.components.SearchBarSection import io.legado.app.ui.widget.components.SearchBarSection
import io.legado.app.ui.widget.components.button.SmallTopBarButton import io.legado.app.ui.widget.components.button.TopbarNavigationButton
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
import io.legado.app.ui.widget.components.rules.RuleActionState import io.legado.app.ui.widget.components.rules.RuleActionState
@@ -75,7 +75,7 @@ fun <T> DynamicTopAppBar(
{ AnimatedTextLine(text = it) } { AnimatedTextLine(text = it) }
}, },
navigationIcon = { navigationIcon = {
SmallTopBarButton( TopbarNavigationButton(
onClick = { if (isSelecting) onClearSelection() else onBackClick() }, onClick = { if (isSelecting) onClearSelection() else onBackClick() },
imageVector = if (isSelecting) Icons.Default.Close else Icons.AutoMirrored.Filled.ArrowBack, imageVector = if (isSelecting) Icons.Default.Close else Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = if (isSelecting) "取消选择" else "返回" contentDescription = if (isSelecting) "取消选择" else "返回"
@@ -1386,4 +1386,5 @@
<string name="show_swipe_animation">启用滑动时动画</string> <string name="show_swipe_animation">启用滑动时动画</string>
<string name="click_image_way">点击图片行为</string> <string name="click_image_way">点击图片行为</string>
<string name="show_web_log">输出日志</string> <string name="show_web_log">输出日志</string>
<string name="is_blur_progressive_enable">启用渐变模糊</string>
</resources> </resources>
+1
View File
@@ -1390,4 +1390,5 @@
<string name="show_swipe_animation">启用滑动时动画</string> <string name="show_swipe_animation">启用滑动时动画</string>
<string name="click_image_way">点击图片行为</string> <string name="click_image_way">点击图片行为</string>
<string name="show_web_log">输出日志</string> <string name="show_web_log">输出日志</string>
<string name="is_blur_progressive_enable">启用渐变模糊</string>
</resources> </resources>
@@ -116,6 +116,12 @@
android:title="@string/is_blur_enable" android:title="@string/is_blur_enable"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<io.legado.app.lib.prefs.SwitchPreference
android:defaultValue="false"
android:key="enableProgressiveBlur"
android:title="@string/is_blur_progressive_enable"
app:iconSpaceReserved="false" />
</io.legado.app.lib.prefs.PreferenceCategory> </io.legado.app.lib.prefs.PreferenceCategory>
<io.legado.app.lib.prefs.PreferenceCategory <io.legado.app.lib.prefs.PreferenceCategory