HapeLee 8 месяцев назад
Родитель
Сommit
0250001a27
45 измененных файлов с 361 добавлено и 327 удалено
  1. 3 4
      app/src/main/java/io/legado/app/App.kt
  2. 2 4
      app/src/main/java/io/legado/app/base/BaseActivity.kt
  3. 2 4
      app/src/main/java/io/legado/app/base/BaseComposeActivity.kt
  4. 1 0
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  5. 4 4
      app/src/main/java/io/legado/app/help/DefaultData.kt
  6. 3 3
      app/src/main/java/io/legado/app/help/JsExtensions.kt
  7. 1 1
      app/src/main/java/io/legado/app/help/config/OldThemeConfig.kt
  8. 4 4
      app/src/main/java/io/legado/app/help/storage/Backup.kt
  9. 6 6
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  10. 2 2
      app/src/main/java/io/legado/app/lib/prefs/ThemeCardPreference.kt
  11. 2 2
      app/src/main/java/io/legado/app/lib/prefs/ThemeModePreference.kt
  12. 2 2
      app/src/main/java/io/legado/app/ui/about/AboutScreen.kt
  13. 3 3
      app/src/main/java/io/legado/app/ui/association/ImportThemeDialog.kt
  14. 16 8
      app/src/main/java/io/legado/app/ui/association/ImportThemeViewModel.kt
  15. 7 7
      app/src/main/java/io/legado/app/ui/book/bookmark/AllBookmarkScreen.kt
  16. 5 0
      app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt
  17. 3 3
      app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditScreen.kt
  18. 2 2
      app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
  19. 2 2
      app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
  20. 6 4
      app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordScreen.kt
  21. 2 2
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt
  22. 12 12
      app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt
  23. 16 11
      app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
  24. 11 10
      app/src/main/java/io/legado/app/ui/config/ThemeListDialog.kt
  25. 2 2
      app/src/main/java/io/legado/app/ui/config/otherConfig/OtherConfigScreen.kt
  26. 31 0
      app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfig.kt
  27. 9 8
      app/src/main/java/io/legado/app/ui/replace/ReplaceRuleScreen.kt
  28. 2 2
      app/src/main/java/io/legado/app/ui/replace/edit/ReplaceEditScreen.kt
  29. 19 14
      app/src/main/java/io/legado/app/ui/theme/AppTheme.kt
  30. 0 4
      app/src/main/java/io/legado/app/ui/theme/BaseColorScheme.kt
  31. 31 0
      app/src/main/java/io/legado/app/ui/theme/OpaqueColorScheme.kt
  32. 5 6
      app/src/main/java/io/legado/app/ui/theme/ThemeManager.kt
  33. 0 73
      app/src/main/java/io/legado/app/ui/theme/ThemeState.kt
  34. 0 45
      app/src/main/java/io/legado/app/ui/theme/ThemeSyncer.kt
  35. 44 27
      app/src/main/java/io/legado/app/ui/widget/components/GlassMediumFlexibleTopAppBar.kt
  36. 48 9
      app/src/main/java/io/legado/app/ui/widget/components/button/TopBarButton.kt
  37. 2 4
      app/src/main/java/io/legado/app/ui/widget/components/card/GlassCard.kt
  38. 22 4
      app/src/main/java/io/legado/app/ui/widget/components/menuItem/RoundDropdownMenu.kt
  39. 7 5
      app/src/main/java/io/legado/app/ui/widget/components/menuItem/RoundDropdownMenuItem.kt
  40. 2 16
      app/src/main/java/io/legado/app/ui/widget/components/modalBottomSheet/GlassModalBottomSheet.kt
  41. 10 6
      app/src/main/java/io/legado/app/ui/widget/components/swipe/SwipeActionContainer.kt
  42. 2 2
      app/src/main/java/io/legado/app/ui/widget/components/topbar/DynamicTopAppBar.kt
  43. 1 0
      app/src/main/res/values-zh-rCN/strings.xml
  44. 1 0
      app/src/main/res/values/strings.xml
  45. 6 0
      app/src/main/res/xml/pref_config_theme.xml

+ 3 - 4
app/src/main/java/io/legado/app/App.kt

@@ -10,7 +10,6 @@ import android.content.res.Configuration
 import android.graphics.BitmapFactory
 import android.os.Build
 import androidx.core.graphics.scale
-import coil.ImageLoader
 import com.github.liuyueyi.quick.transfer.constants.TransType
 import com.google.android.material.color.DynamicColors
 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.book.BookHelp
 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.ThemeConfig
-import io.legado.app.help.config.ThemeConfig.applyDayNightInit
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.http.Cronet
 import io.legado.app.help.http.ObsoleteUrlFactory
@@ -155,7 +154,7 @@ class App : Application() {
             BookHelp.clearInvalidCache()
             Backup.clearCache()
             ReadBookConfig.clearBgAndCache()
-            ThemeConfig.clearBg()
+            OldThemeConfig.clearBg()
             //初始化简繁转换引擎
             when (AppConfig.chineseConverterType) {
                 1 -> {

+ 2 - 4
app/src/main/java/io/legado/app/base/BaseActivity.kt

@@ -27,9 +27,8 @@ import io.legado.app.constant.EventBus
 import io.legado.app.constant.PreferKey
 import io.legado.app.constant.Theme
 import io.legado.app.help.config.AppConfig
-import io.legado.app.help.config.ThemeConfig
+import io.legado.app.help.config.OldThemeConfig
 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.applyTint
 import io.legado.app.utils.disableAutoFill
@@ -232,7 +231,7 @@ abstract class BaseActivity<VB : ViewBinding>(
     open fun upBackgroundImage() {
         if (imageBg) {
             try {
-                ThemeConfig.getBgImage(this, windowManager.windowSize)?.let {
+                OldThemeConfig.getBgImage(this, windowManager.windowSize)?.let {
                     window.decorView.background = it.toDrawable(resources)
                 }
             } catch (e: OutOfMemoryError) {
@@ -245,7 +244,6 @@ abstract class BaseActivity<VB : ViewBinding>(
 
     open fun observeLiveBus() {
         observeEvent<String>(EventBus.RECREATE) {
-            ThemeSyncer.syncAll()
             recreate()
         }
     }

+ 2 - 4
app/src/main/java/io/legado/app/base/BaseComposeActivity.kt

@@ -9,9 +9,8 @@ import androidx.core.graphics.drawable.toDrawable
 import androidx.core.view.WindowCompat
 import io.legado.app.constant.EventBus
 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.ThemeSyncer
 import io.legado.app.utils.disableAutoFill
 import io.legado.app.utils.fullScreen
 import io.legado.app.utils.observeEvent
@@ -66,7 +65,7 @@ abstract class BaseComposeActivity(
 
     open fun upBackgroundImage() {
         try {
-            ThemeConfig.getBgImage(this, windowManager.windowSize)?.let {
+            OldThemeConfig.getBgImage(this, windowManager.windowSize)?.let {
                 window.setBackgroundDrawable(it.toDrawable(resources))
             }
         } catch (_: Exception) {}
@@ -74,7 +73,6 @@ abstract class BaseComposeActivity(
 
     open fun observeLiveBus() {
         observeEvent<String>(EventBus.RECREATE) {
-            ThemeSyncer.syncAll()
             recreate()
         }
     }

+ 1 - 0
app/src/main/java/io/legado/app/constant/PreferKey.kt

@@ -181,6 +181,7 @@ object PreferKey {
     const val composeEngine = "composeEngine"
     const val containerOpacity = "containerOpacity"
     const val enableBlur = "enableBlur"
+    const val enableProgressiveBlur = "enableProgressiveBlur"
     const val cBackground = "colorBackground"
     const val cBBackground = "colorBottomBackground"
     const val bgImage = "backgroundImage"

+ 4 - 4
app/src/main/java/io/legado/app/help/DefaultData.kt

@@ -8,8 +8,8 @@ import io.legado.app.data.entities.KeyboardAssist
 import io.legado.app.data.entities.RssSource
 import io.legado.app.data.entities.TxtTocRule
 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.ThemeConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.model.BookCover
 import io.legado.app.utils.GSON
@@ -70,12 +70,12 @@ object DefaultData {
         GSON.fromJsonArray<TxtTocRule>(json).getOrNull() ?: emptyList()
     }
 
-    val themeConfigs: List<ThemeConfig.Config> by lazy {
+    val themeConfigs: List<OldThemeConfig.Config> by lazy {
         val json = String(
-            appCtx.assets.open("defaultData${File.separator}${ThemeConfig.configFileName}")
+            appCtx.assets.open("defaultData${File.separator}${OldThemeConfig.configFileName}")
                 .readBytes()
         )
-        GSON.fromJsonArray<ThemeConfig.Config>(json).getOrNull() ?: emptyList()
+        GSON.fromJsonArray<OldThemeConfig.Config>(json).getOrNull() ?: emptyList()
     }
 
     val rssSources: List<RssSource> by lazy {

+ 3 - 3
app/src/main/java/io/legado/app/help/JsExtensions.kt

@@ -15,8 +15,8 @@ import io.legado.app.constant.AppPattern
 import io.legado.app.data.entities.BaseSource
 import io.legado.app.exception.NoStackTraceException
 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.ThemeConfig
 import io.legado.app.help.http.BackstageWebView
 import io.legado.app.help.http.CookieManager.cookieJarHeader
 import io.legado.app.help.http.CookieStore
@@ -1203,12 +1203,12 @@ interface JsExtensions : JsEncodeUtils {
      */
     @JavascriptInterface
     fun getThemeConfig(): String {
-        val themeConfig = ThemeConfig.getDurConfig(appCtx)
+        val themeConfig = OldThemeConfig.getDurConfig(appCtx)
         return GSON.toJson(themeConfig)
     }
 
     fun getThemeConfigMap(): Map<String, Any?> {
-        return ThemeConfig.getDurConfig(appCtx).toMap()
+        return OldThemeConfig.getDurConfig(appCtx).toMap()
     }
 
 }

+ 1 - 1
app/src/main/java/io/legado/app/help/config/ThemeConfig.kt → app/src/main/java/io/legado/app/help/config/OldThemeConfig.kt

@@ -32,7 +32,7 @@ import splitties.init.appCtx
 import java.io.File
 
 @Keep
-object ThemeConfig {
+object OldThemeConfig {
     const val configFileName = "themeConfig.json"
     val configFilePath = FileUtils.getPath(appCtx.filesDir, configFileName)
 

+ 4 - 4
app/src/main/java/io/legado/app/help/storage/Backup.kt

@@ -13,8 +13,8 @@ import io.legado.app.help.AppWebDav
 import io.legado.app.help.DirectLinkUpload
 import io.legado.app.help.config.AppConfig
 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.ThemeConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.model.BookCover
 import io.legado.app.utils.FileUtils
@@ -82,7 +82,7 @@ object Backup {
             DirectLinkUpload.ruleFileName,
             ReadBookConfig.configFileName,
             ReadBookConfig.shareConfigFileName,
-            ThemeConfig.configFileName,
+            OldThemeConfig.configFileName,
             BookCover.configFileName,
             "config.xml"
         )
@@ -169,8 +169,8 @@ object Backup {
             FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.shareConfigFileName)
                 .writeText(it)
         }
-        GSON.toJson(ThemeConfig.configList).let {
-            FileUtils.createFileIfNotExist(backupPath + File.separator + ThemeConfig.configFileName)
+        GSON.toJson(OldThemeConfig.configList).let {
+            FileUtils.createFileIfNotExist(backupPath + File.separator + OldThemeConfig.configFileName)
                 .writeText(it)
         }
         DirectLinkUpload.getConfig()?.let {

+ 6 - 6
app/src/main/java/io/legado/app/help/storage/Restore.kt

@@ -33,8 +33,8 @@ import io.legado.app.help.LauncherIconHelp
 import io.legado.app.help.book.isLocal
 import io.legado.app.help.book.upType
 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.ThemeConfig
 import io.legado.app.model.BookCover
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.utils.ACache
@@ -216,12 +216,12 @@ object Restore {
             AppLog.put("恢复直链上传出错\n${it.localizedMessage}", it)
         }
         //恢复主题配置
-        File(path, ThemeConfig.configFileName).takeIf {
+        File(path, OldThemeConfig.configFileName).takeIf {
             it.exists()
         }?.runCatching {
-            FileUtils.delete(ThemeConfig.configFilePath)
-            copyTo(File(ThemeConfig.configFilePath))
-            ThemeConfig.upConfig()
+            FileUtils.delete(OldThemeConfig.configFilePath)
+            copyTo(File(OldThemeConfig.configFilePath))
+            OldThemeConfig.upConfig()
         }?.onFailure {
             AppLog.put("恢复主题出错\n${it.localizedMessage}", it)
         }
@@ -301,7 +301,7 @@ object Restore {
             if (!BuildConfig.DEBUG) {
                 LauncherIconHelp.changeIcon(appCtx.getPrefString(PreferKey.launcherIcon))
             }
-            ThemeConfig.applyDayNight(appCtx)
+            OldThemeConfig.applyDayNight(appCtx)
         }
     }
 

+ 2 - 2
app/src/main/java/io/legado/app/lib/prefs/ThemeCardPreference.kt

@@ -21,7 +21,7 @@ import io.legado.app.constant.EventBus
 import io.legado.app.constant.PreferKey
 import io.legado.app.help.config.AppConfig
 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.postEvent
 import io.legado.app.utils.restart
@@ -91,13 +91,13 @@ class ThemeCardPreference(context: Context, attrs: AttributeSet) : Preference(co
                             return@setOnClickListener
                         } else {
                             AppConfig.containerOpacity = 0
-                            ThemeSyncer.syncContainerOpacity()
                         }
                     }
                     val oldValue = currentValue
                     currentValue = value
                     persistString(value)
                     callChangeListener(value)
+                    ThemeConfig.appTheme = value
                     notifyDataSetChanged()
                     val isDynamicSwitch = (oldValue == "12" || value == "12")
                     Handler(Looper.getMainLooper()).postDelayed({

+ 2 - 2
app/src/main/java/io/legado/app/lib/prefs/ThemeModePreference.kt

@@ -7,7 +7,7 @@ import android.util.AttributeSet
 import androidx.preference.PreferenceViewHolder
 import com.google.android.material.button.MaterialButtonToggleGroup
 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) {
@@ -59,7 +59,7 @@ class ThemeModePreference(context: Context, attrs: AttributeSet) : Preference(co
                     persistString(newValue)
                     callChangeListener(newValue)
                     Handler(Looper.getMainLooper()).postDelayed({
-                        ThemeConfig.applyDayNight(context)
+                        OldThemeConfig.applyDayNight(context)
                     }, 300)
                 }
             }

+ 2 - 2
app/src/main/java/io/legado/app/ui/about/AboutScreen.kt

@@ -35,7 +35,7 @@ import io.legado.app.R
 import io.legado.app.constant.AppConst.appInfo
 import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
 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.settingItem.SettingItem
 
@@ -61,7 +61,7 @@ fun AboutScreen(
             GlassMediumFlexibleTopAppBar(
                 title = { Text(stringResource(R.string.about)) },
                 navigationIcon = {
-                    SmallTopBarButton(onClick = onBack)
+                    TopbarNavigationButton(onClick = onBack)
                 },
                 scrollBehavior = scrollBehavior
             )

+ 3 - 3
app/src/main/java/io/legado/app/ui/association/ImportThemeDialog.kt

@@ -14,7 +14,7 @@ import io.legado.app.base.adapter.ItemViewHolder
 import io.legado.app.base.adapter.RecyclerAdapter
 import io.legado.app.databinding.DialogRecyclerViewBinding
 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.ui.widget.dialog.CodeDialog
 import io.legado.app.ui.widget.dialog.WaitDialog
@@ -126,7 +126,7 @@ class ImportThemeDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_recycl
     }
 
     inner class SourcesAdapter(context: Context) :
-        RecyclerAdapter<ThemeConfig.Config, ItemSourceImportBinding>(context) {
+        RecyclerAdapter<OldThemeConfig.Config, ItemSourceImportBinding>(context) {
 
         override fun getViewBinding(parent: ViewGroup): ItemSourceImportBinding {
             return ItemSourceImportBinding.inflate(inflater, parent, false)
@@ -135,7 +135,7 @@ class ImportThemeDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_recycl
         override fun convert(
             holder: ItemViewHolder,
             binding: ItemSourceImportBinding,
-            item: ThemeConfig.Config,
+            item: OldThemeConfig.Config,
             payloads: MutableList<Any>
         ) {
             binding.apply {

+ 16 - 8
app/src/main/java/io/legado/app/ui/association/ImportThemeViewModel.kt

@@ -8,12 +8,19 @@ import io.legado.app.base.BaseViewModel
 import io.legado.app.constant.AppConst
 import io.legado.app.constant.AppLog
 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.newCallResponseBody
 import io.legado.app.help.http.okHttpClient
 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
 
 class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
@@ -21,8 +28,8 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
     val errorLiveData = MutableLiveData<String>()
     val successLiveData = MutableLiveData<Int>()
 
-    val allSources = arrayListOf<ThemeConfig.Config>()
-    val checkSources = arrayListOf<ThemeConfig.Config?>()
+    val allSources = arrayListOf<OldThemeConfig.Config>()
+    val checkSources = arrayListOf<OldThemeConfig.Config?>()
     val selectStatus = arrayListOf<Boolean>()
 
     val isSelectAll: Boolean
@@ -50,7 +57,7 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
         execute {
             selectStatus.forEachIndexed { index, b ->
                 if (b) {
-                    ThemeConfig.addConfig(allSources[index])
+                    OldThemeConfig.addConfig(allSources[index])
                 }
             }
         }.onFinally {
@@ -72,11 +79,12 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
     private suspend fun importSourceAwait(text: String) {
         when {
             text.isJsonObject() -> {
-                GSON.fromJsonObject<ThemeConfig.Config>(text).getOrThrow().let {
+                GSON.fromJsonObject<OldThemeConfig.Config>(text).getOrThrow().let {
                     allSources.add(it)
                 }
             }
-            text.isJsonArray() -> GSON.fromJsonArray<ThemeConfig.Config>(text).getOrThrow()
+
+            text.isJsonArray() -> GSON.fromJsonArray<OldThemeConfig.Config>(text).getOrThrow()
                 .let { items ->
                     allSources.addAll(items)
                 }
@@ -106,7 +114,7 @@ class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
     private fun comparisonSource() {
         execute {
             allSources.forEach { config ->
-                val source = ThemeConfig.configList.find {
+                val source = OldThemeConfig.configList.find {
                     it.themeName == config.themeName
                 }
                 checkSources.add(source)

+ 7 - 7
app/src/main/java/io/legado/app/ui/book/bookmark/AllBookmarkScreen.kt

@@ -22,8 +22,6 @@ import androidx.compose.material.icons.filled.MoreVert
 import androidx.compose.material.icons.filled.Search
 import androidx.compose.material.icons.filled.UnfoldLess
 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.ExperimentalMaterial3ExpressiveApi
 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.bookmark.BookmarkEditSheet
 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.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
 
 @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class,
@@ -105,7 +105,7 @@ fun AllBookmarkScreen(
                     },
                     scrollBehavior = scrollBehavior,
                     navigationIcon = {
-                        SmallTopBarButton(onClick = onBack)
+                        TopbarNavigationButton(onClick = onBack)
                     },
                     actions = {
                         if (bookmarksGrouped.isNotEmpty()) {
@@ -127,11 +127,11 @@ fun AllBookmarkScreen(
                         IconButton(onClick = { showMenu = true }) {
                             Icon(Icons.Default.MoreVert, contentDescription = "Menu")
                         }
-                        DropdownMenu(
+                        RoundDropdownMenu(
                             expanded = showMenu,
                             onDismissRequest = { showMenu = false }
                         ) {
-                            DropdownMenuItem(
+                            RoundDropdownMenuItem(
                                 text = { Text("导出 JSON") },
                                 onClick = {
                                     showMenu = false
@@ -139,7 +139,7 @@ fun AllBookmarkScreen(
                                     exportLauncher.launch(null)
                                 }
                             )
-                            DropdownMenuItem(
+                            RoundDropdownMenuItem(
                                 text = { Text("导出 Markdown") },
                                 onClick = {
                                     showMenu = false

+ 5 - 0
app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt

@@ -53,6 +53,7 @@ class ExploreShowViewModel(
     private val _rawBooks = MutableStateFlow<List<SearchBook>>(emptyList())
     private val _filterState = MutableStateFlow(BookFilterState.fromId(AppConfig.exploreFilterState))
     private val _isLoading = MutableStateFlow(false)
+    private val _isRefreshing = MutableStateFlow(false)
     private val _errorMsg = MutableStateFlow<String?>(null)
     private var bookSource: BookSource? = null
     private var exploreUrl: String? = null
@@ -68,10 +69,14 @@ class ExploreShowViewModel(
     private val _gridCount = MutableStateFlow(appCtx.exploreLayoutGrid)
     val gridCount = _gridCount.asStateFlow()
     val isEnd: StateFlow<Boolean> = _isEndStateFlow.asStateFlow()
+
+    val isRefreshing = _isRefreshing.asStateFlow()
+
     fun saveGridCount(value: Int) {
         appCtx.exploreLayoutGrid = value
         _gridCount.value = value
     }
+
     val uiBooks = combine(
         _rawBooks,
         _filterState,

+ 3 - 3
app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditScreen.kt

@@ -50,8 +50,8 @@ import io.legado.app.R
 import io.legado.app.ui.book.changecover.ChangeCoverDialog
 import io.legado.app.ui.widget.components.Cover
 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.TopbarNavigationButton
 import io.legado.app.utils.SelectImageContract
 import io.legado.app.utils.launch
 import io.legado.app.utils.showDialogFragment
@@ -72,13 +72,13 @@ fun BookInfoEditScreen(
             GlassMediumFlexibleTopAppBar(
                 title = { Text(text = stringResource(id = R.string.book_info_edit)) },
                 navigationIcon = {
-                    SmallTopBarButton(
+                    TopbarNavigationButton(
                         onClick = onBack,
                         style = TopBarButtonVariant.Outlined
                     )
                 },
                 actions = {
-                    SmallTopBarButton(
+                    TopbarNavigationButton(
                         onClick = { viewModel.save(onSave) },
                         imageVector = Icons.Default.Save
                     )

+ 2 - 2
app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt

@@ -33,8 +33,8 @@ import io.legado.app.constant.PreferKey
 import io.legado.app.databinding.ViewReadMenuBinding
 import io.legado.app.help.config.AppConfig
 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.ThemeConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.source.getSourceType
 import io.legado.app.lib.dialogs.alert
@@ -693,7 +693,7 @@ class ReadMenu @JvmOverloads constructor(
                 description = context.getString(R.string.day_night_switch),
                 onClick = {
                     AppConfig.isNightTheme = !AppConfig.isNightTheme
-                    ThemeConfig.applyDayNight(context)
+                    OldThemeConfig.applyDayNight(context)
                     buttonMap["theme"]?.setIconResource(
                         if (AppConfig.isNightTheme) R.drawable.ic_daytime else R.drawable.ic_brightness
                     )

+ 2 - 2
app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt

@@ -15,8 +15,8 @@ import io.legado.app.constant.EventBus
 import io.legado.app.databinding.DialogReadBookStyleBinding
 import io.legado.app.databinding.ItemReadStyleBinding
 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.ThemeConfig
 import io.legado.app.lib.dialogs.alert
 import io.legado.app.model.ReadBook
 import io.legado.app.ui.book.read.ReadBookActivity
@@ -121,7 +121,7 @@ class ReadStyleDialog : BaseBottomSheetDialogFragment(R.layout.dialog_read_book_
         }
         tvDayNight.setOnClickListener {
             AppConfig.isNightTheme = !AppConfig.isNightTheme
-            ThemeConfig.applyDayNight(requireContext())
+            OldThemeConfig.applyDayNight(requireContext())
         }
 //        rgPageAnim.setOnCheckedChangeListener { _, checkedId ->
 //            ReadBook.book?.setPageAnim(-1)

+ 6 - 4
app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordScreen.kt

@@ -47,6 +47,7 @@ import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
 import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.rememberModalBottomSheetState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 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.SectionHeader
 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.HeatmapConfig
 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.rememberDaysInRange
 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.SwipeActionContainer
 import io.legado.app.utils.StringUtils.formatFriendlyDate
@@ -163,7 +165,7 @@ fun ReadRecordScreen(
                         )
                     },
                     navigationIcon = {
-                        SmallTopBarButton(onClick = onBackClick)
+                        TopbarNavigationButton(onClick = onBackClick)
                     },
                     actions = {
                         IconButton(onClick = {
@@ -318,8 +320,8 @@ fun ReadRecordScreen(
 
     if (showCalendar) {
         val sheetState =
-            androidx.compose.material3.rememberModalBottomSheetState(skipPartiallyExpanded = true)
-        androidx.compose.material3.ModalBottomSheet(
+            rememberModalBottomSheetState(skipPartiallyExpanded = true)
+        GlassModalBottomSheet(
             onDismissRequest = { showCalendar = false },
             sheetState = sheetState
         ) {

+ 2 - 2
app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt

@@ -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.SearchBarSection
 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.lazylist.FastScrollLazyColumn
 import org.koin.androidx.compose.koinViewModel
@@ -141,7 +141,7 @@ fun SearchContentScreen(
                         )
                     },
                     navigationIcon = {
-                        SmallTopBarButton(onClick = onBack)
+                        TopbarNavigationButton(onClick = onBack)
                     },
                     actions = {
                         Row(

+ 12 - 12
app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt

@@ -294,33 +294,33 @@ fun TocScreen(
                     when (pagerState.currentPage) {
                         0 -> {
                             RoundDropdownMenuItem(
-                                text = "使用替换规则",
+                                text = { Text("使用替换规则") },
                                 trailingIcon = {
                                     Checkbox(checked = useReplace, onCheckedChange = null)
                                 },
                                 onClick = { viewModel.toggleUseReplace() }
                             )
                             RoundDropdownMenuItem(
-                                text = "显示字数",
+                                text = { Text("显示字数") },
                                 trailingIcon = {
                                     Checkbox(checked = showWordCount, onCheckedChange = null)
                                 },
                                 onClick = { viewModel.toggleShowWordCount() }
                             )
                             RoundDropdownMenuItem(
-                                text = "反转目录",
+                                text = { Text("反转目录") },
                                 onClick = { viewModel.reverseToc() }
                             )
                             PillDivider()
                             RoundDropdownMenuItem(
-                                text = "替换规则",
+                                text = { Text("替换规则") },
                                 onClick = {
                                     onOpenReplaceRule(null)
                                     dismiss()
                                 }
                             )
                             RoundDropdownMenuItem(
-                                text = "新建替换规则",
+                                text = { Text("新建替换规则") },
                                 onClick = {
                                     val scopes = mutableListOf<String>()
                                     book?.name?.let { scopes.add(it) }
@@ -340,7 +340,7 @@ fun TocScreen(
                             if (book?.isLocal == true) {
                                 PillHeaderDivider(title = "本地书籍选项")
                                 RoundDropdownMenuItem(
-                                    text = "本地书籍目录规则",
+                                    text = { Text("本地书籍目录规则") },
                                     onClick = {
                                         val intent =
                                             Intent(context, TxtTocRuleActivity::class.java).apply {
@@ -351,7 +351,7 @@ fun TocScreen(
                                     }
                                 )
                                 RoundDropdownMenuItem(
-                                    text = "拆分超长章节",
+                                    text = { Text("拆分超长章节") },
                                     trailingIcon = {
                                         Checkbox(
                                             checked = viewModel.isSplitLongChapter,
@@ -368,7 +368,7 @@ fun TocScreen(
 
                         else -> {
                             RoundDropdownMenuItem(
-                                text = "导出书签为JSON",
+                                text = { Text("导出书签为JSON") },
                                 onClick = {
                                     val dateFormat = SimpleDateFormat(
                                         "yyyyMMdd_HHmm",
@@ -380,7 +380,7 @@ fun TocScreen(
                                 }
                             )
                             RoundDropdownMenuItem(
-                                text = "导出书签为MarkDown",
+                                text = { Text("导出书签为MarkDown") },
                                 onClick = {
                                     val dateFormat = SimpleDateFormat(
                                         "yyyyMMdd_HHmm",
@@ -445,13 +445,13 @@ fun TocScreen(
                                     onDismissRequest = { showVolumeMenu = false }
                                 ) {
                                     RoundDropdownMenuItem(
-                                        text = "展开所有卷",
+                                        text = { Text("展开所有卷") },
                                         onClick = {
                                             viewModel.expandAllVolumes(); showVolumeMenu = false
                                         }
                                     )
                                     RoundDropdownMenuItem(
-                                        text = "收起所有卷",
+                                        text = { Text("收起所有卷") },
                                         onClick = {
                                             viewModel.collapseAllVolumes(); showVolumeMenu = false
                                         }
@@ -463,7 +463,7 @@ fun TocScreen(
                                         PillHeaderDivider(title = "快速跳转")
                                         volumeItems.forEach { uiItem ->
                                             RoundDropdownMenuItem(
-                                                text = uiItem.title,
+                                                text = { Text(uiItem.title) },
                                                 onClick = {
                                                     scope.launch {
                                                         val targetIndex =

+ 16 - 11
app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt

@@ -22,7 +22,7 @@ import io.legado.app.databinding.DialogEditTextBinding
 import io.legado.app.databinding.DialogImageBlurringBinding
 import io.legado.app.help.LauncherIconHelp
 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.selector
 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.theme.ThemeStore
 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.seekbar.SeekBarChangeListener
 import io.legado.app.utils.ColorUtils
@@ -181,13 +181,20 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
             }
 
 
-            PreferKey.paletteStyle, PreferKey.pureBlack, PreferKey.enableBlur, PreferKey.swipeAnimation -> {
-                ThemeSyncer.syncAll()
+            PreferKey.paletteStyle, PreferKey.pureBlack, PreferKey.swipeAnimation -> {
                 Handler(Looper.getMainLooper()).postDelayed({
                     recreateActivities()
                 }, 100)
             }
 
+            PreferKey.enableBlur -> {
+                ThemeConfig.enableBlur = PreferKey.enableBlur.toBoolean()
+            }
+
+            PreferKey.enableProgressiveBlur -> {
+                ThemeConfig.enableProgressiveBlur = PreferKey.enableProgressiveBlur.toBoolean()
+            }
+
             PreferKey.customMode -> handleRestartRequired()
 
             PreferKey.isPredictiveBackEnabled -> {
@@ -263,17 +270,15 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
                 .setTitle(getString(R.string.container_opacity))
                 .setMaxValue(100)
                 .setMinValue(0)
-                .setValue(AppConfig.containerOpacity)
+                .setValue(ThemeConfig.containerOpacity)
                 .setCustomButton((R.string.btn_default_s)) {
-                    AppConfig.containerOpacity = 100
-                    ThemeSyncer.syncContainerOpacity()
+                    ThemeConfig.containerOpacity = 100
                     Handler(Looper.getMainLooper()).postDelayed({
                         upPreferenceSummary(PreferKey.containerOpacity)
                     }, 100)
                 }
                 .show {
-                    AppConfig.containerOpacity = it
-                    ThemeSyncer.syncContainerOpacity()
+                    ThemeConfig.containerOpacity = it
                     Handler(Looper.getMainLooper()).postDelayed({
                         upPreferenceSummary(PreferKey.containerOpacity)
                     }, 100)
@@ -305,11 +310,11 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
                 alertBinding.editView.text?.toString()?.let { themeName ->
                     when (key) {
                         "saveDayTheme" -> {
-                            ThemeConfig.saveDayTheme(requireContext(), themeName)
+                            OldThemeConfig.saveDayTheme(requireContext(), themeName)
                         }
 
                         "saveNightTheme" -> {
-                            ThemeConfig.saveNightTheme(requireContext(), themeName)
+                            OldThemeConfig.saveNightTheme(requireContext(), themeName)
                         }
                     }
                 }

+ 11 - 10
app/src/main/java/io/legado/app/ui/config/ThemeListDialog.kt

@@ -2,7 +2,6 @@ package io.legado.app.ui.config
 
 import android.content.Context
 import android.os.Bundle
-import android.util.Log
 import android.view.MenuItem
 import android.view.View
 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.databinding.DialogRecyclerViewBinding
 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.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.viewbindingdelegate.viewBinding
 
@@ -54,14 +52,14 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
     }
 
     fun initData() {
-        adapter.setItems(ThemeConfig.configList)
+        adapter.setItems(OldThemeConfig.configList)
     }
 
     override fun onMenuItemClick(item: MenuItem?): Boolean {
         when (item?.itemId) {
             R.id.menu_import -> {
                 requireContext().getClipText()?.let {
-                    if (ThemeConfig.addConfig(it)) {
+                    if (OldThemeConfig.addConfig(it)) {
                         initData()
                     } else {
                         toastOnUi("格式不对,添加失败")
@@ -75,7 +73,7 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
     fun delete(index: Int) {
         alert(R.string.delete, R.string.sure_del) {
             yesButton {
-                ThemeConfig.delConfig(index)
+                OldThemeConfig.delConfig(index)
                 initData()
             }
             noButton()
@@ -83,12 +81,12 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
     }
 
     fun share(index: Int) {
-        val json = GSON.toJson(ThemeConfig.configList[index])
+        val json = GSON.toJson(OldThemeConfig.configList[index])
         requireContext().share(json, "主题分享")
     }
 
     inner class Adapter(context: Context) :
-        RecyclerAdapter<ThemeConfig.Config, ItemThemeConfigBinding>(context) {
+        RecyclerAdapter<OldThemeConfig.Config, ItemThemeConfigBinding>(context) {
 
         override fun getViewBinding(parent: ViewGroup): ItemThemeConfigBinding {
             return ItemThemeConfigBinding.inflate(inflater, parent, false)
@@ -97,7 +95,7 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
         override fun convert(
             holder: ItemViewHolder,
             binding: ItemThemeConfigBinding,
-            item: ThemeConfig.Config,
+            item: OldThemeConfig.Config,
             payloads: MutableList<Any>
         ) {
             binding.apply {
@@ -113,7 +111,10 @@ class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
         override fun registerListener(holder: ItemViewHolder, binding: ItemThemeConfigBinding) {
             binding.apply {
                 root.setOnClickListener {
-                    ThemeConfig.applyConfig(context, ThemeConfig.configList[holder.layoutPosition])
+                    OldThemeConfig.applyConfig(
+                        context,
+                        OldThemeConfig.configList[holder.layoutPosition]
+                    )
                 }
                 ivShare.setOnClickListener {
                     share(holder.layoutPosition)

+ 2 - 2
app/src/main/java/io/legado/app/ui/config/otherConfig/OtherConfigScreen.kt

@@ -34,7 +34,7 @@ import io.legado.app.R
 import io.legado.app.service.WebService
 import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
 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.settingItem.ClickableSettingItem
 import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem
@@ -89,7 +89,7 @@ fun OtherConfigScreen(
                 },
                 scrollBehavior = scrollBehavior,
                 navigationIcon = {
-                    SmallTopBarButton(onClick = onBackClick)
+                    TopbarNavigationButton(onClick = onBackClick)
                 }
             )
         }

+ 31 - 0
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfig.kt

@@ -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()
+        }
+    }
+}

+ 9 - 8
app/src/main/java/io/legado/app/ui/replace/ReplaceRuleScreen.kt

@@ -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.SourceInputDialog
 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 kotlinx.coroutines.launch
 import org.koin.androidx.compose.koinViewModel
@@ -387,35 +388,35 @@ fun ReplaceRuleScreen(
         },
         snackbarHostState = snackbarHostState,
         dropDownMenuContent = { dismiss ->
-            DropdownMenuItem(
+            RoundDropdownMenuItem(
                 text = { Text("在线导入") },
                 onClick = {
                     dismiss()
                     showUrlInput = true // 触发输入框
                 }
             )
-            DropdownMenuItem(
+            RoundDropdownMenuItem(
                 text = { Text("本地导入") },
                 onClick = { importDoc.launch(arrayOf("text/plain", "application/json")); dismiss() }
             )
-            DropdownMenuItem(
+            RoundDropdownMenuItem(
                 text = { Text("分组管理") },
                 onClick = { showGroupManageSheet = true; dismiss() }
             )
-            DropdownMenuItem(
+            RoundDropdownMenuItem(
                 text = { Text("帮助") },
                 onClick = { /*TODO*/ dismiss() }
             )
             HorizontalDivider()
-            DropdownMenuItem(
+            RoundDropdownMenuItem(
                 text = { Text("旧的在前") },
                 onClick = { viewModel.setSortMode("asc"); dismiss() }
             )
-            DropdownMenuItem(
+            RoundDropdownMenuItem(
                 text = { Text("新的在前") },
                 onClick = { viewModel.setSortMode("desc"); dismiss() }
             )
-            DropdownMenuItem(
+            RoundDropdownMenuItem(
                 text = { Text("名称升序") },
                 onClick = {
                     viewModel.setSortMode("name_asc")
@@ -425,7 +426,7 @@ fun ReplaceRuleScreen(
                     }
                 }
             )
-            DropdownMenuItem(
+            RoundDropdownMenuItem(
                 text = { Text("名称降序") },
                 onClick = {
                     viewModel.setSortMode("name_desc")

+ 2 - 2
app/src/main/java/io/legado/app/ui/replace/edit/ReplaceEditScreen.kt

@@ -81,7 +81,7 @@ import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
 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.SmallTopBarButton
+import io.legado.app.ui.widget.components.button.TopbarNavigationButton
 import org.koin.androidx.compose.koinViewModel
 
 @Composable
@@ -121,7 +121,7 @@ fun ReplaceEditScreen(
                 GlassMediumFlexibleTopAppBar(
                     title = { Text(if (state.id > 0) "编辑替换规则" else "新增替换规则") },
                     navigationIcon = {
-                        SmallTopBarButton(onClick = onBack)
+                        TopbarNavigationButton(onClick = onBack)
                     },
                     actions = {
                         AnimatedVisibility(

+ 19 - 14
app/src/main/java/io/legado/app/ui/theme/AppTheme.kt

@@ -7,8 +7,9 @@ import androidx.compose.material3.MotionScheme
 import androidx.compose.material3.Shapes
 import androidx.compose.material3.Typography
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import io.legado.app.ui.config.themeConfig.ThemeConfig
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
@@ -16,18 +17,23 @@ fun AppTheme(
     darkTheme: Boolean = isSystemInDarkTheme(),
     content: @Composable () -> Unit
 ) {
-    val appThemeMode by ThemeState.themeMode.collectAsState()
-    val isPureBlack by ThemeState.isPureBlack.collectAsState()
-    val hasImageBg by ThemeState.hasImageBg.collectAsState()
-    val paletteStyle by ThemeState.paletteStyle.collectAsState()
+    val context = LocalContext.current
+    val appThemeMode = ThemeResolver.resolveThemeMode(ThemeConfig.appTheme)
+    val isPureBlack = ThemeConfig.isPureBlack
+    val hasImageBg = ThemeConfig.hasImageBg(darkTheme)
+    val paletteStyle = ThemeConfig.paletteStyle
 
-    val colorScheme = ThemeManager.getColorScheme(
-        mode = appThemeMode,
-        darkTheme = darkTheme,
-        isAmoled = isPureBlack,
-        isImageBg = hasImageBg,
-        paletteStyle = paletteStyle
-    )
+    val colorScheme =
+        remember(context, appThemeMode, darkTheme, isPureBlack, hasImageBg, paletteStyle) {
+            ThemeManager.getColorScheme(
+                context = context,
+                mode = appThemeMode,
+                darkTheme = darkTheme,
+                isAmoled = isPureBlack,
+                isImageBg = hasImageBg,
+                paletteStyle = paletteStyle
+            )
+        }
 
     MaterialExpressiveTheme(
         colorScheme = colorScheme,
@@ -36,5 +42,4 @@ fun AppTheme(
         shapes = Shapes(),
         content = content
     )
-
 }

+ 0 - 4
app/src/main/java/io/legado/app/ui/theme/BaseColorScheme.kt

@@ -1,15 +1,11 @@
 package io.legado.app.ui.theme
 
 import androidx.compose.material3.ColorScheme
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ReadOnlyComposable
 
 abstract class BaseColorScheme {
     abstract val lightScheme: ColorScheme
     abstract val darkScheme: ColorScheme
 
-    @Composable
-    @ReadOnlyComposable
     fun getColorScheme(darkTheme: Boolean): ColorScheme {
         return if (darkTheme) darkScheme else lightScheme
     }

+ 31 - 0
app/src/main/java/io/legado/app/ui/theme/OpaqueColorScheme.kt

@@ -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
+        )
+    }
+}

+ 5 - 6
app/src/main/java/io/legado/app/ui/theme/ThemeManager.kt

@@ -1,13 +1,11 @@
 package io.legado.app.ui.theme
 
+import android.content.Context
 import android.os.Build
-import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.material3.ColorScheme
 import androidx.compose.material3.dynamicDarkColorScheme
 import androidx.compose.material3.dynamicLightColorScheme
-import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalContext
 import io.legado.app.lib.theme.primaryColor
 import io.legado.app.ui.theme.ThemeResolver.resolvePaletteStyle
 import io.legado.app.ui.theme.colorScheme.AugustColorScheme
@@ -40,16 +38,17 @@ object ThemeManager {
         AppThemeMode.Transparent to TransparentColorScheme,
     )
 
-    @Composable
+
     fun getColorScheme(
+        context: Context,
         mode: AppThemeMode,
-        darkTheme: Boolean = isSystemInDarkTheme(),
+        darkTheme: Boolean,
         isAmoled: Boolean,
         isImageBg: Boolean,
         paletteStyle: String?,
         forceOpaque: Boolean = false
     ): ColorScheme {
-        val context = LocalContext.current
+
         val style = resolvePaletteStyle(paletteStyle)
         val actualMode = if (forceOpaque && mode == AppThemeMode.Transparent) {
             AppThemeMode.WH

+ 0 - 73
app/src/main/java/io/legado/app/ui/theme/ThemeState.kt

@@ -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
-        }
-    }
-
-}

+ 0 - 45
app/src/main/java/io/legado/app/ui/theme/ThemeSyncer.kt

@@ -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)
-    }
-
-}

+ 44 - 27
app/src/main/java/io/legado/app/ui/widget/components/GlassMediumFlexibleTopAppBar.kt

@@ -5,14 +5,13 @@ import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.MediumFlexibleTopAppBar
+import androidx.compose.material3.TopAppBarColors
 import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import io.legado.app.ui.theme.ThemeState
+import io.legado.app.ui.config.themeConfig.ThemeConfig
 
 @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 @Composable
@@ -22,30 +21,9 @@ fun GlassMediumFlexibleTopAppBar(
     subtitle: (@Composable () -> Unit)? = null,
     scrollBehavior: TopAppBarScrollBehavior? = null,
     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(
         modifier = modifier,
         title = title,
@@ -53,7 +31,46 @@ fun GlassMediumFlexibleTopAppBar(
         navigationIcon = navigationIcon,
         actions = actions,
         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)
+        }
+    }
 
 }

+ 48 - 9
app/src/main/java/io/legado/app/ui/widget/components/button/SmallTopBarButton.kt → app/src/main/java/io/legado/app/ui/widget/components/button/TopBarButton.kt

@@ -17,31 +17,26 @@ import androidx.compose.material3.IconButtonDefaults
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.OutlinedIconButton
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 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 {
     Filled, Outlined, Icon
 }
 
 @Composable
-fun SmallTopBarButton(
+fun TopBarButton(
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
     imageVector: ImageVector = Icons.AutoMirrored.Filled.ArrowBack,
     contentDescription: String? = null,
     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
-        .padding(horizontal = 12.dp)
         .size(36.dp)
 
     when (style) {
@@ -50,7 +45,7 @@ fun SmallTopBarButton(
                 onClick = onClick,
                 modifier = commonModifier,
                 colors = IconButtonDefaults.filledTonalIconButtonColors(
-                    containerColor = glassColor,
+                    containerColor = GlassTopAppBarDefaults.controlContainerColor(),
                     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
 private fun SmallIcon(imageVector: ImageVector, contentDescription: String?) {
     AnimatedContent(

+ 2 - 4
app/src/main/java/io/legado/app/ui/widget/components/card/GlassCard.kt

@@ -7,12 +7,10 @@ import androidx.compose.material3.CardDefaults
 import androidx.compose.material3.CardElevation
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
-import io.legado.app.ui.theme.ThemeState
+import io.legado.app.ui.config.themeConfig.ThemeConfig
 
 @Composable
 fun GlassCard(
@@ -24,7 +22,7 @@ fun GlassCard(
     border: BorderStroke? = null,
     content: @Composable ColumnScope.() -> Unit
 ) {
-    val opacity by ThemeState.containerOpacity.collectAsState()
+    val opacity = ThemeConfig.containerOpacity
     val cardColors = CardDefaults.cardColors(
         containerColor = color.copy(alpha = opacity / 100f)
     )

+ 22 - 4
app/src/main/java/io/legado/app/ui/widget/components/menuItem/RoundDropdownMenu.kt

@@ -4,13 +4,21 @@ import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.MaterialExpressiveTheme
 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.ui.Modifier
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import io.legado.app.ui.theme.rememberOpaqueColorScheme
 
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 fun RoundDropdownMenu(
     expanded: Boolean,
@@ -21,17 +29,27 @@ fun RoundDropdownMenu(
     verticalSpacing: Dp = 8.dp,
     content: @Composable ColumnScope.(dismiss: () -> Unit) -> Unit
 ) {
+    val colorScheme = rememberOpaqueColorScheme()
+
     DropdownMenu(
         expanded = expanded,
         onDismissRequest = onDismissRequest,
         modifier = modifier,
         shape = shape,
-        shadowElevation = shadowElevation
+        shadowElevation = shadowElevation,
+        containerColor = colorScheme.surfaceContainerLow
     ) {
-        Column(
-            verticalArrangement = Arrangement.spacedBy(verticalSpacing)
+        MaterialExpressiveTheme(
+            colorScheme = colorScheme,
+            typography = Typography(),
+            motionScheme = MotionScheme.expressive(),
+            shapes = Shapes()
         ) {
-            content(onDismissRequest)
+            Column(
+                verticalArrangement = Arrangement.spacedBy(verticalSpacing)
+            ) {
+                content(onDismissRequest)
+            }
         }
     }
 }

+ 7 - 5
app/src/main/java/io/legado/app/ui/widget/components/menuItem/RoundDropdownMenuItem.kt

@@ -4,26 +4,28 @@ import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.DropdownMenuItem
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.unit.dp
+import io.legado.app.ui.theme.rememberOpaqueColorScheme
 
 @Composable
 fun RoundDropdownMenuItem(
     modifier: Modifier = Modifier,
-    text: String,
+    text: @Composable () -> Unit,
     onClick: () -> Unit,
     trailingIcon: @Composable (() -> Unit)? = null,
 ) {
+    val colorScheme = rememberOpaqueColorScheme()
+
     DropdownMenuItem(
         modifier = modifier
             .padding(horizontal = 8.dp)
             .clip(MaterialTheme.shapes.small)
-            .background(MaterialTheme.colorScheme.surface),
-        text = { Text(text) },
+            .background(colorScheme.surface),
+        text = text,
         trailingIcon = trailingIcon,
-        onClick = onClick,
+        onClick = onClick
     )
 }

+ 2 - 16
app/src/main/java/io/legado/app/ui/widget/components/modalBottomSheet/GlassModalBottomSheet.kt

@@ -15,12 +15,9 @@ import androidx.compose.material3.SheetState
 import androidx.compose.material3.Typography
 import androidx.compose.material3.rememberModalBottomSheetState
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import io.legado.app.ui.theme.ThemeManager
-import io.legado.app.ui.theme.ThemeState
+import io.legado.app.ui.theme.rememberOpaqueColorScheme
 
 @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
 @Composable
@@ -31,18 +28,7 @@ fun GlassModalBottomSheet(
     containerColor: (ColorScheme) -> Color = { it.surfaceContainerLow },
     content: @Composable ColumnScope.() -> Unit
 ) {
-    val themeMode by ThemeState.themeMode.collectAsState()
-    val isPureBlack by ThemeState.isPureBlack.collectAsState()
-    val hasImageBg by ThemeState.hasImageBg.collectAsState()
-    val paletteStyle by ThemeState.paletteStyle.collectAsState()
-
-    val colorScheme = ThemeManager.getColorScheme(
-        mode = themeMode,
-        isAmoled = isPureBlack,
-        isImageBg = hasImageBg,
-        forceOpaque = true,
-        paletteStyle = paletteStyle
-    )
+    val colorScheme = rememberOpaqueColorScheme()
 
     ModalBottomSheet(
         onDismissRequest = onDismissRequest,

+ 10 - 6
app/src/main/java/io/legado/app/ui/widget/components/swipe/SwipeActionContainer.kt

@@ -12,6 +12,7 @@ import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
 import androidx.compose.material3.SwipeToDismissBox
+import androidx.compose.material3.SwipeToDismissBoxState
 import androidx.compose.material3.SwipeToDismissBoxValue
 import androidx.compose.material3.contentColorFor
 import androidx.compose.material3.rememberSwipeToDismissBoxState
@@ -79,8 +80,8 @@ fun SwipeActionContainer(
                 SwipeToDismissBoxValue.StartToEnd -> {
                     startAction?.let { action ->
                         SwipeBackground(
+                            dismissState = dismissState,
                             action = action,
-                            progress = progress,
                             alignStart = true
                         )
                     }
@@ -89,8 +90,8 @@ fun SwipeActionContainer(
                 SwipeToDismissBoxValue.EndToStart -> {
                     endAction?.let { action ->
                         SwipeBackground(
+                            dismissState = dismissState,
                             action = action,
-                            progress = progress,
                             alignStart = false
                         )
                     }
@@ -114,10 +115,13 @@ fun SwipeActionContainer(
 @Composable
 private fun SwipeBackground(
     action: SwipeAction,
-    progress: Float,
+    dismissState: SwipeToDismissBoxState,
     alignStart: Boolean
 ) {
-    val isThresholdReached = progress > 0.6f
+    val isThresholdReached = dismissState.targetValue == if (alignStart)
+        SwipeToDismissBoxValue.StartToEnd
+    else
+        SwipeToDismissBoxValue.EndToStart
 
     val backgroundColor by animateColorAsState(
         targetValue = if (isThresholdReached)
@@ -128,8 +132,8 @@ private fun SwipeBackground(
     )
 
     val iconScale by animateFloatAsState(
-        targetValue = if (isThresholdReached) 1.3f
-        else progress.coerceIn(0.5f, 1f),
+        targetValue = if (isThresholdReached) 1.2f
+        else 1f,
         label = "iconScale"
     )
 

+ 2 - 2
app/src/main/java/io/legado/app/ui/widget/components/topbar/DynamicTopAppBar.kt

@@ -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.GlassMediumFlexibleTopAppBar
 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.rules.RuleActionState
 
@@ -75,7 +75,7 @@ fun <T> DynamicTopAppBar(
                 { AnimatedTextLine(text = it) }
             },
             navigationIcon = {
-                SmallTopBarButton(
+                TopbarNavigationButton(
                     onClick = { if (isSelecting) onClearSelection() else onBackClick() },
                     imageVector = if (isSelecting) Icons.Default.Close else Icons.AutoMirrored.Filled.ArrowBack,
                     contentDescription = if (isSelecting) "取消选择" else "返回"

+ 1 - 0
app/src/main/res/values-zh-rCN/strings.xml

@@ -1386,4 +1386,5 @@
     <string name="show_swipe_animation">启用滑动时动画</string>
     <string name="click_image_way">点击图片行为</string>
     <string name="show_web_log">输出日志</string>
+    <string name="is_blur_progressive_enable">启用渐变模糊</string>
 </resources>

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -1390,4 +1390,5 @@
     <string name="show_swipe_animation">启用滑动时动画</string>
     <string name="click_image_way">点击图片行为</string>
     <string name="show_web_log">输出日志</string>
+    <string name="is_blur_progressive_enable">启用渐变模糊</string>
 </resources>

+ 6 - 0
app/src/main/res/xml/pref_config_theme.xml

@@ -116,6 +116,12 @@
             android:title="@string/is_blur_enable"
             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