Parcourir la source

refactor: 消除编译弃用警告(421→72)

- 九个弃用 *Config 门面(AppConfig/ReadConfig/ThemeConfig/OtherConfig/
  BackupConfig/ChangeSourceConfig/BookshelfConfig/CoverConfig/ReadMangaConfig)
  的全部调用点迁移至对应 SettingsGateway.currentSettings:
  ViewModel 构造注入,其余按既有约定 GlobalContext 查找
- DatabaseMigrations migrate 参数名统一为 db(36 处)
- 弃用图标替换为 AutoMirrored 变体(16 处)
- Compose M3 弃用:ListItem 尾随 lambda 重载、TextFieldLabelPosition.Inside、
  ExposedDropdownMenuAnchorType、LocalClipboard(suspend 版)
- Kotlin 清理:多余安全调用/非空断言、恒真条件、PersistentMap.putting、
  OVERRIDE_DEPRECATION 补注
- Java 平台类型不匹配真修(AbsCallBack/ObsoleteUrlFactory/CronetLoader/RequestManager)

剩余 72 条警告均为约定排除项:androidx.media Compat(61,待 media3 迁移)
与平台 API 弃用(11,待 WindowInsetsController 等改造)。

验证::app:compileAppDebugKotlin 全量重编译 0 错误;
testAppDebugUnitTest 927 例仅存量 3 例失败(stash 基线确认与本次无关);
verifyConfigArchitecture 通过;git diff --check 通过。
HapeLee il y a 2 mois
Parent
commit
78079d9ac9
100 fichiers modifiés avec 824 ajouts et 572 suppressions
  1. 15 5
      app/src/main/java/io/legado/app/App.kt
  2. 5 2
      app/src/main/java/io/legado/app/api/controller/BookController.kt
  3. 5 2
      app/src/main/java/io/legado/app/base/AppContextWrapper.kt
  4. 13 5
      app/src/main/java/io/legado/app/base/BaseActivity.kt
  5. 6 3
      app/src/main/java/io/legado/app/base/BaseDialogFragment.kt
  6. 5 2
      app/src/main/java/io/legado/app/base/BasePrefDialogFragment.kt
  7. 5 2
      app/src/main/java/io/legado/app/constant/AppLog.kt
  8. 141 141
      app/src/main/java/io/legado/app/data/DatabaseMigrations.kt
  9. 2 1
      app/src/main/java/io/legado/app/di/appModule.kt
  10. 1 0
      app/src/main/java/io/legado/app/domain/model/AiMessageParts.kt
  11. 6 3
      app/src/main/java/io/legado/app/help/AppFreezeMonitor.kt
  12. 14 12
      app/src/main/java/io/legado/app/help/AppWebDav.kt
  13. 10 3
      app/src/main/java/io/legado/app/help/CrashHandler.kt
  14. 5 2
      app/src/main/java/io/legado/app/help/DispatchersMonitor.kt
  15. 14 9
      app/src/main/java/io/legado/app/help/JsExtensions.kt
  16. 12 9
      app/src/main/java/io/legado/app/help/WebDavManager.kt
  17. 16 9
      app/src/main/java/io/legado/app/help/book/BookExtensions.kt
  18. 11 7
      app/src/main/java/io/legado/app/help/book/BookHelp.kt
  19. 13 8
      app/src/main/java/io/legado/app/help/book/ContentProcessor.kt
  20. 1 2
      app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt
  21. 22 5
      app/src/main/java/io/legado/app/help/config/ReadStyleResolver.kt
  22. 41 18
      app/src/main/java/io/legado/app/help/config/ThemeConfigStore.kt
  23. 3 2
      app/src/main/java/io/legado/app/help/glide/LegadoGlideModule.kt
  24. 3 2
      app/src/main/java/io/legado/app/help/http/BackstageWebView.kt
  25. 4 3
      app/src/main/java/io/legado/app/help/http/HttpHelper.kt
  26. 2 2
      app/src/main/java/io/legado/app/help/http/ObsoleteUrlFactory.kt
  27. 2 0
      app/src/main/java/io/legado/app/help/readaloud/playback/SystemTtsFileSynthesizer.kt
  28. 1 1
      app/src/main/java/io/legado/app/help/readaloud/playback/SystemTtsVoiceCatalog.kt
  29. 8 5
      app/src/main/java/io/legado/app/help/storage/Backup.kt
  30. 1 2
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  31. 3 2
      app/src/main/java/io/legado/app/help/update/AppUpdateGitHub.kt
  32. 3 3
      app/src/main/java/io/legado/app/lib/cronet/AbsCallBack.kt
  33. 1 1
      app/src/main/java/io/legado/app/lib/cronet/CronetLoader.kt
  34. 1 2
      app/src/main/java/io/legado/app/lib/permission/RequestManager.kt
  35. 3 2
      app/src/main/java/io/legado/app/lib/prefs/EditTextPreferenceDialog.kt
  36. 3 2
      app/src/main/java/io/legado/app/lib/prefs/ListPreferenceDialog.kt
  37. 3 2
      app/src/main/java/io/legado/app/lib/prefs/MultiSelectListPreferenceDialog.kt
  38. 2 3
      app/src/main/java/io/legado/app/lib/prefs/ThemeCardPreference.kt
  39. 8 3
      app/src/main/java/io/legado/app/model/AudioPlay.kt
  40. 20 9
      app/src/main/java/io/legado/app/model/BookCover.kt
  41. 5 2
      app/src/main/java/io/legado/app/model/CacheBook.kt
  42. 8 3
      app/src/main/java/io/legado/app/model/ImageProvider.kt
  43. 3 3
      app/src/main/java/io/legado/app/model/ReadAloud.kt
  44. 21 17
      app/src/main/java/io/legado/app/model/ReadBook.kt
  45. 4 2
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
  46. 6 4
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
  47. 17 11
      app/src/main/java/io/legado/app/model/localBook/LocalBook.kt
  48. 6 3
      app/src/main/java/io/legado/app/model/localBook/TextFile.kt
  49. 5 2
      app/src/main/java/io/legado/app/model/remote/RemoteBookWebDav.kt
  50. 13 7
      app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt
  51. 7 4
      app/src/main/java/io/legado/app/model/webBook/BookContent.kt
  52. 6 3
      app/src/main/java/io/legado/app/receiver/MediaButtonReceiver.kt
  53. 10 7
      app/src/main/java/io/legado/app/service/AudioPlayService.kt
  54. 4 2
      app/src/main/java/io/legado/app/service/CacheBookService.kt
  55. 9 8
      app/src/main/java/io/legado/app/ui/about/AboutViewModel.kt
  56. 2 3
      app/src/main/java/io/legado/app/ui/about/CrashLogsDialog.kt
  57. 3 2
      app/src/main/java/io/legado/app/ui/about/UpdateDialog.kt
  58. 9 4
      app/src/main/java/io/legado/app/ui/ai/chat/AiChatScreen.kt
  59. 2 2
      app/src/main/java/io/legado/app/ui/ai/chat/AiChatViewModel.kt
  60. 3 4
      app/src/main/java/io/legado/app/ui/association/ImportRssSourceDialog.kt
  61. 5 4
      app/src/main/java/io/legado/app/ui/association/ImportRssSourceViewModel.kt
  62. 7 8
      app/src/main/java/io/legado/app/ui/book/bookmark/AllBookmarkScreen.kt
  63. 3 2
      app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt
  64. 4 2
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceAdapter.kt
  65. 13 10
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceDialog.kt
  66. 15 12
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt
  67. 3 4
      app/src/main/java/io/legado/app/ui/book/info/BookInfoBackdropStyle.kt
  68. 1 2
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  69. 10 6
      app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreenViewModel.kt
  70. 1 1
      app/src/main/java/io/legado/app/ui/book/manga/MangaReaderViewModel.kt
  71. 12 10
      app/src/main/java/io/legado/app/ui/book/read/ReadBookController.kt
  72. 2 2
      app/src/main/java/io/legado/app/ui/book/read/ReadBookFloatingActionBar.kt
  73. 3 3
      app/src/main/java/io/legado/app/ui/book/read/ReadBookMenuBarFloating.kt
  74. 3 3
      app/src/main/java/io/legado/app/ui/book/read/ReadBookMenuBarTitle.kt
  75. 2 2
      app/src/main/java/io/legado/app/ui/book/read/sheet/CharsetConfigSheet.kt
  76. 3 3
      app/src/main/java/io/legado/app/ui/book/read/sheet/ReaderMoreActionsSheet.kt
  77. 14 14
      app/src/main/java/io/legado/app/ui/book/read/sheet/TypographyTabs.kt
  78. 2 3
      app/src/main/java/io/legado/app/ui/book/readaloud/player/ReadAloudPlayerCoordinator.kt
  79. 3 2
      app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt
  80. 3 3
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentAdapter.kt
  81. 7 8
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt
  82. 1 1
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceScreen.kt
  83. 3 3
      app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt
  84. 1 0
      app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt
  85. 1 1
      app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleViewModel.kt
  86. 6 2
      app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfManageScreenConfig.kt
  87. 13 8
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
  88. 8 9
      app/src/main/java/io/legado/app/ui/main/explore/ExploreScreen.kt
  89. 1 1
      app/src/main/java/io/legado/app/ui/main/homepage/modules/ButtonGroupModule.kt
  90. 7 3
      app/src/main/java/io/legado/app/ui/replace/ReplaceRuleViewModel.kt
  91. 3 3
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
  92. 2 2
      app/src/main/java/io/legado/app/ui/rss/read/RssReadRouteScreen.kt
  93. 2 2
      app/src/main/java/io/legado/app/ui/widget/BatteryViewOrgin.kt
  94. 3 3
      app/src/main/java/io/legado/app/ui/widget/components/AppTextField.kt
  95. 8 9
      app/src/main/java/io/legado/app/ui/widget/components/card/SelectionItemCard.kt
  96. 14 3
      app/src/main/java/io/legado/app/ui/widget/dialog/BottomWebViewDialog.kt
  97. 27 14
      app/src/main/java/io/legado/app/ui/widget/image/CoverImageView.kt
  98. 7 3
      app/src/main/java/io/legado/app/utils/AnimationExtensions.kt
  99. 17 10
      app/src/main/java/io/legado/app/utils/ContextExtensions.kt
  100. 7 2
      app/src/main/java/io/legado/app/utils/CustomExportUtils.kt

+ 15 - 5
app/src/main/java/io/legado/app/App.kt

@@ -39,7 +39,10 @@ import io.legado.app.di.appModule
 import io.legado.app.domain.gateway.AppLocaleGateway
 import io.legado.app.domain.gateway.AppLocaleGateway
 import io.legado.app.domain.gateway.AppShellSettingsGateway
 import io.legado.app.domain.gateway.AppShellSettingsGateway
 import io.legado.app.domain.gateway.BackupSettingsGateway
 import io.legado.app.domain.gateway.BackupSettingsGateway
+import io.legado.app.domain.gateway.OtherSettingsGateway
+import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.domain.gateway.ReadStyleGateway
 import io.legado.app.domain.gateway.ReadStyleGateway
+import io.legado.app.domain.gateway.ThemeSettingsGateway
 import io.legado.app.help.AppFreezeMonitor
 import io.legado.app.help.AppFreezeMonitor
 import io.legado.app.help.AppWebDav
 import io.legado.app.help.AppWebDav
 import io.legado.app.help.CrashHandler
 import io.legado.app.help.CrashHandler
@@ -48,8 +51,8 @@ import io.legado.app.help.DispatchersMonitor
 import io.legado.app.help.LifecycleHelp
 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.AppConfigStore
 import io.legado.app.help.config.AppConfigStore
+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.ReadBookConfig
 import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.help.config.ThemeConfigStore
 import io.legado.app.help.config.ThemeConfigStore
@@ -87,6 +90,11 @@ import java.util.logging.Level
 
 
 class App : Application(), SingletonImageLoader.Factory {
 class App : Application(), SingletonImageLoader.Factory {
 
 
+    private val themeGateway get() = get<ThemeSettingsGateway>()
+    private val otherGateway get() = get<OtherSettingsGateway>()
+    private val backupGateway get() = get<BackupSettingsGateway>()
+    private val readGateway get() = get<ReadSettingsGateway>()
+
     override fun newImageLoader(context: Context): ImageLoader {
     override fun newImageLoader(context: Context): ImageLoader {
         return get()
         return get()
     }
     }
@@ -107,6 +115,7 @@ class App : Application(), SingletonImageLoader.Factory {
             androidContext(this@App)
             androidContext(this@App)
             modules(appDatabaseModule, appModule)
             modules(appDatabaseModule, appModule)
         }
         }
+        @Suppress("DEPRECATION")
         AppConfig.initialize(
         AppConfig.initialize(
             shellGateway = get(),
             shellGateway = get(),
             themeGateway = get(),
             themeGateway = get(),
@@ -129,7 +138,7 @@ class App : Application(), SingletonImageLoader.Factory {
         }
         }
         applyDayNightInit(this)
         applyDayNightInit(this)
         if (getPrefString("app_theme", "0") == "12") {
         if (getPrefString("app_theme", "0") == "12") {
-            if (AppConfig.customMode == "accent")
+            if (themeGateway.currentSettings.customMode == "accent")
                 setTheme(R.style.ThemeOverlay_WhiteBackground)
                 setTheme(R.style.ThemeOverlay_WhiteBackground)
 
 
             val colorImagePath = getPrefString(PreferKey.colorImage)
             val colorImagePath = getPrefString(PreferKey.colorImage)
@@ -196,7 +205,7 @@ class App : Application(), SingletonImageLoader.Factory {
             LiveEventBus.config()
             LiveEventBus.config()
                 .lifecycleObserverAlwaysActive(true)
                 .lifecycleObserverAlwaysActive(true)
                 .autoClear(false)
                 .autoClear(false)
-                .enableLogger(BuildConfig.DEBUG || AppConfig.recordLog)
+                .enableLogger(BuildConfig.DEBUG || otherGateway.currentSettings.recordLog)
                 .setLogger(EventLogger())
                 .setLogger(EventLogger())
             DefaultData.upVersion()
             DefaultData.upVersion()
             AppFreezeMonitor.init(this@App)
             AppFreezeMonitor.init(this@App)
@@ -218,7 +227,7 @@ class App : Application(), SingletonImageLoader.Factory {
             get<ReadStyleGateway>().clearUnusedBackgrounds()
             get<ReadStyleGateway>().clearUnusedBackgrounds()
             ThemeConfigStore.clearBg()
             ThemeConfigStore.clearBg()
             //初始化简繁转换引擎
             //初始化简繁转换引擎
-            when (AppConfig.chineseConverterType) {
+            when (readGateway.currentSettings.chineseConverterType) {
                 1 -> {
                 1 -> {
                     ChineseUtils.fixT2sDict()
                     ChineseUtils.fixT2sDict()
                     ChineseUtils.preLoad(true, TransType.TRADITIONAL_TO_SIMPLE)
                     ChineseUtils.preLoad(true, TransType.TRADITIONAL_TO_SIMPLE)
@@ -229,7 +238,7 @@ class App : Application(), SingletonImageLoader.Factory {
             //调整排序序号
             //调整排序序号
             SourceHelp.adjustSortNumber()
             SourceHelp.adjustSortNumber()
             //同步阅读记录
             //同步阅读记录
-            if (AppConfig.syncBookProgress) {
+            if (backupGateway.currentSettings.syncBookProgress) {
                 AppWebDav.upConfig()
                 AppWebDav.upConfig()
                 AppWebDav.downloadAllBookProgress()
                 AppWebDav.downloadAllBookProgress()
             }
             }
@@ -329,6 +338,7 @@ class App : Application(), SingletonImageLoader.Factory {
     }
     }
 
 
     private fun initRhino() {
     private fun initRhino() {
+        @Suppress("UNUSED_EXPRESSION")
         RhinoScriptEngine
         RhinoScriptEngine
         RhinoWrapFactory.register(BookSource::class.java, NativeBaseSource.factory)
         RhinoWrapFactory.register(BookSource::class.java, NativeBaseSource.factory)
         RhinoWrapFactory.register(RssSource::class.java, NativeBaseSource.factory)
         RhinoWrapFactory.register(RssSource::class.java, NativeBaseSource.factory)

+ 5 - 2
app/src/main/java/io/legado/app/api/controller/BookController.kt

@@ -14,7 +14,7 @@ import io.legado.app.help.CacheManager
 import io.legado.app.help.book.BookHelp
 import io.legado.app.help.book.BookHelp
 import io.legado.app.help.book.ContentProcessor
 import io.legado.app.help.book.ContentProcessor
 import io.legado.app.help.book.isLocal
 import io.legado.app.help.book.isLocal
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.BookshelfSettingsGateway
 import io.legado.app.help.glide.ImageLoader
 import io.legado.app.help.glide.ImageLoader
 import io.legado.app.model.BookCover
 import io.legado.app.model.BookCover
 import io.legado.app.model.ImageProvider
 import io.legado.app.model.ImageProvider
@@ -29,12 +29,15 @@ import io.legado.app.utils.stackTraceStr
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.runBlocking
 import splitties.init.appCtx
 import splitties.init.appCtx
+import org.koin.core.context.GlobalContext
 import java.io.File
 import java.io.File
 import java.util.WeakHashMap
 import java.util.WeakHashMap
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.TimeUnit
 
 
 object BookController {
 object BookController {
 
 
+    private val bookshelfGateway by lazy { GlobalContext.get().get<BookshelfSettingsGateway>() }
+
     private lateinit var book: Book
     private lateinit var book: Book
     private var bookSource: BookSource? = null
     private var bookSource: BookSource? = null
     private var bookUrl: String = ""
     private var bookUrl: String = ""
@@ -50,7 +53,7 @@ object BookController {
             return if (books.isEmpty()) {
             return if (books.isEmpty()) {
                 returnData.setErrorMsg("还没有添加小说")
                 returnData.setErrorMsg("还没有添加小说")
             } else {
             } else {
-                val data = when (AppConfig.bookshelfSort) {
+                val data = when (bookshelfGateway.currentSettings.bookshelfSort) {
                     1 -> books.sortedByDescending { it.latestChapterTime }
                     1 -> books.sortedByDescending { it.latestChapterTime }
                     2 -> books.sortedWith { o1, o2 ->
                     2 -> books.sortedWith { o1, o2 ->
                         o1.name.cnCompare(o2.name)
                         o1.name.cnCompare(o2.name)

+ 5 - 2
app/src/main/java/io/legado/app/base/AppContextWrapper.kt

@@ -3,13 +3,16 @@ package io.legado.app.base
 import android.app.Activity
 import android.app.Activity
 import android.content.Context
 import android.content.Context
 import android.content.res.Configuration
 import android.content.res.Configuration
-import io.legado.app.ui.config.themeConfig.ThemeConfig
+import io.legado.app.domain.gateway.AppShellSettingsGateway
 import io.legado.app.ui.theme.resolveAppFontScale
 import io.legado.app.ui.theme.resolveAppFontScale
+import org.koin.core.context.GlobalContext
 
 
 
 
 @Suppress("unused")
 @Suppress("unused")
 object AppContextWrapper {
 object AppContextWrapper {
 
 
+    private val shellGateway get() = GlobalContext.get().get<AppShellSettingsGateway>()
+
     fun applyFont(activity: Activity) {
     fun applyFont(activity: Activity) {
         val config = activity.resources.configuration
         val config = activity.resources.configuration
         val fontScale = getFontScale(activity)
         val fontScale = getFontScale(activity)
@@ -22,6 +25,6 @@ object AppContextWrapper {
     }
     }
 
 
     fun getFontScale(context: Context): Float =
     fun getFontScale(context: Context): Float =
-        resolveAppFontScale(ThemeConfig.fontScale)
+        resolveAppFontScale(shellGateway.currentSettings.fontScale)
 
 
 }
 }

+ 13 - 5
app/src/main/java/io/legado/app/base/BaseActivity.kt

@@ -30,10 +30,11 @@ import io.legado.app.BuildConfig
 import io.legado.app.constant.AppLog
 import io.legado.app.constant.AppLog
 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.ThemeConfigStore
 import io.legado.app.help.config.ThemeConfigStore
 import io.legado.app.domain.gateway.AppLocaleGateway
 import io.legado.app.domain.gateway.AppLocaleGateway
+import io.legado.app.domain.gateway.AppShellSettingsGateway
 import io.legado.app.domain.gateway.AppUiConfigurationGateway
 import io.legado.app.domain.gateway.AppUiConfigurationGateway
+import io.legado.app.domain.gateway.ThemeSettingsGateway
 import io.legado.app.domain.model.settings.AppUiConfiguration
 import io.legado.app.domain.model.settings.AppUiConfiguration
 import io.legado.app.domain.model.settings.AppUiConfigurationDiff
 import io.legado.app.domain.model.settings.AppUiConfigurationDiff
 import io.legado.app.domain.model.settings.diffFrom
 import io.legado.app.domain.model.settings.diffFrom
@@ -47,6 +48,7 @@ import io.legado.app.utils.getPrefString
 import io.legado.app.utils.hideSoftInput
 import io.legado.app.utils.hideSoftInput
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.setStatusBarColorAuto
 import io.legado.app.utils.setStatusBarColorAuto
+import io.legado.app.utils.sysConfiguration
 import io.legado.app.utils.themeColor
 import io.legado.app.utils.themeColor
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.windowSize
 import io.legado.app.utils.windowSize
@@ -67,6 +69,8 @@ abstract class BaseActivity<VB : ViewBinding>(
 
 
     private val appLocaleGateway by inject<AppLocaleGateway>()
     private val appLocaleGateway by inject<AppLocaleGateway>()
     private val appUiConfigurationGateway by inject<AppUiConfigurationGateway>()
     private val appUiConfigurationGateway by inject<AppUiConfigurationGateway>()
+    private val shellGateway by inject<AppShellSettingsGateway>()
+    private val themeGateway by inject<ThemeSettingsGateway>()
     private var lastUiConfiguration: AppUiConfiguration? = null
     private var lastUiConfiguration: AppUiConfiguration? = null
     private var lastPlatformConfiguration: Configuration? = null
     private var lastPlatformConfiguration: Configuration? = null
     private var pendingControlledRecreate = false
     private var pendingControlledRecreate = false
@@ -104,7 +108,7 @@ abstract class BaseActivity<VB : ViewBinding>(
         window.decorView.disableAutoFill()
         window.decorView.disableAutoFill()
         AppContextWrapper.applyFont(this)
         AppContextWrapper.applyFont(this)
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-            val enable = !AppConfig.isPredictiveBackEnabled
+            val enable = !shellGateway.currentSettings.predictiveBackEnabled
             if (enable) {
             if (enable) {
                 onBackInvokedDispatcher.registerOnBackInvokedCallback(
                 onBackInvokedDispatcher.registerOnBackInvokedCallback(
                     OnBackInvokedDispatcher.PRIORITY_DEFAULT
                     OnBackInvokedDispatcher.PRIORITY_DEFAULT
@@ -155,7 +159,11 @@ abstract class BaseActivity<VB : ViewBinding>(
             true,
             true,
             fullScreen
             fullScreen
         )
         )
-        val isDarkTheme = AppConfig.isNightTheme
+        val isDarkTheme = when (shellGateway.currentSettings.themeMode) {
+            "1" -> false
+            "2" -> true
+            else -> sysConfiguration.isNightMode
+        }
         WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = !isDarkTheme
         WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars = !isDarkTheme
     }
     }
 
 
@@ -276,14 +284,14 @@ abstract class BaseActivity<VB : ViewBinding>(
                 }
                 }
 
 
                 // 必须在动态取色之后应用,否则会被动态配色的 surface/background 覆盖
                 // 必须在动态取色之后应用,否则会被动态配色的 surface/background 覆盖
-                if (AppConfig.customMode == "accent")
+                if (themeGateway.currentSettings.customMode == "accent")
                     setTheme(R.style.ThemeOverlay_WhiteBackground)
                     setTheme(R.style.ThemeOverlay_WhiteBackground)
             }
             }
 
 
             "13" -> setTheme(R.style.AppTheme_Transparent)
             "13" -> setTheme(R.style.AppTheme_Transparent)
         }
         }
 
 
-        if (AppConfig.pureBlack)
+        if (themeGateway.currentSettings.isPureBlack)
             setTheme(R.style.ThemeOverlay_PureBlack)
             setTheme(R.style.ThemeOverlay_PureBlack)
     }
     }
 
 

+ 6 - 3
app/src/main/java/io/legado/app/base/BaseDialogFragment.kt

@@ -12,11 +12,12 @@ import androidx.lifecycle.lifecycleScope
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.constant.AppLog
 import io.legado.app.constant.AppLog
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.ThemeSettingsGateway
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Dispatchers
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.CoroutineContext
+import org.koin.core.context.GlobalContext
 
 
 
 
 abstract class BaseDialogFragment(
 abstract class BaseDialogFragment(
@@ -24,6 +25,8 @@ abstract class BaseDialogFragment(
     private val adaptationSoftKeyboard: Boolean = false
     private val adaptationSoftKeyboard: Boolean = false
 ) : DialogFragment(layoutID) {
 ) : DialogFragment(layoutID) {
 
 
+    private val themeGateway get() = GlobalContext.get().get<ThemeSettingsGateway>()
+
     private var onDismissListener: OnDismissListener? = null
     private var onDismissListener: OnDismissListener? = null
 
 
     private lateinit var dialogView: View
     private lateinit var dialogView: View
@@ -45,7 +48,7 @@ abstract class BaseDialogFragment(
         super.onStart()
         super.onStart()
 //        if (adaptationSoftKeyboard) {
 //        if (adaptationSoftKeyboard) {
 //            dialog?.window?.setBackgroundDrawableResource(R.color.transparent)
 //            dialog?.window?.setBackgroundDrawableResource(R.color.transparent)
-//        } else if (AppConfig.isEInkMode) {
+//        } else if (themeGateway.currentSettings.appTheme == "4") {
 //            dialog?.window?.let {
 //            dialog?.window?.let {
 //                it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
 //                it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
 //                val attr = it.attributes
 //                val attr = it.attributes
@@ -80,7 +83,7 @@ abstract class BaseDialogFragment(
         if (adaptationSoftKeyboard) {
         if (adaptationSoftKeyboard) {
             view.findViewById<View>(R.id.vw_bg)?.setOnClickListener(null)
             view.findViewById<View>(R.id.vw_bg)?.setOnClickListener(null)
             view.setOnClickListener { dismiss() }
             view.setOnClickListener { dismiss() }
-        } else if (!AppConfig.isEInkMode) {
+        } else if (themeGateway.currentSettings.appTheme != "4") {
             //view.setBackgroundColor(ThemeStore.backgroundColor())
             //view.setBackgroundColor(ThemeStore.backgroundColor())
         }
         }
         onFragmentCreated(view, savedInstanceState)
         onFragmentCreated(view, savedInstanceState)

+ 5 - 2
app/src/main/java/io/legado/app/base/BasePrefDialogFragment.kt

@@ -2,15 +2,18 @@ package io.legado.app.base
 
 
 import androidx.lifecycle.LifecycleEventObserver
 import androidx.lifecycle.LifecycleEventObserver
 import com.google.android.material.bottomsheet.BottomSheetDialogFragment
 import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.ThemeSettingsGateway
+import org.koin.core.context.GlobalContext
 
 
 
 
 abstract class BasePrefDialogFragment(
 abstract class BasePrefDialogFragment(
 ) : BottomSheetDialogFragment() {
 ) : BottomSheetDialogFragment() {
 
 
+    private val themeGateway get() = GlobalContext.get().get<ThemeSettingsGateway>()
+
     override fun onStart() {
     override fun onStart() {
         super.onStart()
         super.onStart()
-        if (AppConfig.isEInkMode) {
+        if (themeGateway.currentSettings.appTheme == "4") {
 //            dialog?.window?.let {
 //            dialog?.window?.let {
 //                it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
 //                it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
 //                val attr = it.attributes
 //                val attr = it.attributes

+ 5 - 2
app/src/main/java/io/legado/app/constant/AppLog.kt

@@ -2,13 +2,16 @@ package io.legado.app.constant
 
 
 import android.util.Log
 import android.util.Log
 import io.legado.app.BuildConfig
 import io.legado.app.BuildConfig
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.OtherSettingsGateway
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.toastOnUi
+import org.koin.core.context.GlobalContext
 import splitties.init.appCtx
 import splitties.init.appCtx
 
 
 object AppLog {
 object AppLog {
 
 
+    private val otherGateway by lazy { GlobalContext.get().get<OtherSettingsGateway>() }
+
     private val mLogs = arrayListOf<Triple<Long, String, Throwable?>>()
     private val mLogs = arrayListOf<Triple<Long, String, Throwable?>>()
 
 
     val logs get() = mLogs.toList()
     val logs get() = mLogs.toList()
@@ -56,7 +59,7 @@ object AppLog {
     }
     }
 
 
     fun putDebug(message: String?, throwable: Throwable? = null) {
     fun putDebug(message: String?, throwable: Throwable? = null) {
-        if (AppConfig.recordLog) {
+        if (otherGateway.currentSettings.recordLog) {
             put(message, throwable)
             put(message, throwable)
         }
         }
     }
     }

+ 141 - 141
app/src/main/java/io/legado/app/data/DatabaseMigrations.kt

@@ -26,9 +26,9 @@ object DatabaseMigrations {
     }
     }
 
 
     private val migration_10_11 = object : Migration(10, 11) {
     private val migration_10_11 = object : Migration(10, 11) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("DROP TABLE txtTocRules")
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("DROP TABLE txtTocRules")
+            db.execSQL(
                 """CREATE TABLE txtTocRules(id INTEGER NOT NULL, 
                 """CREATE TABLE txtTocRules(id INTEGER NOT NULL, 
                     name TEXT NOT NULL, rule TEXT NOT NULL, serialNumber INTEGER NOT NULL, 
                     name TEXT NOT NULL, rule TEXT NOT NULL, serialNumber INTEGER NOT NULL, 
                     enable INTEGER NOT NULL, PRIMARY KEY (id))"""
                     enable INTEGER NOT NULL, PRIMARY KEY (id))"""
@@ -37,20 +37,20 @@ object DatabaseMigrations {
     }
     }
 
 
     private val migration_11_12 = object : Migration(11, 12) {
     private val migration_11_12 = object : Migration(11, 12) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE rssSources ADD style TEXT ")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE rssSources ADD style TEXT ")
         }
         }
     }
     }
 
 
     private val migration_12_13 = object : Migration(12, 13) {
     private val migration_12_13 = object : Migration(12, 13) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE rssSources ADD articleStyle INTEGER NOT NULL DEFAULT 0 ")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE rssSources ADD articleStyle INTEGER NOT NULL DEFAULT 0 ")
         }
         }
     }
     }
 
 
     private val migration_13_14 = object : Migration(13, 14) {
     private val migration_13_14 = object : Migration(13, 14) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(
                 """CREATE TABLE IF NOT EXISTS `books_new` (`bookUrl` TEXT NOT NULL, `tocUrl` TEXT NOT NULL, `origin` TEXT NOT NULL,
                 """CREATE TABLE IF NOT EXISTS `books_new` (`bookUrl` TEXT NOT NULL, `tocUrl` TEXT NOT NULL, `origin` TEXT NOT NULL,
                     `originName` TEXT NOT NULL, `name` TEXT NOT NULL, `author` TEXT NOT NULL, `kind` TEXT, `customTag` TEXT, `coverUrl` TEXT, 
                     `originName` TEXT NOT NULL, `name` TEXT NOT NULL, `author` TEXT NOT NULL, `kind` TEXT, `customTag` TEXT, `coverUrl` TEXT, 
                     `customCoverUrl` TEXT, `intro` TEXT, `customIntro` TEXT, `charset` TEXT, `type` INTEGER NOT NULL, `group` INTEGER NOT NULL, 
                     `customCoverUrl` TEXT, `intro` TEXT, `customIntro` TEXT, `charset` TEXT, `type` INTEGER NOT NULL, `group` INTEGER NOT NULL, 
@@ -59,57 +59,57 @@ object DatabaseMigrations {
                     `durChapterTime` INTEGER NOT NULL, `wordCount` TEXT, `canUpdate` INTEGER NOT NULL, `order` INTEGER NOT NULL, 
                     `durChapterTime` INTEGER NOT NULL, `wordCount` TEXT, `canUpdate` INTEGER NOT NULL, `order` INTEGER NOT NULL, 
                     `originOrder` INTEGER NOT NULL, `useReplaceRule` INTEGER NOT NULL, `variable` TEXT, PRIMARY KEY(`bookUrl`))"""
                     `originOrder` INTEGER NOT NULL, `useReplaceRule` INTEGER NOT NULL, `variable` TEXT, PRIMARY KEY(`bookUrl`))"""
             )
             )
-            database.execSQL("INSERT INTO books_new select * from books ")
-            database.execSQL("DROP TABLE books")
-            database.execSQL("ALTER TABLE books_new RENAME TO books")
-            database.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_books_name_author` ON `books` (`name`, `author`) ")
+            db.execSQL("INSERT INTO books_new select * from books ")
+            db.execSQL("DROP TABLE books")
+            db.execSQL("ALTER TABLE books_new RENAME TO books")
+            db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_books_name_author` ON `books` (`name`, `author`) ")
         }
         }
     }
     }
 
 
     private val migration_14_15 = object : Migration(14, 15) {
     private val migration_14_15 = object : Migration(14, 15) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE bookmarks ADD bookAuthor TEXT NOT NULL DEFAULT ''")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE bookmarks ADD bookAuthor TEXT NOT NULL DEFAULT ''")
         }
         }
     }
     }
 
 
     private val migration_15_17 = object : Migration(15, 17) {
     private val migration_15_17 = object : Migration(15, 17) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("CREATE TABLE IF NOT EXISTS `readRecord` (`bookName` TEXT NOT NULL, `readTime` INTEGER NOT NULL, PRIMARY KEY(`bookName`))")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("CREATE TABLE IF NOT EXISTS `readRecord` (`bookName` TEXT NOT NULL, `readTime` INTEGER NOT NULL, PRIMARY KEY(`bookName`))")
         }
         }
     }
     }
 
 
     private val migration_17_18 = object : Migration(17, 18) {
     private val migration_17_18 = object : Migration(17, 18) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("CREATE TABLE IF NOT EXISTS `httpTTS` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`id`))")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("CREATE TABLE IF NOT EXISTS `httpTTS` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`id`))")
         }
         }
     }
     }
 
 
     private val migration_18_19 = object : Migration(18, 19) {
     private val migration_18_19 = object : Migration(18, 19) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(
                 """CREATE TABLE IF NOT EXISTS `readRecordNew` (`androidId` TEXT NOT NULL, `bookName` TEXT NOT NULL, `readTime` INTEGER NOT NULL, 
                 """CREATE TABLE IF NOT EXISTS `readRecordNew` (`androidId` TEXT NOT NULL, `bookName` TEXT NOT NULL, `readTime` INTEGER NOT NULL, 
                     PRIMARY KEY(`androidId`, `bookName`))"""
                     PRIMARY KEY(`androidId`, `bookName`))"""
             )
             )
-            database.execSQL("INSERT INTO readRecordNew(androidId, bookName, readTime) select '${AppConst.androidId}' as androidId, bookName, readTime from readRecord")
-            database.execSQL("DROP TABLE readRecord")
-            database.execSQL("ALTER TABLE readRecordNew RENAME TO readRecord")
+            db.execSQL("INSERT INTO readRecordNew(androidId, bookName, readTime) select '${AppConst.androidId}' as androidId, bookName, readTime from readRecord")
+            db.execSQL("DROP TABLE readRecord")
+            db.execSQL("ALTER TABLE readRecordNew RENAME TO readRecord")
         }
         }
     }
     }
     private val migration_19_20 = object : Migration(19, 20) {
     private val migration_19_20 = object : Migration(19, 20) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE book_sources ADD bookSourceComment TEXT")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE book_sources ADD bookSourceComment TEXT")
         }
         }
     }
     }
 
 
     private val migration_20_21 = object : Migration(20, 21) {
     private val migration_20_21 = object : Migration(20, 21) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE book_groups ADD show INTEGER NOT NULL DEFAULT 1")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE book_groups ADD show INTEGER NOT NULL DEFAULT 1")
         }
         }
     }
     }
 
 
     private val migration_21_22 = object : Migration(21, 22) {
     private val migration_21_22 = object : Migration(21, 22) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(
                 """CREATE TABLE IF NOT EXISTS `books_new` (`bookUrl` TEXT NOT NULL, `tocUrl` TEXT NOT NULL, `origin` TEXT NOT NULL, 
                 """CREATE TABLE IF NOT EXISTS `books_new` (`bookUrl` TEXT NOT NULL, `tocUrl` TEXT NOT NULL, `origin` TEXT NOT NULL, 
                     `originName` TEXT NOT NULL, `name` TEXT NOT NULL, `author` TEXT NOT NULL, `kind` TEXT, `customTag` TEXT, 
                     `originName` TEXT NOT NULL, `name` TEXT NOT NULL, `author` TEXT NOT NULL, `kind` TEXT, `customTag` TEXT, 
                     `coverUrl` TEXT, `customCoverUrl` TEXT, `intro` TEXT, `customIntro` TEXT, `charset` TEXT, `type` INTEGER NOT NULL, 
                     `coverUrl` TEXT, `customCoverUrl` TEXT, `intro` TEXT, `customIntro` TEXT, `charset` TEXT, `type` INTEGER NOT NULL, 
@@ -118,35 +118,35 @@ object DatabaseMigrations {
                     `durChapterPos` INTEGER NOT NULL, `durChapterTime` INTEGER NOT NULL, `wordCount` TEXT, `canUpdate` INTEGER NOT NULL, 
                     `durChapterPos` INTEGER NOT NULL, `durChapterTime` INTEGER NOT NULL, `wordCount` TEXT, `canUpdate` INTEGER NOT NULL, 
                     `order` INTEGER NOT NULL, `originOrder` INTEGER NOT NULL, `variable` TEXT, `readConfig` TEXT, PRIMARY KEY(`bookUrl`))"""
                     `order` INTEGER NOT NULL, `originOrder` INTEGER NOT NULL, `variable` TEXT, `readConfig` TEXT, PRIMARY KEY(`bookUrl`))"""
             )
             )
-            database.execSQL(
+            db.execSQL(
                 """INSERT INTO books_new select `bookUrl`, `tocUrl`, `origin`, `originName`, `name`, `author`, `kind`, `customTag`, `coverUrl`, 
                 """INSERT INTO books_new select `bookUrl`, `tocUrl`, `origin`, `originName`, `name`, `author`, `kind`, `customTag`, `coverUrl`, 
                     `customCoverUrl`, `intro`, `customIntro`, `charset`, `type`, `group`, `latestChapterTitle`, `latestChapterTime`, `lastCheckTime`, 
                     `customCoverUrl`, `intro`, `customIntro`, `charset`, `type`, `group`, `latestChapterTitle`, `latestChapterTime`, `lastCheckTime`, 
                     `lastCheckCount`, `totalChapterNum`, `durChapterTitle`, `durChapterIndex`, `durChapterPos`, `durChapterTime`, `wordCount`, `canUpdate`, 
                     `lastCheckCount`, `totalChapterNum`, `durChapterTitle`, `durChapterIndex`, `durChapterPos`, `durChapterTime`, `wordCount`, `canUpdate`, 
                     `order`, `originOrder`, `variable`, null
                     `order`, `originOrder`, `variable`, null
                     from books"""
                     from books"""
             )
             )
-            database.execSQL("DROP TABLE books")
-            database.execSQL("ALTER TABLE books_new RENAME TO books")
-            database.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_books_name_author` ON `books` (`name`, `author`) ")
+            db.execSQL("DROP TABLE books")
+            db.execSQL("ALTER TABLE books_new RENAME TO books")
+            db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_books_name_author` ON `books` (`name`, `author`) ")
         }
         }
     }
     }
 
 
     private val migration_22_23 = object : Migration(22, 23) {
     private val migration_22_23 = object : Migration(22, 23) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE chapters ADD baseUrl TEXT NOT NULL DEFAULT ''")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE chapters ADD baseUrl TEXT NOT NULL DEFAULT ''")
         }
         }
     }
     }
 
 
     private val migration_23_24 = object : Migration(23, 24) {
     private val migration_23_24 = object : Migration(23, 24) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("CREATE TABLE IF NOT EXISTS `caches` (`key` TEXT NOT NULL, `value` TEXT, `deadline` INTEGER NOT NULL, PRIMARY KEY(`key`))")
-            database.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_caches_key` ON `caches` (`key`)")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("CREATE TABLE IF NOT EXISTS `caches` (`key` TEXT NOT NULL, `value` TEXT, `deadline` INTEGER NOT NULL, PRIMARY KEY(`key`))")
+            db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_caches_key` ON `caches` (`key`)")
         }
         }
     }
     }
 
 
     private val migration_24_25 = object : Migration(24, 25) {
     private val migration_24_25 = object : Migration(24, 25) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(
                 """CREATE TABLE IF NOT EXISTS `sourceSubs` 
                 """CREATE TABLE IF NOT EXISTS `sourceSubs` 
                     (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `type` INTEGER NOT NULL, `customOrder` INTEGER NOT NULL, 
                     (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `type` INTEGER NOT NULL, `customOrder` INTEGER NOT NULL, 
                     PRIMARY KEY(`id`))"""
                     PRIMARY KEY(`id`))"""
@@ -155,86 +155,86 @@ object DatabaseMigrations {
     }
     }
 
 
     private val migration_25_26 = object : Migration(25, 26) {
     private val migration_25_26 = object : Migration(25, 26) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(
                 """CREATE TABLE IF NOT EXISTS `ruleSubs` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `type` INTEGER NOT NULL, 
                 """CREATE TABLE IF NOT EXISTS `ruleSubs` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `type` INTEGER NOT NULL, 
                     `customOrder` INTEGER NOT NULL, `autoUpdate` INTEGER NOT NULL, `update` INTEGER NOT NULL, PRIMARY KEY(`id`))"""
                     `customOrder` INTEGER NOT NULL, `autoUpdate` INTEGER NOT NULL, `update` INTEGER NOT NULL, PRIMARY KEY(`id`))"""
             )
             )
-            database.execSQL(" insert into `ruleSubs` select *, 0, 0 from `sourceSubs` ")
-            database.execSQL("DROP TABLE `sourceSubs`")
+            db.execSQL(" insert into `ruleSubs` select *, 0, 0 from `sourceSubs` ")
+            db.execSQL("DROP TABLE `sourceSubs`")
         }
         }
     }
     }
 
 
     private val migration_26_27 = object : Migration(26, 27) {
     private val migration_26_27 = object : Migration(26, 27) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(" ALTER TABLE rssSources ADD singleUrl INTEGER NOT NULL DEFAULT 0 ")
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(" ALTER TABLE rssSources ADD singleUrl INTEGER NOT NULL DEFAULT 0 ")
+            db.execSQL(
                 """CREATE TABLE IF NOT EXISTS `bookmarks1` (`time` INTEGER NOT NULL, `bookUrl` TEXT NOT NULL, `bookName` TEXT NOT NULL, 
                 """CREATE TABLE IF NOT EXISTS `bookmarks1` (`time` INTEGER NOT NULL, `bookUrl` TEXT NOT NULL, `bookName` TEXT NOT NULL, 
                         `bookAuthor` TEXT NOT NULL, `chapterIndex` INTEGER NOT NULL, `chapterPos` INTEGER NOT NULL, `chapterName` TEXT NOT NULL, 
                         `bookAuthor` TEXT NOT NULL, `chapterIndex` INTEGER NOT NULL, `chapterPos` INTEGER NOT NULL, `chapterName` TEXT NOT NULL, 
                         `bookText` TEXT NOT NULL, `content` TEXT NOT NULL, PRIMARY KEY(`time`))"""
                         `bookText` TEXT NOT NULL, `content` TEXT NOT NULL, PRIMARY KEY(`time`))"""
             )
             )
-            database.execSQL(
+            db.execSQL(
                 """insert into `bookmarks1` 
                 """insert into `bookmarks1` 
                         select `time`, `bookUrl`, `bookName`, `bookAuthor`, `chapterIndex`, `pageIndex`, `chapterName`, '', `content` 
                         select `time`, `bookUrl`, `bookName`, `bookAuthor`, `chapterIndex`, `pageIndex`, `chapterName`, '', `content` 
                         from bookmarks"""
                         from bookmarks"""
             )
             )
-            database.execSQL(" DROP TABLE `bookmarks` ")
-            database.execSQL(" ALTER TABLE bookmarks1 RENAME TO bookmarks ")
-            database.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_bookmarks_time` ON `bookmarks` (`time`)")
+            db.execSQL(" DROP TABLE `bookmarks` ")
+            db.execSQL(" ALTER TABLE bookmarks1 RENAME TO bookmarks ")
+            db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_bookmarks_time` ON `bookmarks` (`time`)")
         }
         }
     }
     }
 
 
     private val migration_27_28 = object : Migration(27, 28) {
     private val migration_27_28 = object : Migration(27, 28) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE rssArticles ADD variable TEXT")
-            database.execSQL("ALTER TABLE rssStars ADD variable TEXT")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE rssArticles ADD variable TEXT")
+            db.execSQL("ALTER TABLE rssStars ADD variable TEXT")
         }
         }
     }
     }
 
 
     private val migration_28_29 = object : Migration(28, 29) {
     private val migration_28_29 = object : Migration(28, 29) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE rssSources ADD sourceComment TEXT")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE rssSources ADD sourceComment TEXT")
         }
         }
     }
     }
 
 
     private val migration_29_30 = object : Migration(29, 30) {
     private val migration_29_30 = object : Migration(29, 30) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE chapters ADD `startFragmentId` TEXT")
-            database.execSQL("ALTER TABLE chapters ADD `endFragmentId` TEXT")
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE chapters ADD `startFragmentId` TEXT")
+            db.execSQL("ALTER TABLE chapters ADD `endFragmentId` TEXT")
+            db.execSQL(
                 """
                 """
                     CREATE TABLE IF NOT EXISTS `epubChapters` 
                     CREATE TABLE IF NOT EXISTS `epubChapters` 
                     (`bookUrl` TEXT NOT NULL, `href` TEXT NOT NULL, `parentHref` TEXT, 
                     (`bookUrl` TEXT NOT NULL, `href` TEXT NOT NULL, `parentHref` TEXT, 
                     PRIMARY KEY(`bookUrl`, `href`), FOREIGN KEY(`bookUrl`) REFERENCES `books`(`bookUrl`) ON UPDATE NO ACTION ON DELETE CASCADE )
                     PRIMARY KEY(`bookUrl`, `href`), FOREIGN KEY(`bookUrl`) REFERENCES `books`(`bookUrl`) ON UPDATE NO ACTION ON DELETE CASCADE )
                 """
                 """
             )
             )
-            database.execSQL("CREATE INDEX IF NOT EXISTS `index_epubChapters_bookUrl` ON `epubChapters` (`bookUrl`)")
-            database.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_epubChapters_bookUrl_href` ON `epubChapters` (`bookUrl`, `href`)")
+            db.execSQL("CREATE INDEX IF NOT EXISTS `index_epubChapters_bookUrl` ON `epubChapters` (`bookUrl`)")
+            db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_epubChapters_bookUrl_href` ON `epubChapters` (`bookUrl`, `href`)")
         }
         }
     }
     }
 
 
     private val migration_30_31 = object : Migration(30, 31) {
     private val migration_30_31 = object : Migration(30, 31) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE readRecord RENAME TO readRecord1")
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE readRecord RENAME TO readRecord1")
+            db.execSQL(
                 """
                 """
                     CREATE TABLE IF NOT EXISTS `readRecord` (`deviceId` TEXT NOT NULL, `bookName` TEXT NOT NULL, `readTime` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `bookName`))
                     CREATE TABLE IF NOT EXISTS `readRecord` (`deviceId` TEXT NOT NULL, `bookName` TEXT NOT NULL, `readTime` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `bookName`))
                 """
                 """
             )
             )
-            database.execSQL("insert into readRecord (deviceId, bookName, readTime) select androidId, bookName, readTime from readRecord1")
+            db.execSQL("insert into readRecord (deviceId, bookName, readTime) select androidId, bookName, readTime from readRecord1")
         }
         }
     }
     }
 
 
     private val migration_31_32 = object : Migration(31, 32) {
     private val migration_31_32 = object : Migration(31, 32) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("DROP TABLE `epubChapters`")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("DROP TABLE `epubChapters`")
         }
         }
     }
     }
 
 
     private val migration_32_33 = object : Migration(32, 33) {
     private val migration_32_33 = object : Migration(32, 33) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE bookmarks RENAME TO bookmarks_old")
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE bookmarks RENAME TO bookmarks_old")
+            db.execSQL(
                 """
                 """
                     CREATE TABLE IF NOT EXISTS `bookmarks` (`time` INTEGER NOT NULL,
                     CREATE TABLE IF NOT EXISTS `bookmarks` (`time` INTEGER NOT NULL,
                     `bookName` TEXT NOT NULL, `bookAuthor` TEXT NOT NULL, `chapterIndex` INTEGER NOT NULL, 
                     `bookName` TEXT NOT NULL, `bookAuthor` TEXT NOT NULL, `chapterIndex` INTEGER NOT NULL, 
@@ -242,12 +242,12 @@ object DatabaseMigrations {
                     `content` TEXT NOT NULL, PRIMARY KEY(`time`))
                     `content` TEXT NOT NULL, PRIMARY KEY(`time`))
                 """
                 """
             )
             )
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                     CREATE INDEX IF NOT EXISTS `index_bookmarks_bookName_bookAuthor` ON `bookmarks` (`bookName`, `bookAuthor`)
                     CREATE INDEX IF NOT EXISTS `index_bookmarks_bookName_bookAuthor` ON `bookmarks` (`bookName`, `bookAuthor`)
                 """
                 """
             )
             )
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                     insert into bookmarks (time, bookName, bookAuthor, chapterIndex, chapterPos, chapterName, bookText, content)
                     insert into bookmarks (time, bookName, bookAuthor, chapterIndex, chapterPos, chapterName, bookText, content)
                     select time, ifNull(b.name, bookName) bookName, ifNull(b.author, bookAuthor) bookAuthor, 
                     select time, ifNull(b.name, bookName) bookName, ifNull(b.author, bookAuthor) bookAuthor, 
@@ -259,77 +259,77 @@ object DatabaseMigrations {
     }
     }
 
 
     private val migration_33_34 = object : Migration(33, 34) {
     private val migration_33_34 = object : Migration(33, 34) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE `book_groups` ADD `cover` TEXT")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE `book_groups` ADD `cover` TEXT")
         }
         }
     }
     }
 
 
     private val migration_34_35 = object : Migration(34, 35) {
     private val migration_34_35 = object : Migration(34, 35) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE `book_sources` ADD `concurrentRate` TEXT")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE `book_sources` ADD `concurrentRate` TEXT")
         }
         }
     }
     }
 
 
     private val migration_35_36 = object : Migration(35, 36) {
     private val migration_35_36 = object : Migration(35, 36) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE `book_sources` ADD `loginUi` TEXT")
-            database.execSQL("ALTER TABLE `book_sources` ADD`loginCheckJs` TEXT")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE `book_sources` ADD `loginUi` TEXT")
+            db.execSQL("ALTER TABLE `book_sources` ADD`loginCheckJs` TEXT")
         }
         }
     }
     }
 
 
     private val migration_36_37 = object : Migration(36, 37) {
     private val migration_36_37 = object : Migration(36, 37) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE `rssSources` ADD `loginUrl` TEXT")
-            database.execSQL("ALTER TABLE `rssSources` ADD `loginUi` TEXT")
-            database.execSQL("ALTER TABLE `rssSources` ADD `loginCheckJs` TEXT")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE `rssSources` ADD `loginUrl` TEXT")
+            db.execSQL("ALTER TABLE `rssSources` ADD `loginUi` TEXT")
+            db.execSQL("ALTER TABLE `rssSources` ADD `loginCheckJs` TEXT")
         }
         }
     }
     }
 
 
     private val migration_37_38 = object : Migration(37, 38) {
     private val migration_37_38 = object : Migration(37, 38) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE `book_sources` ADD `respondTime` INTEGER NOT NULL DEFAULT 180000")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE `book_sources` ADD `respondTime` INTEGER NOT NULL DEFAULT 180000")
         }
         }
     }
     }
 
 
     private val migration_38_39 = object : Migration(38, 39) {
     private val migration_38_39 = object : Migration(38, 39) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE `rssSources` ADD `concurrentRate` TEXT")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE `rssSources` ADD `concurrentRate` TEXT")
         }
         }
     }
     }
 
 
     private val migration_39_40 = object : Migration(39, 40) {
     private val migration_39_40 = object : Migration(39, 40) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE `chapters` ADD `isVip` INTEGER NOT NULL DEFAULT 0")
-            database.execSQL("ALTER TABLE `chapters` ADD `isPay` INTEGER NOT NULL DEFAULT 0")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE `chapters` ADD `isVip` INTEGER NOT NULL DEFAULT 0")
+            db.execSQL("ALTER TABLE `chapters` ADD `isPay` INTEGER NOT NULL DEFAULT 0")
         }
         }
     }
     }
 
 
     private val migration_40_41 = object : Migration(40, 41) {
     private val migration_40_41 = object : Migration(40, 41) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE `httpTTS` ADD `loginUrl` TEXT")
-            database.execSQL("ALTER TABLE `httpTTS` ADD `loginUi` TEXT")
-            database.execSQL("ALTER TABLE `httpTTS` ADD `loginCheckJs` TEXT")
-            database.execSQL("ALTER TABLE `httpTTS` ADD `header` TEXT")
-            database.execSQL("ALTER TABLE `httpTTS` ADD `concurrentRate` TEXT")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE `httpTTS` ADD `loginUrl` TEXT")
+            db.execSQL("ALTER TABLE `httpTTS` ADD `loginUi` TEXT")
+            db.execSQL("ALTER TABLE `httpTTS` ADD `loginCheckJs` TEXT")
+            db.execSQL("ALTER TABLE `httpTTS` ADD `header` TEXT")
+            db.execSQL("ALTER TABLE `httpTTS` ADD `concurrentRate` TEXT")
         }
         }
     }
     }
 
 
     private val migration_41_42 = object : Migration(41, 42) {
     private val migration_41_42 = object : Migration(41, 42) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE 'httpTTS' ADD `contentType` TEXT")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE 'httpTTS' ADD `contentType` TEXT")
         }
         }
     }
     }
 
 
     private val migration_42_43 = object : Migration(42, 43) {
     private val migration_42_43 = object : Migration(42, 43) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE `chapters` ADD `isVolume` INTEGER NOT NULL DEFAULT 0")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE `chapters` ADD `isVolume` INTEGER NOT NULL DEFAULT 0")
         }
         }
     }
     }
 
 
     private val migration_82_83 = object : Migration(82, 83) {
     private val migration_82_83 = object : Migration(82, 83) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE readRecord RENAME TO readRecord_old")
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE readRecord RENAME TO readRecord_old")
+            db.execSQL(
                 """
                 """
                 CREATE TABLE IF NOT EXISTS `readRecord` (
                 CREATE TABLE IF NOT EXISTS `readRecord` (
                     `deviceId` TEXT NOT NULL,
                     `deviceId` TEXT NOT NULL,
@@ -341,7 +341,7 @@ object DatabaseMigrations {
                 )
                 )
                 """
                 """
             )
             )
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                 INSERT INTO readRecord(deviceId, bookName, bookAuthor, readTime, lastRead)
                 INSERT INTO readRecord(deviceId, bookName, bookAuthor, readTime, lastRead)
                 SELECT
                 SELECT
@@ -363,10 +363,10 @@ object DatabaseMigrations {
                 FROM readRecord_old rr
                 FROM readRecord_old rr
                 """
                 """
             )
             )
-            database.execSQL("DROP TABLE readRecord_old")
+            db.execSQL("DROP TABLE readRecord_old")
 
 
-            database.execSQL("ALTER TABLE readRecordDetail RENAME TO readRecordDetail_old")
-            database.execSQL(
+            db.execSQL("ALTER TABLE readRecordDetail RENAME TO readRecordDetail_old")
+            db.execSQL(
                 """
                 """
                 CREATE TABLE IF NOT EXISTS `readRecordDetail` (
                 CREATE TABLE IF NOT EXISTS `readRecordDetail` (
                     `deviceId` TEXT NOT NULL,
                     `deviceId` TEXT NOT NULL,
@@ -381,7 +381,7 @@ object DatabaseMigrations {
                 )
                 )
                 """
                 """
             )
             )
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                 INSERT INTO readRecordDetail(
                 INSERT INTO readRecordDetail(
                     deviceId, bookName, bookAuthor, date, readTime, readWords, firstReadTime, lastReadTime
                     deviceId, bookName, bookAuthor, date, readTime, readWords, firstReadTime, lastReadTime
@@ -408,10 +408,10 @@ object DatabaseMigrations {
                 FROM readRecordDetail_old rd
                 FROM readRecordDetail_old rd
                 """
                 """
             )
             )
-            database.execSQL("DROP TABLE readRecordDetail_old")
+            db.execSQL("DROP TABLE readRecordDetail_old")
 
 
-            database.execSQL("ALTER TABLE readRecordSession RENAME TO readRecordSession_old")
-            database.execSQL(
+            db.execSQL("ALTER TABLE readRecordSession RENAME TO readRecordSession_old")
+            db.execSQL(
                 """
                 """
                 CREATE TABLE IF NOT EXISTS `readRecordSession` (
                 CREATE TABLE IF NOT EXISTS `readRecordSession` (
                     `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
                     `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
@@ -424,7 +424,7 @@ object DatabaseMigrations {
                 )
                 )
                 """
                 """
             )
             )
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                 INSERT INTO readRecordSession(id, deviceId, bookName, bookAuthor, startTime, endTime, words)
                 INSERT INTO readRecordSession(id, deviceId, bookName, bookAuthor, startTime, endTime, words)
                 SELECT
                 SELECT
@@ -448,7 +448,7 @@ object DatabaseMigrations {
                 FROM readRecordSession_old rs
                 FROM readRecordSession_old rs
                 """
                 """
             )
             )
-            database.execSQL("DROP TABLE readRecordSession_old")
+            db.execSQL("DROP TABLE readRecordSession_old")
         }
         }
     }
     }
 
 
@@ -515,8 +515,8 @@ object DatabaseMigrations {
     }
     }
 
 
     private val migration_98_99 = object : Migration(98, 99) {
     private val migration_98_99 = object : Migration(98, 99) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(
                 """
                 """
                 CREATE TABLE IF NOT EXISTS `txtTocRules_new` (
                 CREATE TABLE IF NOT EXISTS `txtTocRules_new` (
                     `id` INTEGER NOT NULL,
                     `id` INTEGER NOT NULL,
@@ -530,26 +530,26 @@ object DatabaseMigrations {
                 )
                 )
                 """.trimIndent()
                 """.trimIndent()
             )
             )
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                 INSERT INTO txtTocRules_new (id, name, chapterRule, volumeRule, example, serialNumber, enable)
                 INSERT INTO txtTocRules_new (id, name, chapterRule, volumeRule, example, serialNumber, enable)
                 SELECT id, name, rule, '', example, serialNumber, enable FROM txtTocRules
                 SELECT id, name, rule, '', example, serialNumber, enable FROM txtTocRules
                 """.trimIndent()
                 """.trimIndent()
             )
             )
-            database.execSQL("DROP TABLE txtTocRules")
-            database.execSQL("ALTER TABLE txtTocRules_new RENAME TO txtTocRules")
-            database.execSQL("ALTER TABLE highlightRules ADD COLUMN fontWeight INTEGER NOT NULL DEFAULT 400")
-            database.execSQL("ALTER TABLE highlightRules ADD COLUMN isItalic INTEGER NOT NULL DEFAULT 0")
-            database.execSQL("ALTER TABLE highlightRules ADD COLUMN npLeft REAL NOT NULL DEFAULT 0.1")
-            database.execSQL("ALTER TABLE highlightRules ADD COLUMN npRight REAL NOT NULL DEFAULT 0.1")
-            database.execSQL("ALTER TABLE highlightRules ADD COLUMN npTop REAL NOT NULL DEFAULT 0.1")
-            database.execSQL("ALTER TABLE highlightRules ADD COLUMN npBottom REAL NOT NULL DEFAULT 0.1")
+            db.execSQL("DROP TABLE txtTocRules")
+            db.execSQL("ALTER TABLE txtTocRules_new RENAME TO txtTocRules")
+            db.execSQL("ALTER TABLE highlightRules ADD COLUMN fontWeight INTEGER NOT NULL DEFAULT 400")
+            db.execSQL("ALTER TABLE highlightRules ADD COLUMN isItalic INTEGER NOT NULL DEFAULT 0")
+            db.execSQL("ALTER TABLE highlightRules ADD COLUMN npLeft REAL NOT NULL DEFAULT 0.1")
+            db.execSQL("ALTER TABLE highlightRules ADD COLUMN npRight REAL NOT NULL DEFAULT 0.1")
+            db.execSQL("ALTER TABLE highlightRules ADD COLUMN npTop REAL NOT NULL DEFAULT 0.1")
+            db.execSQL("ALTER TABLE highlightRules ADD COLUMN npBottom REAL NOT NULL DEFAULT 0.1")
         }
         }
     }
     }
 
 
     private val migration_99_100 = object : Migration(99, 100) {
     private val migration_99_100 = object : Migration(99, 100) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("ALTER TABLE highlightRules ADD COLUMN fontSizeOffset INTEGER NOT NULL DEFAULT 0")
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("ALTER TABLE highlightRules ADD COLUMN fontSizeOffset INTEGER NOT NULL DEFAULT 0")
         }
         }
     }
     }
 
 
@@ -560,10 +560,10 @@ object DatabaseMigrations {
      * 中的聚合规则合并,以保证主键唯一。
      * 中的聚合规则合并,以保证主键唯一。
      */
      */
     private val migration_102_103 = object : Migration(102, 103) {
     private val migration_102_103 = object : Migration(102, 103) {
-        override fun migrate(database: SupportSQLiteDatabase) {
+        override fun migrate(db: SupportSQLiteDatabase) {
             // 将旧设备分区合并到本地分区;同一本书的旧空作者仅在书架存在唯一作者时归并。
             // 将旧设备分区合并到本地分区;同一本书的旧空作者仅在书架存在唯一作者时归并。
             // 阅读时段记录按书名、作者和完整时间区间聚合,字段重复的记录只保留一条并取较大的字数。
             // 阅读时段记录按书名、作者和完整时间区间聚合,字段重复的记录只保留一条并取较大的字数。
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                 CREATE TABLE readRecord_migrated (
                 CREATE TABLE readRecord_migrated (
                     deviceId TEXT NOT NULL,
                     deviceId TEXT NOT NULL,
@@ -575,7 +575,7 @@ object DatabaseMigrations {
                 )
                 )
                 """.trimIndent()
                 """.trimIndent()
             )
             )
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                 INSERT INTO readRecord_migrated(deviceId, bookName, bookAuthor, readTime, lastRead)
                 INSERT INTO readRecord_migrated(deviceId, bookName, bookAuthor, readTime, lastRead)
                 SELECT '', bookName, canonicalAuthor, SUM(readTime), MAX(lastRead)
                 SELECT '', bookName, canonicalAuthor, SUM(readTime), MAX(lastRead)
@@ -590,10 +590,10 @@ object DatabaseMigrations {
                 GROUP BY bookName, canonicalAuthor
                 GROUP BY bookName, canonicalAuthor
                 """.trimIndent()
                 """.trimIndent()
             )
             )
-            database.execSQL("DROP TABLE readRecord")
-            database.execSQL("ALTER TABLE readRecord_migrated RENAME TO readRecord")
+            db.execSQL("DROP TABLE readRecord")
+            db.execSQL("ALTER TABLE readRecord_migrated RENAME TO readRecord")
 
 
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                 CREATE TABLE readRecordDetail_migrated (
                 CREATE TABLE readRecordDetail_migrated (
                     deviceId TEXT NOT NULL,
                     deviceId TEXT NOT NULL,
@@ -608,7 +608,7 @@ object DatabaseMigrations {
                 )
                 )
                 """.trimIndent()
                 """.trimIndent()
             )
             )
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                 INSERT INTO readRecordDetail_migrated(
                 INSERT INTO readRecordDetail_migrated(
                     deviceId, bookName, bookAuthor, date, readTime, readWords,
                     deviceId, bookName, bookAuthor, date, readTime, readWords,
@@ -629,10 +629,10 @@ object DatabaseMigrations {
                 GROUP BY bookName, canonicalAuthor, date
                 GROUP BY bookName, canonicalAuthor, date
                 """.trimIndent()
                 """.trimIndent()
             )
             )
-            database.execSQL("DROP TABLE readRecordDetail")
-            database.execSQL("ALTER TABLE readRecordDetail_migrated RENAME TO readRecordDetail")
+            db.execSQL("DROP TABLE readRecordDetail")
+            db.execSQL("ALTER TABLE readRecordDetail_migrated RENAME TO readRecordDetail")
 
 
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                 CREATE TABLE readRecordSession_migrated (
                 CREATE TABLE readRecordSession_migrated (
                     id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
                     id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
@@ -645,7 +645,7 @@ object DatabaseMigrations {
                 )
                 )
                 """.trimIndent()
                 """.trimIndent()
             )
             )
-            database.execSQL(
+            db.execSQL(
                 """
                 """
                 INSERT INTO readRecordSession_migrated(
                 INSERT INTO readRecordSession_migrated(
                     deviceId, bookName, bookAuthor, startTime, endTime, words
                     deviceId, bookName, bookAuthor, startTime, endTime, words
@@ -662,8 +662,8 @@ object DatabaseMigrations {
                 GROUP BY bookName, canonicalAuthor, startTime, endTime
                 GROUP BY bookName, canonicalAuthor, startTime, endTime
                 """.trimIndent()
                 """.trimIndent()
             )
             )
-            database.execSQL("DROP TABLE readRecordSession")
-            database.execSQL("ALTER TABLE readRecordSession_migrated RENAME TO readRecordSession")
+            db.execSQL("DROP TABLE readRecordSession")
+            db.execSQL("ALTER TABLE readRecordSession_migrated RENAME TO readRecordSession")
         }
         }
     }
     }
 }
 }

+ 2 - 1
app/src/main/java/io/legado/app/di/appModule.kt

@@ -776,7 +776,8 @@ val appModule = module {
             changeBookSourceUseCase = get(),
             changeBookSourceUseCase = get(),
             clearBookCacheUseCase = get(),
             clearBookCacheUseCase = get(),
             deleteBooksUseCase = get(),
             deleteBooksUseCase = get(),
-            updateBooksGroupUseCase = get()
+            updateBooksGroupUseCase = get(),
+            downloadCacheSettingsGateway = get()
         )
         )
     }
     }
 
 

+ 1 - 0
app/src/main/java/io/legado/app/domain/model/AiMessageParts.kt

@@ -121,6 +121,7 @@ object AiMessagePartJson {
      * Migrate legacy ToolCall/ToolResult pairs into unified Tool parts.
      * Migrate legacy ToolCall/ToolResult pairs into unified Tool parts.
      * Handles old messages stored before the unified Tool part was introduced.
      * Handles old messages stored before the unified Tool part was introduced.
      */
      */
+    @Suppress("DEPRECATION")
     private fun AiMessagePart.migrateLegacy(): AiMessagePart {
     private fun AiMessagePart.migrateLegacy(): AiMessagePart {
         return when (this) {
         return when (this) {
             is AiMessagePart.ToolCall -> AiMessagePart.Tool(
             is AiMessagePart.ToolCall -> AiMessagePart.Tool(

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

@@ -8,13 +8,16 @@ import android.content.IntentFilter
 import android.os.Handler
 import android.os.Handler
 import android.os.HandlerThread
 import android.os.HandlerThread
 import android.os.SystemClock
 import android.os.SystemClock
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.OtherSettingsGateway
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.LogUtils
+import org.koin.core.context.GlobalContext
 
 
 object AppFreezeMonitor {
 object AppFreezeMonitor {
 
 
     private const val TAG = "AppFreezeMonitor"
     private const val TAG = "AppFreezeMonitor"
 
 
+    private val otherGateway by lazy { GlobalContext.get().get<OtherSettingsGateway>() }
+
     val handler by lazy {
     val handler by lazy {
         Handler(HandlerThread("AppFreezeMonitor").apply { start() }.looper)
         Handler(HandlerThread("AppFreezeMonitor").apply { start() }.looper)
     }
     }
@@ -28,7 +31,7 @@ object AppFreezeMonitor {
 
 
     @SuppressLint("UnspecifiedRegisterReceiverFlag")
     @SuppressLint("UnspecifiedRegisterReceiverFlag")
     fun init(context: Context) {
     fun init(context: Context) {
-        if (!AppConfig.recordLog) {
+        if (!otherGateway.currentSettings.recordLog) {
             if (registeredReceiver) {
             if (registeredReceiver) {
                 registeredReceiver = false
                 registeredReceiver = false
                 context.unregisterReceiver(screenStatusReceiver)
                 context.unregisterReceiver(screenStatusReceiver)
@@ -60,7 +63,7 @@ object AppFreezeMonitor {
 
 
                 previous = current
                 previous = current
 
 
-                if (AppConfig.recordLog) {
+                if (otherGateway.currentSettings.recordLog) {
                     handler.postDelayed(this, 3000)
                     handler.postDelayed(this, 3000)
                 } else {
                 } else {
                     monitorRunnable = null
                     monitorRunnable = null

+ 14 - 12
app/src/main/java/io/legado/app/help/AppWebDav.kt

@@ -7,7 +7,7 @@ import io.legado.app.data.appDb
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookProgress
 import io.legado.app.data.entities.BookProgress
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.BackupSettingsGateway
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.help.storage.Backup
 import io.legado.app.help.storage.Backup
 import io.legado.app.help.storage.BackupRestoreLock
 import io.legado.app.help.storage.BackupRestoreLock
@@ -17,7 +17,6 @@ import io.legado.app.lib.webdav.WebDav
 import io.legado.app.lib.webdav.WebDavException
 import io.legado.app.lib.webdav.WebDavException
 import io.legado.app.lib.webdav.WebDavFile
 import io.legado.app.lib.webdav.WebDavFile
 import io.legado.app.model.remote.RemoteBookWebDav
 import io.legado.app.model.remote.RemoteBookWebDav
-import io.legado.app.ui.config.backupConfig.BackupConfig
 import io.legado.app.utils.AlphanumComparator
 import io.legado.app.utils.AlphanumComparator
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.GSON
 import io.legado.app.utils.GSON
@@ -33,12 +32,15 @@ import kotlinx.coroutines.ensureActive
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.sync.withLock
 import splitties.init.appCtx
 import splitties.init.appCtx
+import org.koin.core.context.GlobalContext
 import java.io.File
 import java.io.File
 
 
 /**
 /**
  * webDav初始化会访问网络,不要放到主线程
  * webDav初始化会访问网络,不要放到主线程
  */
  */
 object AppWebDav {
 object AppWebDav {
+    private val backupGateway by lazy { GlobalContext.get().get<BackupSettingsGateway>() }
+
     private const val defaultWebDavUrl = "https://dav.jianguoyun.com/dav/"
     private const val defaultWebDavUrl = "https://dav.jianguoyun.com/dav/"
     private val bookProgressUrl get() = "${rootWebDavUrl}bookProgress/"
     private val bookProgressUrl get() = "${rootWebDavUrl}bookProgress/"
     private val exportsWebDavUrl get() = "${rootWebDavUrl}books/"
     private val exportsWebDavUrl get() = "${rootWebDavUrl}books/"
@@ -60,10 +62,10 @@ object AppWebDav {
 
 
     private val rootWebDavUrl: String
     private val rootWebDavUrl: String
         get() {
         get() {
-            val configUrl = BackupConfig.webDavUrl.trim()
+            val configUrl = backupGateway.currentSettings.webDavUrl.trim()
             var url = if (configUrl.isEmpty()) defaultWebDavUrl else configUrl
             var url = if (configUrl.isEmpty()) defaultWebDavUrl else configUrl
             if (!url.endsWith("/")) url = "${url}/"
             if (!url.endsWith("/")) url = "${url}/"
-            AppConfig.webDavDir.trim().let {
+            backupGateway.currentSettings.webDavDir.trim().let {
                 if (it.isNotEmpty()) {
                 if (it.isNotEmpty()) {
                     url = "${url}${it}/"
                     url = "${url}${it}/"
                 }
                 }
@@ -74,10 +76,10 @@ object AppWebDav {
     suspend fun upConfig() {
     suspend fun upConfig() {
         configMutex.withLock {
         configMutex.withLock {
             val config = AppliedWebDavConfig(
             val config = AppliedWebDavConfig(
-                url = BackupConfig.webDavUrl,
-                account = BackupConfig.webDavAccount,
-                password = BackupConfig.webDavPassword,
-                dir = BackupConfig.webDavDir,
+                url = backupGateway.currentSettings.webDavUrl,
+                account = backupGateway.currentSettings.webDavAccount,
+                password = backupGateway.currentSettings.webDavPassword,
+                dir = backupGateway.currentSettings.webDavDir,
             )
             )
             if (appliedConfig == config) return
             if (appliedConfig == config) return
 
 
@@ -176,8 +178,8 @@ object AppWebDav {
 
 
     suspend fun testWebDav(): Boolean {
     suspend fun testWebDav(): Boolean {
         return kotlin.runCatching {
         return kotlin.runCatching {
-            val account = BackupConfig.webDavAccount
-            val password = BackupConfig.webDavPassword
+            val account = backupGateway.currentSettings.webDavAccount
+            val password = backupGateway.currentSettings.webDavPassword
             if (account.isNullOrEmpty() || password.isNullOrEmpty()) {
             if (account.isNullOrEmpty() || password.isNullOrEmpty()) {
                 appCtx.toastOnUi("账号或密码为空")
                 appCtx.toastOnUi("账号或密码为空")
                 return false
                 return false
@@ -289,7 +291,7 @@ object AppWebDav {
         onSuccess: (() -> Unit)? = null
         onSuccess: (() -> Unit)? = null
     ) {
     ) {
         val authorization = authorization ?: return
         val authorization = authorization ?: return
-        if (!AppConfig.syncBookProgress) return
+        if (!backupGateway.currentSettings.syncBookProgress) return
         if (!NetworkUtils.isAvailable()) return
         if (!NetworkUtils.isAvailable()) return
         try {
         try {
             val bookProgress = BookProgress(book)
             val bookProgress = BookProgress(book)
@@ -310,7 +312,7 @@ object AppWebDav {
     ): Boolean {
     ): Boolean {
         try {
         try {
             val authorization = authorization ?: return false
             val authorization = authorization ?: return false
-            if (!AppConfig.syncBookProgress) return false
+            if (!backupGateway.currentSettings.syncBookProgress) return false
             if (!NetworkUtils.isAvailable()) return false
             if (!NetworkUtils.isAvailable()) return false
             val json = GSON.toJson(bookProgress)
             val json = GSON.toJson(bookProgress)
             val url = getProgressUrl(bookProgress.name, bookProgress.author)
             val url = getProgressUrl(bookProgress.name, bookProgress.author)

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

@@ -11,8 +11,9 @@ import android.os.Process
 import android.webkit.WebSettings
 import android.webkit.WebSettings
 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.domain.gateway.BackupSettingsGateway
+import io.legado.app.domain.gateway.OtherSettingsGateway
 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.LocalConfig
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.model.ReadAloud
 import io.legado.app.model.ReadAloud
 import io.legado.app.utils.FileDoc
 import io.legado.app.utils.FileDoc
@@ -25,6 +26,7 @@ import io.legado.app.utils.longToastOnUiLegacy
 import io.legado.app.utils.stackTraceStr
 import io.legado.app.utils.stackTraceStr
 import io.legado.app.utils.writeText
 import io.legado.app.utils.writeText
 import splitties.init.appCtx
 import splitties.init.appCtx
+import org.koin.core.context.GlobalContext
 import java.io.PrintWriter
 import java.io.PrintWriter
 import java.io.StringWriter
 import java.io.StringWriter
 import java.text.SimpleDateFormat
 import java.text.SimpleDateFormat
@@ -85,7 +87,9 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
         LocalConfig.appCrash = true
         LocalConfig.appCrash = true
         //保存日志文件
         //保存日志文件
         val crashFileName = saveCrashInfo2File(ex)
         val crashFileName = saveCrashInfo2File(ex)
-        if ((ex is OutOfMemoryError || ex.cause is OutOfMemoryError) && AppConfig.recordHeapDump) {
+        if ((ex is OutOfMemoryError || ex.cause is OutOfMemoryError) &&
+            otherGateway.currentSettings.recordHeapDump
+        ) {
             doHeapDump()
             doHeapDump()
         }
         }
         return if (startCrashReport(crashFileName)) {
         return if (startCrashReport(crashFileName)) {
@@ -111,6 +115,9 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
     companion object {
     companion object {
         const val EXTRA_CRASH_FILE_NAME = "crashFileName"
         const val EXTRA_CRASH_FILE_NAME = "crashFileName"
 
 
+        private val otherGateway by lazy { GlobalContext.get().get<OtherSettingsGateway>() }
+        private val backupGateway by lazy { GlobalContext.get().get<BackupSettingsGateway>() }
+
         private const val CRASH_REPORT_ACTIVITY = "io.legado.app.ui.about.CrashReportActivity"
         private const val CRASH_REPORT_ACTIVITY = "io.legado.app.ui.about.CrashReportActivity"
 
 
         /**
         /**
@@ -172,7 +179,7 @@ class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
             val time = format.format(Date())
             val time = format.format(Date())
             val fileName = "crash-$time-$timestamp.log"
             val fileName = "crash-$time-$timestamp.log"
             try {
             try {
-                val backupPath = AppConfig.backupPath
+                val backupPath = backupGateway.currentSettings.backupPath
                     ?: throw NoStackTraceException("备份路径未配置")
                     ?: throw NoStackTraceException("备份路径未配置")
                 val uri = Uri.parse(backupPath)
                 val uri = Uri.parse(backupPath)
                 val fileDoc = FileDoc.fromUri(uri, true)
                 val fileDoc = FileDoc.fromUri(uri, true)

+ 5 - 2
app/src/main/java/io/legado/app/help/DispatchersMonitor.kt

@@ -1,7 +1,8 @@
 package io.legado.app.help
 package io.legado.app.help
 
 
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.OtherSettingsGateway
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.LogUtils
+import org.koin.core.context.GlobalContext
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers.Default
 import kotlinx.coroutines.Dispatchers.Default
@@ -22,6 +23,8 @@ object DispatchersMonitor {
 
 
     private const val TAG = "DispatchersMonitor"
     private const val TAG = "DispatchersMonitor"
 
 
+    private val otherGateway by lazy { GlobalContext.get().get<OtherSettingsGateway>() }
+
     private val dispatcher by lazy {
     private val dispatcher by lazy {
         Executors.newSingleThreadExecutor {
         Executors.newSingleThreadExecutor {
             Thread(it, TAG)
             Thread(it, TAG)
@@ -32,7 +35,7 @@ object DispatchersMonitor {
 
 
     fun init() {
     fun init() {
         scope.coroutineContext.cancelChildren()
         scope.coroutineContext.cancelChildren()
-        if (!AppConfig.recordLog) {
+        if (!otherGateway.currentSettings.recordLog) {
             return
             return
         }
         }
         monitor(IO)
         monitor(IO)

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

@@ -12,9 +12,9 @@ import io.legado.app.constant.AppLog
 import io.legado.app.constant.AppPattern
 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.domain.gateway.AppShellSettingsGateway
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.help.config.ThemeConfigStore
 import io.legado.app.help.config.ThemeConfigStore
-import io.legado.app.ui.config.themeConfig.ThemeConfig
 import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.help.crypto.base64ToByteArray
 import io.legado.app.help.crypto.base64ToByteArray
 import io.legado.app.help.crypto.digest
 import io.legado.app.help.crypto.digest
@@ -32,7 +32,6 @@ import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.analyzeRule.QueryTTF
 import io.legado.app.model.analyzeRule.QueryTTF
 import io.legado.app.ui.association.OnLineImportActivity
 import io.legado.app.ui.association.OnLineImportActivity
 import io.legado.app.ui.association.OpenUrlConfirmActivity
 import io.legado.app.ui.association.OpenUrlConfirmActivity
-import io.legado.app.ui.config.otherConfig.OtherConfig
 import io.legado.app.utils.ArchiveUtils
 import io.legado.app.utils.ArchiveUtils
 import io.legado.app.utils.ChineseUtils
 import io.legado.app.utils.ChineseUtils
 import io.legado.app.utils.EncoderUtils
 import io.legado.app.utils.EncoderUtils
@@ -65,6 +64,7 @@ import okio.use
 import org.jsoup.Connection
 import org.jsoup.Connection
 import org.jsoup.Jsoup
 import org.jsoup.Jsoup
 import splitties.init.appCtx
 import splitties.init.appCtx
+import org.koin.core.context.GlobalContext
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayOutputStream
 import java.io.ByteArrayOutputStream
 import java.io.File
 import java.io.File
@@ -93,6 +93,11 @@ interface JsExtensions : JsEncodeUtils {
     fun getSource(): BaseSource?
     fun getSource(): BaseSource?
     fun getTag(): String?
     fun getTag(): String?
 
 
+    private val cacheGateway
+        get() = GlobalContext.get().get<DownloadCacheSettingsGateway>()
+    private val shellGateway
+        get() = GlobalContext.get().get<AppShellSettingsGateway>()
+
     private val context: CoroutineContext
     private val context: CoroutineContext
         get() = rhinoContextOrNull?.coroutineContext ?: EmptyCoroutineContext
         get() = rhinoContextOrNull?.coroutineContext ?: EmptyCoroutineContext
 
 
@@ -133,7 +138,7 @@ interface JsExtensions : JsEncodeUtils {
     }
     }
     fun ajaxAll(urlList: Array<String>, skipRateLimit: Boolean): Array<StrResponse> {
     fun ajaxAll(urlList: Array<String>, skipRateLimit: Boolean): Array<StrResponse> {
         return runBlocking(context) {
         return runBlocking(context) {
-            urlList.asFlow().mapAsync(OtherConfig.threadCount) { url ->
+            urlList.asFlow().mapAsync(cacheGateway.currentSettings.threadCount) { url ->
                 val analyzeUrl = AnalyzeUrl(
                 val analyzeUrl = AnalyzeUrl(
                     url,
                     url,
                     source = getSource(),
                     source = getSource(),
@@ -156,7 +161,7 @@ interface JsExtensions : JsEncodeUtils {
         skipRateLimit: Boolean
         skipRateLimit: Boolean
     ): Array<StrResponse> {
     ): Array<StrResponse> {
         return runBlocking(context) {
         return runBlocking(context) {
-            urlList.asFlow().mapAsync(OtherConfig.threadCount) { url ->
+            urlList.asFlow().mapAsync(cacheGateway.currentSettings.threadCount) { url ->
                 val analyzeUrl = AnalyzeUrl(
                 val analyzeUrl = AnalyzeUrl(
                     url,
                     url,
                     source = getSource(),
                     source = getSource(),
@@ -232,7 +237,7 @@ interface JsExtensions : JsEncodeUtils {
                 url = url,
                 url = url,
                 html = html,
                 html = html,
                 javaScript = js,
                 javaScript = js,
-                headerMap = getSource()?.getHeaderMap(AppConfig.userAgent, true),
+                headerMap = getSource()?.getHeaderMap(cacheGateway.currentSettings.userAgent, true),
                 tag = getSource()?.getKey()
                 tag = getSource()?.getKey()
             ).getStrResponse().body
             ).getStrResponse().body
         }
         }
@@ -271,7 +276,7 @@ interface JsExtensions : JsEncodeUtils {
                 url = url,
                 url = url,
                 html = html,
                 html = html,
                 javaScript = js,
                 javaScript = js,
-                headerMap = getSource()?.getHeaderMap(AppConfig.userAgent, true),
+                headerMap = getSource()?.getHeaderMap(cacheGateway.currentSettings.userAgent, true),
                 tag = getSource()?.getKey(),
                 tag = getSource()?.getKey(),
                 sourceRegex = sourceRegex,
                 sourceRegex = sourceRegex,
                 delayTime = delayTime
                 delayTime = delayTime
@@ -317,7 +322,7 @@ interface JsExtensions : JsEncodeUtils {
                 url = url,
                 url = url,
                 html = html,
                 html = html,
                 javaScript = js,
                 javaScript = js,
-                headerMap = getSource()?.getHeaderMap(AppConfig.userAgent, true),
+                headerMap = getSource()?.getHeaderMap(cacheGateway.currentSettings.userAgent, true),
                 tag = getSource()?.getKey(),
                 tag = getSource()?.getKey(),
                 overrideUrlRegex = overrideUrlRegex,
                 overrideUrlRegex = overrideUrlRegex,
                 delayTime = delayTime
                 delayTime = delayTime
@@ -1196,7 +1201,7 @@ interface JsExtensions : JsEncodeUtils {
      */
      */
     @JavascriptInterface
     @JavascriptInterface
     fun getThemeMode(): String {
     fun getThemeMode(): String {
-        return ThemeConfig.themeMode
+        return shellGateway.currentSettings.themeMode
     }
     }
 
 
     /**
     /**

+ 12 - 9
app/src/main/java/io/legado/app/help/WebDavManager.kt

@@ -6,7 +6,7 @@ import io.legado.app.data.appDb
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookProgress
 import io.legado.app.data.entities.BookProgress
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.BackupSettingsGateway
 import io.legado.app.help.config.AppConfigStore
 import io.legado.app.help.config.AppConfigStore
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.help.storage.Backup
 import io.legado.app.help.storage.Backup
@@ -17,7 +17,6 @@ import io.legado.app.lib.webdav.WebDav
 import io.legado.app.lib.webdav.WebDavException
 import io.legado.app.lib.webdav.WebDavException
 import io.legado.app.lib.webdav.WebDavFile
 import io.legado.app.lib.webdav.WebDavFile
 import io.legado.app.model.remote.RemoteBookWebDav
 import io.legado.app.model.remote.RemoteBookWebDav
-import io.legado.app.ui.config.backupConfig.BackupConfig
 import io.legado.app.utils.AlphanumComparator
 import io.legado.app.utils.AlphanumComparator
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.GSON
 import io.legado.app.utils.GSON
@@ -41,6 +40,7 @@ import kotlinx.coroutines.flow.debounce
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withContext
+import org.koin.core.context.GlobalContext
 import java.io.File
 import java.io.File
 import kotlin.coroutines.cancellation.CancellationException
 import kotlin.coroutines.cancellation.CancellationException
 
 
@@ -72,6 +72,8 @@ class WebDavManager(
     private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
     private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
 ) {
 ) {
 
 
+    private val backupGateway by lazy { GlobalContext.get().get<BackupSettingsGateway>() }
+
     private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
     private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
     private val configFlow = MutableStateFlow(readConfig())
     private val configFlow = MutableStateFlow(readConfig())
     private val _state = MutableStateFlow<WebDavState>(WebDavState.Unconfigured)
     private val _state = MutableStateFlow<WebDavState>(WebDavState.Unconfigured)
@@ -213,7 +215,7 @@ class WebDavManager(
         onSuccess: (() -> Unit)? = null
         onSuccess: (() -> Unit)? = null
     ) {
     ) {
         val auth = requireAuthorization()
         val auth = requireAuthorization()
-        if (!AppConfig.syncBookProgress) return
+        if (!backupGateway.currentSettings.syncBookProgress) return
         if (!NetworkUtils.isAvailable()) return
         if (!NetworkUtils.isAvailable()) return
         try {
         try {
             val bookProgress = BookProgress(book)
             val bookProgress = BookProgress(book)
@@ -231,7 +233,7 @@ class WebDavManager(
 
 
     suspend fun uploadBookProgress(bookProgress: BookProgress, onSuccess: (() -> Unit)? = null) {
     suspend fun uploadBookProgress(bookProgress: BookProgress, onSuccess: (() -> Unit)? = null) {
         val auth = requireAuthorization()
         val auth = requireAuthorization()
-        if (!AppConfig.syncBookProgress) return
+        if (!backupGateway.currentSettings.syncBookProgress) return
         if (!NetworkUtils.isAvailable()) return
         if (!NetworkUtils.isAvailable()) return
         val json = GSON.toJson(bookProgress)
         val json = GSON.toJson(bookProgress)
         val url = getProgressUrl(bookProgress.name, bookProgress.author)
         val url = getProgressUrl(bookProgress.name, bookProgress.author)
@@ -285,12 +287,13 @@ class WebDavManager(
     }
     }
 
 
     private fun readConfig(): WebDavConfig {
     private fun readConfig(): WebDavConfig {
+        val settings = backupGateway.currentSettings
         return WebDavConfig(
         return WebDavConfig(
-            url = BackupConfig.webDavUrl,
-            account = BackupConfig.webDavAccount,
-            password = BackupConfig.webDavPassword,
-            dir = BackupConfig.webDavDir,
-            deviceName = BackupConfig.webDavDeviceName
+            url = settings.webDavUrl,
+            account = settings.webDavAccount,
+            password = settings.webDavPassword,
+            dir = settings.webDavDir,
+            deviceName = settings.webDavDeviceName
         )
         )
     }
     }
 
 

+ 16 - 9
app/src/main/java/io/legado/app/help/book/BookExtensions.kt

@@ -21,10 +21,12 @@ import io.legado.app.data.entities.HighlightTagRule
 import io.legado.app.data.entities.TagGroupRule
 import io.legado.app.data.entities.TagGroupRule
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.RuleBigDataHelp
 import io.legado.app.help.RuleBigDataHelp
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.BookExportSettingsGateway
+import io.legado.app.domain.gateway.ImportBookSettingsGateway
+import io.legado.app.domain.gateway.OtherSettingsGateway
+import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.ui.book.info.HighlightedTag
 import io.legado.app.ui.book.info.HighlightedTag
-import io.legado.app.ui.config.otherConfig.OtherConfig
 import io.legado.app.utils.FileDoc
 import io.legado.app.utils.FileDoc
 import io.legado.app.utils.GSON
 import io.legado.app.utils.GSON
 import io.legado.app.utils.MD5Utils
 import io.legado.app.utils.MD5Utils
@@ -45,7 +47,12 @@ import kotlinx.datetime.daysUntil
 import kotlinx.datetime.todayIn
 import kotlinx.datetime.todayIn
 import kotlin.math.max
 import kotlin.math.max
 import kotlin.math.min
 import kotlin.math.min
+import org.koin.core.context.GlobalContext
 
 
+private val otherGateway by lazy { GlobalContext.get().get<OtherSettingsGateway>() }
+private val importBookGateway by lazy { GlobalContext.get().get<ImportBookSettingsGateway>() }
+private val readGateway by lazy { GlobalContext.get().get<ReadSettingsGateway>() }
+private val exportGateway by lazy { GlobalContext.get().get<BookExportSettingsGateway>() }
 
 
 val Book.isAudio: Boolean
 val Book.isAudio: Boolean
     get() = isType(BookType.audio)
     get() = isType(BookType.audio)
@@ -189,8 +196,8 @@ fun Book.getLocalUri(): Uri {
         return uri
         return uri
     }
     }
     //不同的设备书籍保存路径可能不一样, uri无效时尝试寻找当前保存路径下的文件
     //不同的设备书籍保存路径可能不一样, uri无效时尝试寻找当前保存路径下的文件
-    val defaultBookDir = OtherConfig.defaultBookTreeUri
-    val importBookDir = AppConfig.importBookPath
+    val defaultBookDir = otherGateway.currentSettings.defaultBookTreeUri
+    val importBookDir = importBookGateway.currentSettings.importBookPath
 
 
     // 查找书籍保存目录
     // 查找书籍保存目录
     if (!defaultBookDir.isNullOrBlank()) {
     if (!defaultBookDir.isNullOrBlank()) {
@@ -261,7 +268,7 @@ fun Book.getLocalUri(): Uri {
 
 
 
 
 fun Book.getArchiveUri(): Uri? {
 fun Book.getArchiveUri(): Uri? {
-    val defaultBookDir = OtherConfig.defaultBookTreeUri
+    val defaultBookDir = otherGateway.currentSettings.defaultBookTreeUri
     return if (isArchive && !defaultBookDir.isNullOrBlank()) {
     return if (isArchive && !defaultBookDir.isNullOrBlank()) {
         FileDoc.fromUri(defaultBookDir.toUri(), true)
         FileDoc.fromUri(defaultBookDir.toUri(), true)
             .find(archiveName)?.uri
             .find(archiveName)?.uri
@@ -542,8 +549,8 @@ fun Book.sync(oldBook: Book) {
         appDb.bookChapterDao.getChapter(bookUrl, durChapterIndex)?.let {
         appDb.bookChapterDao.getChapter(bookUrl, durChapterIndex)?.let {
             durChapterTitle = it.getDisplayTitle(
             durChapterTitle = it.getDisplayTitle(
                 replaceRules,
                 replaceRules,
-                getUseReplaceRule(AppConfig.replaceEnableDefault),
-                chineseConverterType = AppConfig.chineseConverterType,
+                getUseReplaceRule(otherGateway.currentSettings.replaceEnableDefault),
+                chineseConverterType = readGateway.currentSettings.chineseConverterType,
             )
             )
         }
         }
     }
     }
@@ -619,7 +626,7 @@ fun Book.isSameNameAuthor(other: Any?): Boolean {
 
 
 fun Book.getExportFileName(
 fun Book.getExportFileName(
     suffix: String,
     suffix: String,
-    template: String? = AppConfig.bookExportFileName,
+    template: String? = exportGateway.currentSettings.bookExportFileName,
 ): String {
 ): String {
     if (template.isNullOrBlank()) {
     if (template.isNullOrBlank()) {
         return "$name 作者:${getRealAuthor()}.$suffix"
         return "$name 作者:${getRealAuthor()}.$suffix"
@@ -672,7 +679,7 @@ fun Book.getExportFileName(
 fun Book.getExportFileName(
 fun Book.getExportFileName(
     suffix: String,
     suffix: String,
     epubIndex: Int,
     epubIndex: Int,
-    jsStr: String? = AppConfig.episodeExportFileName
+    jsStr: String? = exportGateway.currentSettings.episodeExportFileName
 ): String {
 ): String {
     // 默认规则
     // 默认规则
     val default = "$name 作者:${getRealAuthor()} [${epubIndex}].$suffix"
     val default = "$name 作者:${getRealAuthor()} [${epubIndex}].$suffix"

+ 11 - 7
app/src/main/java/io/legado/app/help/book/BookHelp.kt

@@ -15,8 +15,8 @@ import io.legado.app.data.entities.BookSource
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.model.localBook.TextFile
 import io.legado.app.model.localBook.TextFile
-import io.legado.app.ui.config.otherConfig.OtherConfig
-import io.legado.app.ui.config.readConfig.ReadConfig
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
+import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.utils.ArchiveUtils
 import io.legado.app.utils.ArchiveUtils
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.ImageUtils
 import io.legado.app.utils.ImageUtils
@@ -46,6 +46,7 @@ import kotlinx.coroutines.sync.Semaphore
 import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withContext
 import org.apache.commons.text.similarity.JaccardSimilarity
 import org.apache.commons.text.similarity.JaccardSimilarity
+import org.koin.core.context.GlobalContext
 import splitties.init.appCtx
 import splitties.init.appCtx
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayInputStream
 import java.io.File
 import java.io.File
@@ -61,6 +62,9 @@ import kotlin.math.min
 
 
 @Suppress("unused", "ConstPropertyName")
 @Suppress("unused", "ConstPropertyName")
 object BookHelp {
 object BookHelp {
+    private val readGateway by lazy { GlobalContext.get().get<ReadSettingsGateway>() }
+    private val cacheGateway by lazy { GlobalContext.get().get<DownloadCacheSettingsGateway>() }
+
     private const val CACHED_CONTENT_PREFIX_LENGTH = 1_024
     private const val CACHED_CONTENT_PREFIX_LENGTH = 1_024
     private val downloadDir: File = appCtx.externalFiles
     private val downloadDir: File = appCtx.externalFiles
     private const val cacheFolderName = "book_cache"
     private const val cacheFolderName = "book_cache"
@@ -136,12 +140,12 @@ object BookHelp {
     private fun clearComicCache(book: Book) {
     private fun clearComicCache(book: Book) {
         //只处理漫画
         //只处理漫画
         //为0的时候,不清除已缓存数据
         //为0的时候,不清除已缓存数据
-        if (!book.isImage || ReadConfig.imageRetainNum == 0) {
+        if (!book.isImage || cacheGateway.currentSettings.imageRetainNum == 0) {
             return
             return
         }
         }
         //向前保留设定数量,向后保留预下载数量
         //向前保留设定数量,向后保留预下载数量
-        val startIndex = book.durChapterIndex - ReadConfig.imageRetainNum
-        val endIndex = book.durChapterIndex + ReadConfig.preDownloadNum
+        val startIndex = book.durChapterIndex - cacheGateway.currentSettings.imageRetainNum
+        val endIndex = book.durChapterIndex + readGateway.currentSettings.preDownloadNum
         val chapterList = appDb.bookChapterDao.getChapterList(book.bookUrl, startIndex, endIndex)
         val chapterList = appDb.bookChapterDao.getChapterList(book.bookUrl, startIndex, endIndex)
         val imgNames = hashSetOf<String>()
         val imgNames = hashSetOf<String>()
         //获取需要保留章节的图片信息
         //获取需要保留章节的图片信息
@@ -204,7 +208,7 @@ object BookHelp {
             book.getFolderName(),
             book.getFolderName(),
             bookChapter.getFileName(),
             bookChapter.getFileName(),
         ).writeText(content)
         ).writeText(content)
-        if (book.isOnLineTxt && ReadConfig.tocCountWords) {
+        if (book.isOnLineTxt && readGateway.currentSettings.tocCountWords) {
             val wordCount = StringUtils.wordCountFormat(content.length)
             val wordCount = StringUtils.wordCountFormat(content.length)
             bookChapter.wordCount = wordCount
             bookChapter.wordCount = wordCount
             appDb.bookChapterDao.update(bookChapter)
             appDb.bookChapterDao.update(bookChapter)
@@ -302,7 +306,7 @@ object BookHelp {
         book: Book,
         book: Book,
         bookChapter: BookChapter,
         bookChapter: BookChapter,
         content: String,
         content: String,
-        concurrency: Int = OtherConfig.threadCount,
+        concurrency: Int = cacheGateway.currentSettings.threadCount,
         onProgress: (suspend (completed: Int, total: Int) -> Unit)? = null,
         onProgress: (suspend (completed: Int, total: Int) -> Unit)? = null,
     ): Int = coroutineScope {
     ): Int = coroutineScope {
         val imageUrls = flowImages(bookChapter, content).toList()
         val imageUrls = flowImages(bookChapter, content).toList()

+ 13 - 8
app/src/main/java/io/legado/app/help/book/ContentProcessor.kt

@@ -14,7 +14,8 @@ import io.legado.app.domain.model.BookContentProcessEngine
 import io.legado.app.domain.model.TextProcessAction
 import io.legado.app.domain.model.TextProcessAction
 import io.legado.app.domain.model.TextProcessStyle
 import io.legado.app.domain.model.TextProcessStyle
 import io.legado.app.exception.RegexTimeoutException
 import io.legado.app.exception.RegexTimeoutException
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.OtherSettingsGateway
+import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.utils.ChineseUtils
 import io.legado.app.utils.ChineseUtils
 import io.legado.app.utils.GSON
 import io.legado.app.utils.GSON
@@ -24,6 +25,7 @@ import io.legado.app.utils.replace
 import io.legado.app.utils.stackTraceStr
 import io.legado.app.utils.stackTraceStr
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.toastOnUi
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CancellationException
+import org.koin.core.context.GlobalContext
 import splitties.init.appCtx
 import splitties.init.appCtx
 import java.lang.ref.WeakReference
 import java.lang.ref.WeakReference
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.CopyOnWriteArrayList
@@ -61,6 +63,9 @@ class ContentProcessor private constructor(
     private val contentReplaceRules = CopyOnWriteArrayList<ReplaceRule>()
     private val contentReplaceRules = CopyOnWriteArrayList<ReplaceRule>()
     val removeSameTitleCache = hashSetOf<String>()
     val removeSameTitleCache = hashSetOf<String>()
 
 
+    private val otherGateway by lazy { GlobalContext.get().get<OtherSettingsGateway>() }
+    private val readGateway by lazy { GlobalContext.get().get<ReadSettingsGateway>() }
+
     init {
     init {
         upReplaceRules()
         upReplaceRules()
         upRemoveSameTitle()
         upRemoveSameTitle()
@@ -119,12 +124,12 @@ class ContentProcessor private constructor(
                 if (match != null) {
                 if (match != null) {
                     mContent = mContent.substring(match.range.last + 1)
                     mContent = mContent.substring(match.range.last + 1)
                     sameTitleRemoved = true
                     sameTitleRemoved = true
-                } else if (useReplace && book.getUseReplaceRule(AppConfig.replaceEnableDefault)) {
+                } else if (useReplace && book.getUseReplaceRule(otherGateway.currentSettings.replaceEnableDefault)) {
                     title = Regex.escape(
                     title = Regex.escape(
                         chapter.getDisplayTitle(
                         chapter.getDisplayTitle(
                             contentReplaceRules,
                             contentReplaceRules,
                             chineseConvert = false,
                             chineseConvert = false,
-                            chineseConverterType = AppConfig.chineseConverterType,
+                            chineseConverterType = readGateway.currentSettings.chineseConverterType,
                         )
                         )
                     )
                     )
                     match = Regex("^(\\s|\\p{P}|${name})*${title}(\\s)*")
                     match = Regex("^(\\s|\\p{P}|${name})*${title}(\\s)*")
@@ -144,7 +149,7 @@ class ContentProcessor private constructor(
             if (chineseConvert) {
             if (chineseConvert) {
                 //简繁转换
                 //简繁转换
                 try {
                 try {
-                    when (AppConfig.chineseConverterType) {
+                    when (readGateway.currentSettings.chineseConverterType) {
                         1 -> mContent = ChineseUtils.t2s(mContent)
                         1 -> mContent = ChineseUtils.t2s(mContent)
                         2 -> mContent = ChineseUtils.s2t(mContent)
                         2 -> mContent = ChineseUtils.s2t(mContent)
                     }
                     }
@@ -153,14 +158,14 @@ class ContentProcessor private constructor(
                 }
                 }
             }
             }
             val useHtmlMap = mutableMapOf<String, String>()
             val useHtmlMap = mutableMapOf<String, String>()
-            if (AppConfig.adaptSpecialStyle) { //html处理
+            if (readGateway.currentSettings.adaptSpecialStyle) { //html处理
                 mContent = AppPattern.useHtmlRegex.replace(mContent) { matchResult ->
                 mContent = AppPattern.useHtmlRegex.replace(mContent) { matchResult ->
                     val placeholder = "特殊格式的占位不应该被看见${useHtmlMap.size}。"
                     val placeholder = "特殊格式的占位不应该被看见${useHtmlMap.size}。"
                     useHtmlMap[placeholder] = "\n${matchResult.value.replace("\n", "")}\n"
                     useHtmlMap[placeholder] = "\n${matchResult.value.replace("\n", "")}\n"
                     placeholder
                     placeholder
                 }
                 }
             }
             }
-            if (useReplace && book.getUseReplaceRule(AppConfig.replaceEnableDefault)) {
+            if (useReplace && book.getUseReplaceRule(otherGateway.currentSettings.replaceEnableDefault)) {
                 val replaceBook = book.toSearchBook()
                 val replaceBook = book.toSearchBook()
                 //替换
                 //替换
                 effectiveReplaceRules = arrayListOf()
                 effectiveReplaceRules = arrayListOf()
@@ -220,8 +225,8 @@ class ContentProcessor private constructor(
             //重新添加标题
             //重新添加标题
             mContent = chapter.getDisplayTitle(
             mContent = chapter.getDisplayTitle(
                 getTitleReplaceRules(),
                 getTitleReplaceRules(),
-                useReplace = useReplace && book.getUseReplaceRule(AppConfig.replaceEnableDefault),
-                chineseConverterType = AppConfig.chineseConverterType,
+                useReplace = useReplace && book.getUseReplaceRule(otherGateway.currentSettings.replaceEnableDefault),
+                chineseConverterType = readGateway.currentSettings.chineseConverterType,
             ) + "\n" + mContent
             ) + "\n" + mContent
         }
         }
         if (isAndroid8) {
         if (isAndroid8) {

+ 1 - 2
app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt

@@ -11,7 +11,6 @@ import io.legado.app.data.entities.HighlightRule
 import io.legado.app.data.repository.ReadStyleConfigStore
 import io.legado.app.data.repository.ReadStyleConfigStore
 import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.model.ReadSessionState
 import io.legado.app.model.ReadSessionState
-import io.legado.app.ui.config.readConfig.ReadConfig
 import io.legado.app.utils.GSON
 import io.legado.app.utils.GSON
 import io.legado.app.utils.fromJsonObject
 import io.legado.app.utils.fromJsonObject
 import io.legado.app.utils.hexString
 import io.legado.app.utils.hexString
@@ -154,7 +153,7 @@ object ReadBookConfig {
     val resolvedMenuBgColor: Int
     val resolvedMenuBgColor: Int
         get() {
         get() {
             val isNight = ReadStyleResolver.isNightTheme()
             val isNight = ReadStyleResolver.isNightTheme()
-            return when (ReadConfig.readBarStyle) {
+            return when (readSettings.readBarStyle) {
                 1 -> { // 跟随阅读背景
                 1 -> { // 跟随阅读背景
                     val background = ReadStyleResolver.currentBackground(durConfig)
                     val background = ReadStyleResolver.currentBackground(durConfig)
                     if (background.type == 0) {
                     if (background.type == 0) {

+ 22 - 5
app/src/main/java/io/legado/app/help/config/ReadStyleResolver.kt

@@ -5,18 +5,35 @@ import android.graphics.drawable.Drawable
 import androidx.core.graphics.drawable.toDrawable
 import androidx.core.graphics.drawable.toDrawable
 import androidx.core.graphics.toColorInt
 import androidx.core.graphics.toColorInt
 import com.google.android.material.color.MaterialColors
 import com.google.android.material.color.MaterialColors
+import io.legado.app.domain.gateway.AppShellSettingsGateway
+import io.legado.app.domain.gateway.ThemeSettingsGateway
 import io.legado.app.model.ReadSessionState
 import io.legado.app.model.ReadSessionState
-import io.legado.app.ui.config.readConfig.ReadConfig
 import io.legado.app.utils.BitmapUtils
 import io.legado.app.utils.BitmapUtils
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.externalFiles
 import io.legado.app.utils.externalFiles
+import io.legado.app.utils.isNightMode
 import io.legado.app.utils.printOnDebug
 import io.legado.app.utils.printOnDebug
 import io.legado.app.utils.resizeAndRecycle
 import io.legado.app.utils.resizeAndRecycle
+import io.legado.app.utils.sysConfiguration
 import splitties.init.appCtx
 import splitties.init.appCtx
+import org.koin.core.context.GlobalContext
 import java.io.File
 import java.io.File
 
 
 object ReadStyleResolver {
 object ReadStyleResolver {
 
 
+    private val shellGateway by lazy { GlobalContext.get().get<AppShellSettingsGateway>() }
+    private val themeGateway by lazy { GlobalContext.get().get<ThemeSettingsGateway>() }
+
+    private val isNightThemeCompat: Boolean
+        get() = when (shellGateway.currentSettings.themeMode) {
+            "1" -> false
+            "2" -> true
+            else -> sysConfiguration.isNightMode
+        }
+
+    private val isEInkModeCompat: Boolean
+        get() = themeGateway.currentSettings.appTheme == "4"
+
     enum class ReadStyleMode {
     enum class ReadStyleMode {
         Day,
         Day,
         Night,
         Night,
@@ -29,16 +46,16 @@ object ReadStyleResolver {
     )
     )
 
 
     fun currentMode(): ReadStyleMode {
     fun currentMode(): ReadStyleMode {
-        val isNightTheme = ReadSessionState.isDarkThemeOverride ?: ReadConfig.isNightTheme
-        return resolveReadStyleMode(ReadConfig.isEInkMode, isNightTheme)
+        val isNightTheme = ReadSessionState.isDarkThemeOverride ?: isNightThemeCompat
+        return resolveReadStyleMode(isEInkModeCompat, isNightTheme)
     }
     }
 
 
     fun currentMode(isNightTheme: Boolean): ReadStyleMode {
     fun currentMode(isNightTheme: Boolean): ReadStyleMode {
-        return resolveReadStyleMode(ReadConfig.isEInkMode, isNightTheme)
+        return resolveReadStyleMode(isEInkModeCompat, isNightTheme)
     }
     }
 
 
     fun isNightTheme(): Boolean {
     fun isNightTheme(): Boolean {
-        return ReadSessionState.isDarkThemeOverride ?: ReadConfig.isNightTheme
+        return ReadSessionState.isDarkThemeOverride ?: isNightThemeCompat
     }
     }
 
 
     fun withCurrentBackground(
     fun withCurrentBackground(

+ 41 - 18
app/src/main/java/io/legado/app/help/config/ThemeConfigStore.kt

@@ -4,10 +4,12 @@ import android.content.Context
 import android.graphics.Bitmap
 import android.graphics.Bitmap
 import android.util.DisplayMetrics
 import android.util.DisplayMetrics
 import androidx.annotation.Keep
 import androidx.annotation.Keep
+import androidx.appcompat.app.AppCompatDelegate
 import androidx.core.graphics.toColorInt
 import androidx.core.graphics.toColorInt
+import io.legado.app.domain.gateway.AppShellSettingsGateway
+import io.legado.app.domain.gateway.ThemeSettingsGateway
 import io.legado.app.ui.book.read.ConfigUpdateAction
 import io.legado.app.ui.book.read.ConfigUpdateAction
 import io.legado.app.ui.book.read.ReadConfigUpdateBus
 import io.legado.app.ui.book.read.ReadConfigUpdateBus
-import io.legado.app.ui.config.themeConfig.ThemeConfig
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.constant.PreferKey
 import io.legado.app.constant.PreferKey
 import io.legado.app.constant.Theme
 import io.legado.app.constant.Theme
@@ -23,8 +25,11 @@ import io.legado.app.utils.getFile
 import io.legado.app.utils.getPrefInt
 import io.legado.app.utils.getPrefInt
 import io.legado.app.utils.getPrefString
 import io.legado.app.utils.getPrefString
 import io.legado.app.utils.hexString
 import io.legado.app.utils.hexString
+import io.legado.app.utils.isNightMode
 import io.legado.app.utils.printOnDebug
 import io.legado.app.utils.printOnDebug
 import io.legado.app.utils.stackBlur
 import io.legado.app.utils.stackBlur
+import io.legado.app.utils.sysConfiguration
+import org.koin.core.context.GlobalContext
 import splitties.init.appCtx
 import splitties.init.appCtx
 import java.io.File
 import java.io.File
 
 
@@ -33,13 +38,26 @@ object ThemeConfigStore {
     const val configFileName = "themeConfig.json"
     const val configFileName = "themeConfig.json"
     val configFilePath = FileUtils.getPath(appCtx.filesDir, configFileName)
     val configFilePath = FileUtils.getPath(appCtx.filesDir, configFileName)
 
 
+    private val themeSettingsGateway: ThemeSettingsGateway
+        get() = GlobalContext.get().get()
+
+    private val shellSettingsGateway: AppShellSettingsGateway
+        get() = GlobalContext.get().get()
+
+    val isNightTheme: Boolean
+        get() = when (shellSettingsGateway.currentSettings.themeMode) {
+            "1" -> false
+            "2" -> true
+            else -> sysConfiguration.isNightMode
+        }
+
     val configList: ArrayList<Config> by lazy {
     val configList: ArrayList<Config> by lazy {
         val cList = getConfigs() ?: DefaultData.themeConfigs
         val cList = getConfigs() ?: DefaultData.themeConfigs
         ArrayList(cList)
         ArrayList(cList)
     }
     }
 
 
     fun getTheme() = when {
     fun getTheme() = when {
-        AppConfig.isNightTheme -> Theme.Dark
+        isNightTheme -> Theme.Dark
         else -> Theme.Light
         else -> Theme.Light
     }
     }
 
 
@@ -62,19 +80,25 @@ object ThemeConfigStore {
     }
     }
 
 
     fun initNightMode() {
     fun initNightMode() {
-        ThemeConfig.initNightMode()
+        val mode = when (shellSettingsGateway.currentSettings.themeMode) {
+            "1" -> AppCompatDelegate.MODE_NIGHT_NO
+            "2" -> AppCompatDelegate.MODE_NIGHT_YES
+            else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
+        }
+        AppCompatDelegate.setDefaultNightMode(mode)
     }
     }
 
 
     fun getBgImage(context: Context, metrics: DisplayMetrics): Bitmap? {
     fun getBgImage(context: Context, metrics: DisplayMetrics): Bitmap? {
+        val theme = themeSettingsGateway.currentSettings
         val bgCfg = when (getTheme()) {
         val bgCfg = when (getTheme()) {
             Theme.Light -> Pair(
             Theme.Light -> Pair(
-                ThemeConfig.bgImageLight,
-                ThemeConfig.bgImageBlurring
+                theme.backgroundImageLight,
+                theme.backgroundImageBlurring
             )
             )
 
 
             Theme.Dark -> Pair(
             Theme.Dark -> Pair(
-                ThemeConfig.bgImageDark,
-                ThemeConfig.bgImageNBlurring
+                theme.backgroundImageDark,
+                theme.backgroundImageDarkBlurring
             )
             )
 
 
             else -> null
             else -> null
@@ -153,13 +177,14 @@ object ThemeConfigStore {
      * 清理无用背景图片
      * 清理无用背景图片
      */
      */
     fun clearBg() {
     fun clearBg() {
-        val bgImagePath = ThemeConfig.bgImageLight
+        val theme = themeSettingsGateway.currentSettings
+        val bgImagePath = theme.backgroundImageLight
         appCtx.externalFiles.getFile(PreferKey.bgImage).listFiles()?.forEach {
         appCtx.externalFiles.getFile(PreferKey.bgImage).listFiles()?.forEach {
             if (it.absolutePath != bgImagePath) {
             if (it.absolutePath != bgImagePath) {
                 it.delete()
                 it.delete()
             }
             }
         }
         }
-        val bgImageNPath = ThemeConfig.bgImageDark
+        val bgImageNPath = theme.backgroundImageDark
         appCtx.externalFiles.getFile(PreferKey.bgImageN).listFiles()?.forEach {
         appCtx.externalFiles.getFile(PreferKey.bgImageN).listFiles()?.forEach {
             if (it.absolutePath != bgImageNPath) {
             if (it.absolutePath != bgImageNPath) {
                 it.delete()
                 it.delete()
@@ -168,7 +193,7 @@ object ThemeConfigStore {
     }
     }
 
 
     fun getDurConfig(context: Context): Config {
     fun getDurConfig(context: Context): Config {
-        val isNight = AppConfig.isNightTheme
+        val isNight = isNightTheme
         val name = if (isNight) {
         val name = if (isNight) {
             "MD3-Night"
             "MD3-Night"
         } else {
         } else {
@@ -190,10 +215,9 @@ object ThemeConfigStore {
             context.getPrefInt(PreferKey.cBackground, context.getCompatColor(R.color.md_grey_100))
             context.getPrefInt(PreferKey.cBackground, context.getCompatColor(R.color.md_grey_100))
         val bBackground =
         val bBackground =
             context.getPrefInt(PreferKey.cBBackground, context.getCompatColor(R.color.md_grey_200))
             context.getPrefInt(PreferKey.cBBackground, context.getCompatColor(R.color.md_grey_200))
-        val bgImgPath =
-            ThemeConfig.bgImageLight
-        val bgImgBlur =
-            ThemeConfig.bgImageBlurring
+        val theme = themeSettingsGateway.currentSettings
+        val bgImgPath = theme.backgroundImageLight
+        val bgImgBlur = theme.backgroundImageBlurring
 
 
         return Config(
         return Config(
             themeName = name,
             themeName = name,
@@ -222,10 +246,9 @@ object ThemeConfigStore {
             context.getPrefInt(PreferKey.cNBackground, context.getCompatColor(R.color.md_grey_900))
             context.getPrefInt(PreferKey.cNBackground, context.getCompatColor(R.color.md_grey_900))
         val bBackground =
         val bBackground =
             context.getPrefInt(PreferKey.cNBBackground, context.getCompatColor(R.color.md_grey_850))
             context.getPrefInt(PreferKey.cNBBackground, context.getCompatColor(R.color.md_grey_850))
-        val bgImgPath =
-            ThemeConfig.bgImageDark
-        val bgImgBlur =
-            ThemeConfig.bgImageNBlurring
+        val theme = themeSettingsGateway.currentSettings
+        val bgImgPath = theme.backgroundImageDark
+        val bgImgBlur = theme.backgroundImageDarkBlurring
         return Config(
         return Config(
             themeName = name,
             themeName = name,
             isNightTheme = true,
             isNightTheme = true,

+ 3 - 2
app/src/main/java/io/legado/app/help/glide/LegadoGlideModule.kt

@@ -11,7 +11,6 @@ import com.bumptech.glide.load.engine.cache.MemorySizeCalculator
 import com.bumptech.glide.load.model.GlideUrl
 import com.bumptech.glide.load.model.GlideUrl
 import com.bumptech.glide.module.AppGlideModule
 import com.bumptech.glide.module.AppGlideModule
 import io.legado.app.BuildConfig
 import io.legado.app.BuildConfig
-import io.legado.app.help.config.AppConfig
 import java.io.File
 import java.io.File
 import java.io.InputStream
 import java.io.InputStream
 
 
@@ -20,6 +19,8 @@ import java.io.InputStream
 @GlideModule
 @GlideModule
 class LegadoGlideModule : AppGlideModule() {
 class LegadoGlideModule : AppGlideModule() {
 
 
+    private val otherSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.OtherSettingsGateway>()
+
     override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
     override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
         registry.replace(
         registry.replace(
             GlideUrl::class.java,
             GlideUrl::class.java,
@@ -45,7 +46,7 @@ class LegadoGlideModule : AppGlideModule() {
         builder.setMemorySizeCalculator(calculator)
         builder.setMemorySizeCalculator(calculator)
         builder.setBitmapPool(bitmapPool)
         builder.setBitmapPool(bitmapPool)
         builder.setDiskCache(InternalCacheDiskCacheFactory(context, 1024 * 1024 * 1000))
         builder.setDiskCache(InternalCacheDiskCacheFactory(context, 1024 * 1024 * 1000))
-        if (!BuildConfig.DEBUG && !AppConfig.recordLog) {
+        if (!BuildConfig.DEBUG && !otherSettingsGateway.currentSettings.recordLog) {
             builder.setLogLevel(Log.ERROR)
             builder.setLogLevel(Log.ERROR)
         }
         }
     }
     }

+ 3 - 2
app/src/main/java/io/legado/app/help/http/BackstageWebView.kt

@@ -18,7 +18,6 @@ import io.legado.app.data.entities.BaseSource
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.CacheManager
 import io.legado.app.help.CacheManager
 import io.legado.app.help.WebCacheManager
 import io.legado.app.help.WebCacheManager
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.webView.WebJsExtensions
 import io.legado.app.help.webView.WebJsExtensions
 import io.legado.app.help.webView.WebJsExtensions.Companion.getInjectionString
 import io.legado.app.help.webView.WebJsExtensions.Companion.getInjectionString
@@ -58,6 +57,8 @@ class BackstageWebView(
     private val isRule: Boolean = false,
     private val isRule: Boolean = false,
 ) {
 ) {
 
 
+    private val cacheSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.DownloadCacheSettingsGateway>()
+
     private val mHandler = Handler(Looper.getMainLooper())
     private val mHandler = Handler(Looper.getMainLooper())
     private var callback: Callback? = null
     private var callback: Callback? = null
     private var mWebView: WebView? = null
     private var mWebView: WebView? = null
@@ -138,7 +139,7 @@ class BackstageWebView(
         settings.javaScriptEnabled = true
         settings.javaScriptEnabled = true
         settings.domStorageEnabled = true
         settings.domStorageEnabled = true
         settings.blockNetworkImage = true
         settings.blockNetworkImage = true
-        settings.userAgentString = headerMap?.get(AppConst.UA_NAME) ?: AppConfig.userAgent
+        settings.userAgentString = headerMap?.get(AppConst.UA_NAME) ?: cacheSettingsGateway.currentSettings.userAgent
         settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
         settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
         settings.cacheMode = if (cacheFirst) {
         settings.cacheMode = if (cacheFirst) {
             WebSettings.LOAD_CACHE_ELSE_NETWORK
             WebSettings.LOAD_CACHE_ELSE_NETWORK

+ 4 - 3
app/src/main/java/io/legado/app/help/http/HttpHelper.kt

@@ -2,7 +2,6 @@ package io.legado.app.help.http
 
 
 import io.legado.app.constant.AppConst
 import io.legado.app.constant.AppConst
 import io.legado.app.help.CacheManager
 import io.legado.app.help.CacheManager
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.glide.progress.ProgressManager.LISTENER
 import io.legado.app.help.glide.progress.ProgressManager.LISTENER
 import io.legado.app.help.glide.progress.ProgressResponseBody
 import io.legado.app.help.glide.progress.ProgressResponseBody
 import io.legado.app.help.http.CookieManager.cookieJarHeader
 import io.legado.app.help.http.CookieManager.cookieJarHeader
@@ -30,6 +29,8 @@ private val proxyClientCache: ConcurrentHashMap<String, OkHttpClient> by lazy {
     ConcurrentHashMap()
     ConcurrentHashMap()
 }
 }
 
 
+private val cacheSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.DownloadCacheSettingsGateway>()
+
 val cookieJar by lazy {
 val cookieJar by lazy {
     object : CookieJar {
     object : CookieJar {
 
 
@@ -96,7 +97,7 @@ val okHttpClient: OkHttpClient by lazy {
             val request = chain.request()
             val request = chain.request()
             val builder = request.newBuilder()
             val builder = request.newBuilder()
             if (request.header(AppConst.UA_NAME) == null) {
             if (request.header(AppConst.UA_NAME) == null) {
-                builder.addHeader(AppConst.UA_NAME, AppConfig.userAgent)
+                builder.addHeader(AppConst.UA_NAME, cacheSettingsGateway.currentSettings.userAgent)
             } else if (request.header(AppConst.UA_NAME) == "null") {
             } else if (request.header(AppConst.UA_NAME) == "null") {
                 builder.removeHeader(AppConst.UA_NAME)
                 builder.removeHeader(AppConst.UA_NAME)
             }
             }
@@ -122,7 +123,7 @@ val okHttpClient: OkHttpClient by lazy {
             }
             }
             networkResponse
             networkResponse
         }
         }
-    if (AppConfig.isCronet) {
+    if (cacheSettingsGateway.currentSettings.cronetEnabled) {
         if (Cronet.loader?.install() == true) {
         if (Cronet.loader?.install() == true) {
             Cronet.interceptor?.let {
             Cronet.interceptor?.let {
                 builder.addInterceptor(it)
                 builder.addInterceptor(it)

+ 2 - 2
app/src/main/java/io/legado/app/help/http/ObsoleteUrlFactory.kt

@@ -1048,7 +1048,7 @@ class ObsoleteUrlFactory(private var client: OkHttpClient) : URLStreamHandlerFac
             "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS"
             "NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS"
         )
         )
         fun format(value: Date?): String {
         fun format(value: Date?): String {
-            return STANDARD_DATE_FORMAT.get().format(value)
+            return STANDARD_DATE_FORMAT.get().format(value!!)
         }
         }
 
 
         fun permitsRequestBody(method: String): Boolean {
         fun permitsRequestBody(method: String): Boolean {
@@ -1152,7 +1152,7 @@ class ObsoleteUrlFactory(private var client: OkHttpClient) : URLStreamHandlerFac
         fun getSystemProperty(key: String?, defaultValue: String?): String? {
         fun getSystemProperty(key: String?, defaultValue: String?): String? {
             val value: String?
             val value: String?
             try {
             try {
-                value = System.getProperty(key)
+                value = System.getProperty(key!!)
             } catch (ex: AccessControlException) {
             } catch (ex: AccessControlException) {
                 return defaultValue
                 return defaultValue
             }
             }

+ 2 - 0
app/src/main/java/io/legado/app/help/readaloud/playback/SystemTtsFileSynthesizer.kt

@@ -48,10 +48,12 @@ class SystemTtsFileSynthesizer(
                     }
                     }
                 }
                 }
 
 
+                @Deprecated("Deprecated in Java")
                 override fun onError(failedId: String?) {
                 override fun onError(failedId: String?) {
                     if (failedId == utteranceId && continuation.isActive) continuation.resume(false)
                     if (failedId == utteranceId && continuation.isActive) continuation.resume(false)
                 }
                 }
 
 
+                @Suppress("DEPRECATION")
                 override fun onError(failedId: String?, errorCode: Int) = onError(failedId)
                 override fun onError(failedId: String?, errorCode: Int) = onError(failedId)
             })
             })
             if (instance.setSpeechRate(speechRate) == TextToSpeech.ERROR ||
             if (instance.setSpeechRate(speechRate) == TextToSpeech.ERROR ||

+ 1 - 1
app/src/main/java/io/legado/app/help/readaloud/playback/SystemTtsVoiceCatalog.kt

@@ -79,7 +79,7 @@ class SystemTtsVoiceCatalog(context: Context) {
             } else {
             } else {
                 TextToSpeech(appContext, listener, enginePackage)
                 TextToSpeech(appContext, listener, enginePackage)
             }
             }
-            continuation.invokeOnCancellation { instance?.shutdown() }
+            continuation.invokeOnCancellation { instance.shutdown() }
         }
         }
 
 
     companion object {
     companion object {

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

@@ -7,12 +7,12 @@ import androidx.documentfile.provider.DocumentFile
 import io.legado.app.constant.AppLog
 import io.legado.app.constant.AppLog
 import io.legado.app.constant.PreferKey
 import io.legado.app.constant.PreferKey
 import io.legado.app.data.appDb
 import io.legado.app.data.appDb
+import io.legado.app.domain.gateway.BackupSettingsGateway
 import io.legado.app.domain.gateway.ReadStyleGateway
 import io.legado.app.domain.gateway.ReadStyleGateway
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.AppWebDav
 import io.legado.app.help.AppWebDav
 import io.legado.app.help.DirectLinkUpload
 import io.legado.app.help.DirectLinkUpload
 import io.legado.app.help.book.isLocal
 import io.legado.app.help.book.isLocal
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.config.AppConfigStore
 import io.legado.app.help.config.AppConfigStore
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.help.config.ReadBookConfig
@@ -53,6 +53,9 @@ object Backup {
     private val readStyleGateway: ReadStyleGateway
     private val readStyleGateway: ReadStyleGateway
         get() = GlobalContext.get().get()
         get() = GlobalContext.get().get()
 
 
+    private val backupSettingsGateway: BackupSettingsGateway
+        get() = GlobalContext.get().get()
+
     val backupPath: String by lazy {
     val backupPath: String by lazy {
         appCtx.filesDir.getFile("backup").createFolderIfNotExist().absolutePath
         appCtx.filesDir.getFile("backup").createFolderIfNotExist().absolutePath
     }
     }
@@ -96,8 +99,8 @@ object Backup {
     private fun getNowZipFileName(): String {
     private fun getNowZipFileName(): String {
         val backupDate = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
         val backupDate = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
             .format(Date(System.currentTimeMillis()))
             .format(Date(System.currentTimeMillis()))
-        val deviceName = AppConfig.webDavDeviceName
-        return if (deviceName?.isNotBlank() == true) {
+        val deviceName = backupSettingsGateway.currentSettings.webDavDeviceName
+        return if (deviceName.isNotBlank()) {
             "backup${backupDate}-${deviceName}.zip"
             "backup${backupDate}-${deviceName}.zip"
         } else {
         } else {
             "backup${backupDate}.zip"
             "backup${backupDate}.zip"
@@ -116,7 +119,7 @@ object Backup {
                     if (shouldBackup()) {
                     if (shouldBackup()) {
                         val backupZipFileName = getNowZipFileName()
                         val backupZipFileName = getNowZipFileName()
                         if (!AppWebDav.hasBackUp(backupZipFileName)) {
                         if (!AppWebDav.hasBackUp(backupZipFileName)) {
-                            backup(context, AppConfig.backupPath)
+                            backup(context, backupSettingsGateway.currentSettings.backupPath)
                         } else {
                         } else {
                             LocalConfig.lastBackup = System.currentTimeMillis()
                             LocalConfig.lastBackup = System.currentTimeMillis()
                         }
                         }
@@ -276,7 +279,7 @@ object Backup {
             .map(File::getAbsolutePath)
             .map(File::getAbsolutePath)
         FileUtils.delete(zipFilePath)
         FileUtils.delete(zipFilePath)
         FileUtils.delete(zipFilePath.replace("tmp_", ""))
         FileUtils.delete(zipFilePath.replace("tmp_", ""))
-        val backupFileName = if (AppConfig.onlyLatestBackup) {
+        val backupFileName = if (backupSettingsGateway.currentSettings.onlyLatestBackup) {
             "backup.zip"
             "backup.zip"
         } else {
         } else {
             zipFileName
             zipFileName

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

@@ -51,7 +51,6 @@ import io.legado.app.help.config.SettingsWriter
 import io.legado.app.help.config.ThemeConfigStore
 import io.legado.app.help.config.ThemeConfigStore
 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.ui.config.otherConfig.OtherConfig
 import io.legado.app.utils.ACache
 import io.legado.app.utils.ACache
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.GSON
 import io.legado.app.utils.GSON
@@ -382,7 +381,7 @@ object Restore : KoinComponent {
         appCtx.toastOnUi(R.string.restore_success)
         appCtx.toastOnUi(R.string.restore_success)
         withContext(Main) {
         withContext(Main) {
             delay(100)
             delay(100)
-            get<AppLocaleGateway>().setLanguage(OtherConfig.language)
+            get<AppLocaleGateway>().apply { setLanguage(currentLanguage) }
             if (!BuildConfig.DEBUG) {
             if (!BuildConfig.DEBUG) {
                 LauncherIconHelp.changeIcon(appCtx.getPrefString(PreferKey.launcherIcon))
                 LauncherIconHelp.changeIcon(appCtx.getPrefString(PreferKey.launcherIcon))
             }
             }

+ 3 - 2
app/src/main/java/io/legado/app/help/update/AppUpdateGitHub.kt

@@ -2,7 +2,6 @@ package io.legado.app.help.update
 
 
 import io.legado.app.constant.AppConst
 import io.legado.app.constant.AppConst
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.http.newCallResponse
 import io.legado.app.help.http.newCallResponse
 import io.legado.app.help.http.okHttpClient
 import io.legado.app.help.http.okHttpClient
@@ -21,6 +20,8 @@ import java.time.format.DateTimeFormatter
 
 
 object AppUpdateGitHub : AppUpdate.AppUpdateInterface {
 object AppUpdateGitHub : AppUpdate.AppUpdateInterface {
 
 
+    private val otherSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.OtherSettingsGateway>()
+
     private const val repoPath = "HapeLee/legado-with-MD3"
     private const val repoPath = "HapeLee/legado-with-MD3"
     private const val githubApiBaseUrl = "https://api.github.com/repos/$repoPath/releases"
     private const val githubApiBaseUrl = "https://api.github.com/repos/$repoPath/releases"
     private const val updateManifestBaseUrl =
     private const val updateManifestBaseUrl =
@@ -28,7 +29,7 @@ object AppUpdateGitHub : AppUpdate.AppUpdateInterface {
     private const val manifestTimeoutMillis = 2500L
     private const val manifestTimeoutMillis = 2500L
 
 
     private val checkVariant: AppVariant
     private val checkVariant: AppVariant
-        get() = when (AppConfig.updateToVariant) {
+        get() = when (otherSettingsGateway.currentSettings.updateToVariant) {
             "official_version" -> AppVariant.OFFICIAL
             "official_version" -> AppVariant.OFFICIAL
             "beta_release_version" -> AppVariant.BETA_RELEASE
             "beta_release_version" -> AppVariant.BETA_RELEASE
             "all_version" -> AppVariant.ALL
             "all_version" -> AppVariant.ALL

+ 3 - 3
app/src/main/java/io/legado/app/lib/cronet/AbsCallBack.kt

@@ -432,7 +432,7 @@ abstract class AbsCallBack(
 
 
     inner class CronetBodySource : Source {
     inner class CronetBodySource : Source {
 
 
-        private var buffer = ByteBuffer.allocateDirect(32 * 1024)
+        private var buffer: ByteBuffer? = ByteBuffer.allocateDirect(32 * 1024)
         private var closed = false
         private var closed = false
         private val timeout = readTimeoutMillis.toLong()
         private val timeout = readTimeoutMillis.toLong()
 
 
@@ -460,8 +460,8 @@ abstract class AbsCallBack(
                 return -1
                 return -1
             }
             }
 
 
-            if (byteCount < buffer.limit()) {
-                buffer.limit(byteCount.toInt())
+            if (byteCount < buffer!!.limit()) {
+                buffer!!.limit(byteCount.toInt())
             }
             }
 
 
             request?.read(buffer)
             request?.read(buffer)

+ 1 - 1
app/src/main/java/io/legado/app/lib/cronet/CronetLoader.kt

@@ -132,7 +132,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader(), Cronet.LoaderInterfa
         } catch (e: Throwable) {
         } catch (e: Throwable) {
             //如果找不到,则从远程下载
             //如果找不到,则从远程下载
             //删除历史文件
             //删除历史文件
-            deleteHistoryFile(Objects.requireNonNull(soFile.parentFile), soFile)
+            deleteHistoryFile(soFile.parentFile!!, soFile)
             //md5 = getUrlMd5(md5Url)
             //md5 = getUrlMd5(md5Url)
             DebugLog.d(javaClass.simpleName, "soMD5:$md5")
             DebugLog.d(javaClass.simpleName, "soMD5:$md5")
             if (md5.length != 32 || soUrl.isEmpty()) {
             if (md5.length != 32 || soUrl.isEmpty()) {

+ 1 - 2
app/src/main/java/io/legado/app/lib/permission/RequestManager.kt

@@ -34,8 +34,7 @@ internal object RequestManager : OnPermissionsResultCallback {
             if (index >= 0) {
             if (index >= 0) {
                 val to = it.size - 1
                 val to = it.size - 1
                 if (index != to) {
                 if (index != to) {
-                    @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS")
-                    Collections.swap(requests, index, to)
+                    Collections.swap(it, index, to)
                 }
                 }
             } else {
             } else {
                 it.push(request)
                 it.push(request)

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

@@ -7,13 +7,14 @@ import android.view.WindowManager
 import androidx.preference.EditTextPreferenceDialogFragmentCompat
 import androidx.preference.EditTextPreferenceDialogFragmentCompat
 import androidx.preference.PreferenceDialogFragmentCompat
 import androidx.preference.PreferenceDialogFragmentCompat
 import io.legado.app.R
 import io.legado.app.R
-import io.legado.app.help.config.AppConfig
 //import io.legado.app.lib.theme.accentColor
 //import io.legado.app.lib.theme.accentColor
 //import io.legado.app.lib.theme.filletBackground
 //import io.legado.app.lib.theme.filletBackground
 
 
 
 
 class EditTextPreferenceDialog : EditTextPreferenceDialogFragmentCompat() {
 class EditTextPreferenceDialog : EditTextPreferenceDialogFragmentCompat() {
 
 
+    private val themeSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.ThemeSettingsGateway>()
+
     companion object {
     companion object {
 
 
         fun newInstance(key: String): EditTextPreferenceDialog {
         fun newInstance(key: String): EditTextPreferenceDialog {
@@ -41,7 +42,7 @@ class EditTextPreferenceDialog : EditTextPreferenceDialogFragmentCompat() {
 
 
     override fun onStart() {
     override fun onStart() {
         super.onStart()
         super.onStart()
-        if (AppConfig.isEInkMode) {
+        if (themeSettingsGateway.currentSettings.appTheme == "4") {
             dialog?.window?.let {
             dialog?.window?.let {
                 it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
                 it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
                 val attr = it.attributes
                 val attr = it.attributes

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

@@ -7,13 +7,14 @@ import android.view.WindowManager
 import androidx.preference.ListPreferenceDialogFragmentCompat
 import androidx.preference.ListPreferenceDialogFragmentCompat
 import androidx.preference.PreferenceDialogFragmentCompat
 import androidx.preference.PreferenceDialogFragmentCompat
 import io.legado.app.R
 import io.legado.app.R
-import io.legado.app.help.config.AppConfig
 //import io.legado.app.lib.theme.accentColor
 //import io.legado.app.lib.theme.accentColor
 //import io.legado.app.lib.theme.filletBackground
 //import io.legado.app.lib.theme.filletBackground
 import io.legado.app.utils.dpToPx
 import io.legado.app.utils.dpToPx
 
 
 class ListPreferenceDialog : ListPreferenceDialogFragmentCompat() {
 class ListPreferenceDialog : ListPreferenceDialogFragmentCompat() {
 
 
+    private val themeSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.ThemeSettingsGateway>()
+
     companion object {
     companion object {
 
 
         fun newInstance(key: String?): ListPreferenceDialog {
         fun newInstance(key: String?): ListPreferenceDialog {
@@ -44,7 +45,7 @@ class ListPreferenceDialog : ListPreferenceDialogFragmentCompat() {
 
 
     override fun onStart() {
     override fun onStart() {
         super.onStart()
         super.onStart()
-        if (AppConfig.isEInkMode) {
+        if (themeSettingsGateway.currentSettings.appTheme == "4") {
             dialog?.window?.let {
             dialog?.window?.let {
                 it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
                 it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
                 val attr = it.attributes
                 val attr = it.attributes

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

@@ -7,13 +7,14 @@ import android.view.WindowManager
 import androidx.preference.MultiSelectListPreferenceDialogFragmentCompat
 import androidx.preference.MultiSelectListPreferenceDialogFragmentCompat
 import androidx.preference.PreferenceDialogFragmentCompat
 import androidx.preference.PreferenceDialogFragmentCompat
 import io.legado.app.R
 import io.legado.app.R
-import io.legado.app.help.config.AppConfig
 //import io.legado.app.lib.theme.accentColor
 //import io.legado.app.lib.theme.accentColor
 //import io.legado.app.lib.theme.filletBackground
 //import io.legado.app.lib.theme.filletBackground
 import io.legado.app.utils.dpToPx
 import io.legado.app.utils.dpToPx
 
 
 class MultiSelectListPreferenceDialog : MultiSelectListPreferenceDialogFragmentCompat() {
 class MultiSelectListPreferenceDialog : MultiSelectListPreferenceDialogFragmentCompat() {
 
 
+    private val themeSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.ThemeSettingsGateway>()
+
     companion object {
     companion object {
 
 
         fun newInstance(key: String?): MultiSelectListPreferenceDialog {
         fun newInstance(key: String?): MultiSelectListPreferenceDialog {
@@ -46,7 +47,7 @@ class MultiSelectListPreferenceDialog : MultiSelectListPreferenceDialogFragmentC
 
 
     override fun onStart() {
     override fun onStart() {
         super.onStart()
         super.onStart()
-        if (AppConfig.isEInkMode) {
+        if (themeSettingsGateway.currentSettings.appTheme == "4") {
             dialog?.window?.let {
             dialog?.window?.let {
                 it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
                 it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
                 val attr = it.attributes
                 val attr = it.attributes

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

@@ -19,7 +19,6 @@ import com.google.android.material.card.MaterialCardView
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.constant.PreferKey
 import io.legado.app.constant.PreferKey
 import io.legado.app.domain.gateway.ThemeSettingsGateway
 import io.legado.app.domain.gateway.ThemeSettingsGateway
-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.toastOnUi
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.activity
 import io.legado.app.utils.activity
@@ -92,8 +91,8 @@ class ThemeCardPreference(context: Context, attrs: AttributeSet) : Preference(co
             holder.card.setOnClickListener {
             holder.card.setOnClickListener {
                 if (value != currentValue) {
                 if (value != currentValue) {
                     if (value == "13") {
                     if (value == "13") {
-                        val hasLightBg = !ThemeConfig.bgImageLight.isNullOrEmpty()
-                        val hasDarkBg = !ThemeConfig.bgImageDark.isNullOrEmpty()
+                        val hasLightBg = !themeSettingsGateway.currentSettings.backgroundImageLight.isNullOrEmpty()
+                        val hasDarkBg = !themeSettingsGateway.currentSettings.backgroundImageDark.isNullOrEmpty()
                         if (!hasLightBg || !hasDarkBg) {
                         if (!hasLightBg || !hasDarkBg) {
                             context.toastOnUi(R.string.transparent_theme_alarm)
                             context.toastOnUi(R.string.transparent_theme_alarm)
                             return@setOnClickListener
                             return@setOnClickListener

+ 8 - 3
app/src/main/java/io/legado/app/model/AudioPlay.kt

@@ -18,7 +18,8 @@ import io.legado.app.help.book.getBookSource
 import io.legado.app.help.book.readSimulating
 import io.legado.app.help.book.readSimulating
 import io.legado.app.help.book.simulatedTotalChapterNum
 import io.legado.app.help.book.simulatedTotalChapterNum
 import io.legado.app.help.book.update
 import io.legado.app.help.book.update
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.OtherSettingsGateway
+import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.service.AudioPlayService
 import io.legado.app.service.AudioPlayService
@@ -28,12 +29,16 @@ import io.legado.app.utils.toastOnUi
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.MainScope
 import kotlinx.coroutines.MainScope
 import kotlinx.coroutines.cancelChildren
 import kotlinx.coroutines.cancelChildren
+import org.koin.core.context.GlobalContext
 import splitties.init.appCtx
 import splitties.init.appCtx
 
 
 @SuppressLint("StaticFieldLeak")
 @SuppressLint("StaticFieldLeak")
 @Suppress("unused")
 @Suppress("unused")
 object AudioPlay : CoroutineScope by MainScope() {
 object AudioPlay : CoroutineScope by MainScope() {
 
 
+    private val otherSettingsGateway get() = GlobalContext.get().get<OtherSettingsGateway>()
+    private val readSettingsGateway get() = GlobalContext.get().get<ReadSettingsGateway>()
+
     enum class PlayMode {
     enum class PlayMode {
         LIST_END_STOP,
         LIST_END_STOP,
         SINGLE_LOOP,
         SINGLE_LOOP,
@@ -406,8 +411,8 @@ object AudioPlay : CoroutineScope by MainScope() {
                 appDb.bookChapterDao.getChapter(book.bookUrl, book.durChapterIndex)?.let {
                 appDb.bookChapterDao.getChapter(book.bookUrl, book.durChapterIndex)?.let {
                     book.durChapterTitle = it.getDisplayTitle(
                     book.durChapterTitle = it.getDisplayTitle(
                         ContentProcessor.get(book.name, book.origin).getTitleReplaceRules(),
                         ContentProcessor.get(book.name, book.origin).getTitleReplaceRules(),
-                        book.getUseReplaceRule(AppConfig.replaceEnableDefault),
-                        chineseConverterType = AppConfig.chineseConverterType,
+                        book.getUseReplaceRule(otherSettingsGateway.currentSettings.replaceEnableDefault),
+                        chineseConverterType = readSettingsGateway.currentSettings.chineseConverterType,
                     )
                     )
                     SourceCallBack.callBackBook(SourceCallBack.SAVE_READ, bookSource, book, it)
                     SourceCallBack.callBackBook(SourceCallBack.SAVE_READ, bookSource, book, it)
                 }
                 }

+ 20 - 9
app/src/main/java/io/legado/app/model/BookCover.kt

@@ -19,13 +19,13 @@ import com.bumptech.glide.request.RequestOptions
 import com.bumptech.glide.request.target.Target
 import com.bumptech.glide.request.target.Target
 import com.bumptech.glide.request.target.Target.SIZE_ORIGINAL
 import com.bumptech.glide.request.target.Target.SIZE_ORIGINAL
 import io.legado.app.R
 import io.legado.app.R
-import io.legado.app.ui.config.coverConfig.CoverConfig
 import io.legado.app.data.entities.BaseSource
 import io.legado.app.data.entities.BaseSource
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.Book
+import io.legado.app.domain.gateway.AppShellSettingsGateway
+import io.legado.app.domain.gateway.CoverSettingsGateway
+import io.legado.app.domain.gateway.MangaSettingsGateway
 import io.legado.app.help.CacheManager
 import io.legado.app.help.CacheManager
 import io.legado.app.help.DefaultData
 import io.legado.app.help.DefaultData
-import io.legado.app.help.config.AppConfig
-import io.legado.app.ui.config.readMangaConfig.ReadMangaConfig
 import io.legado.app.help.glide.BlurTransformation
 import io.legado.app.help.glide.BlurTransformation
 import io.legado.app.help.glide.ImageLoader
 import io.legado.app.help.glide.ImageLoader
 import io.legado.app.help.glide.OkHttpModelLoader
 import io.legado.app.help.glide.OkHttpModelLoader
@@ -36,6 +36,8 @@ import io.legado.app.domain.usecase.CoverAlbumUseCase
 import io.legado.app.utils.BitmapUtils
 import io.legado.app.utils.BitmapUtils
 import io.legado.app.utils.GSON
 import io.legado.app.utils.GSON
 import io.legado.app.utils.fromJsonObject
 import io.legado.app.utils.fromJsonObject
+import io.legado.app.utils.isNightMode
+import io.legado.app.utils.sysConfiguration
 import kotlinx.coroutines.currentCoroutineContext
 import kotlinx.coroutines.currentCoroutineContext
 import org.koin.core.component.KoinComponent
 import org.koin.core.component.KoinComponent
 import org.koin.core.component.inject
 import org.koin.core.component.inject
@@ -49,11 +51,20 @@ object BookCover : KoinComponent {
     private const val coverRuleConfigKey = "legadoCoverRuleConfig"
     private const val coverRuleConfigKey = "legadoCoverRuleConfig"
     const val configFileName = "coverRule.json"
     const val configFileName = "coverRule.json"
     private val coverAlbumUseCase: CoverAlbumUseCase by inject()
     private val coverAlbumUseCase: CoverAlbumUseCase by inject()
+    private val shellSettingsGateway: AppShellSettingsGateway by inject()
+    private val coverSettingsGateway: CoverSettingsGateway by inject()
+    private val mangaSettingsGateway: MangaSettingsGateway by inject()
+
+    private val isNightTheme: Boolean
+        get() = when (shellSettingsGateway.currentSettings.themeMode) {
+            "1" -> false
+            "2" -> true
+            else -> sysConfiguration.isNightMode
+        }
 
 
     val defaultDrawable: Drawable
     val defaultDrawable: Drawable
         @SuppressLint("UseCompatLoadingForDrawables")
         @SuppressLint("UseCompatLoadingForDrawables")
         get() {
         get() {
-            val isNightTheme = AppConfig.isNightTheme
             val paths = coverAlbumUseCase.selectedImagePaths(isNightTheme)
             val paths = coverAlbumUseCase.selectedImagePaths(isNightTheme)
 
 
             if (paths.isEmpty()) {
             if (paths.isEmpty()) {
@@ -68,7 +79,7 @@ object BookCover : KoinComponent {
 
 
     fun getRandomDefaultPath(
     fun getRandomDefaultPath(
         seed: Any? = null,
         seed: Any? = null,
-        isNight: Boolean = AppConfig.isNightTheme
+        isNight: Boolean = isNightTheme
     ): String? {
     ): String? {
         val paths = coverAlbumUseCase.selectedImagePaths(isNight)
         val paths = coverAlbumUseCase.selectedImagePaths(isNight)
         if (paths.isEmpty()) return null
         if (paths.isEmpty()) return null
@@ -81,7 +92,7 @@ object BookCover : KoinComponent {
 
 
     fun getRandomDefaultDrawable(
     fun getRandomDefaultDrawable(
         seed: Any? = null,
         seed: Any? = null,
-        isNight: Boolean = AppConfig.isNightTheme
+        isNight: Boolean = isNightTheme
     ): Drawable {
     ): Drawable {
         val randomPath = getRandomDefaultPath(seed, isNight)
         val randomPath = getRandomDefaultPath(seed, isNight)
             ?: return appCtx.resources.getDrawable(R.drawable.image_cover_default, null)
             ?: return appCtx.resources.getDrawable(R.drawable.image_cover_default, null)
@@ -111,7 +122,7 @@ object BookCover : KoinComponent {
         onLoadFinish: (() -> Unit)? = null,
         onLoadFinish: (() -> Unit)? = null,
     ): RequestBuilder<Drawable> {
     ): RequestBuilder<Drawable> {
         val currentDefault = getRandomDefaultDrawable()
         val currentDefault = getRandomDefaultDrawable()
-        if (AppConfig.useDefaultCover) {
+        if (coverSettingsGateway.currentSettings.useDefaultCover) {
             return ImageLoader.load(context, currentDefault)
             return ImageLoader.load(context, currentDefault)
                 .centerCrop()
                 .centerCrop()
         }
         }
@@ -175,7 +186,7 @@ object BookCover : KoinComponent {
         if (transformation != null) {
         if (transformation != null) {
             builder = builder.transform(transformation)
             builder = builder.transform(transformation)
         }
         }
-        builder = if (ReadMangaConfig.disableMangaCrossFade) {
+        builder = if (mangaSettingsGateway.currentSettings.disableMangaCrossFade) {
             builder
             builder
         } else {
         } else {
             builder.transition(DrawableTransitionOptions.withCrossFade())
             builder.transition(DrawableTransitionOptions.withCrossFade())
@@ -214,7 +225,7 @@ object BookCover : KoinComponent {
         val currentDefault = getRandomDefaultDrawable()
         val currentDefault = getRandomDefaultDrawable()
         val loadBlur = ImageLoader.load(context, currentDefault)
         val loadBlur = ImageLoader.load(context, currentDefault)
             .transform(BlurTransformation(25), CenterCrop())
             .transform(BlurTransformation(25), CenterCrop())
-        if (AppConfig.useDefaultCover) {
+        if (coverSettingsGateway.currentSettings.useDefaultCover) {
             return loadBlur
             return loadBlur
         }
         }
         var options = RequestOptions().set(OkHttpModelLoader.loadOnlyWifiOption, loadOnlyWifi)
         var options = RequestOptions().set(OkHttpModelLoader.loadOnlyWifiOption, loadOnlyWifi)

+ 5 - 2
app/src/main/java/io/legado/app/model/CacheBook.kt

@@ -14,8 +14,8 @@ import io.legado.app.model.cache.CacheDownloadRequest
 import io.legado.app.model.cache.CacheDownloadStateStore
 import io.legado.app.model.cache.CacheDownloadStateStore
 import io.legado.app.model.cache.ChapterSelection
 import io.legado.app.model.cache.ChapterSelection
 import io.legado.app.model.cache.ExplicitCacheBookFifo
 import io.legado.app.model.cache.ExplicitCacheBookFifo
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.service.CacheBookService
 import io.legado.app.service.CacheBookService
-import io.legado.app.ui.config.otherConfig.OtherConfig
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.onEachParallel
 import io.legado.app.utils.onEachParallel
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CompletableDeferred
@@ -39,6 +39,7 @@ import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withTimeout
 import kotlinx.coroutines.withTimeout
+import org.koin.core.context.GlobalContext
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.atomic.AtomicInteger
 import java.util.concurrent.atomic.AtomicInteger
 import java.util.concurrent.atomic.AtomicLong
 import java.util.concurrent.atomic.AtomicLong
@@ -67,13 +68,15 @@ object CacheBook {
         private val processMutex = Mutex()
         private val processMutex = Mutex()
         private val workingState = MutableStateFlow(true)
         private val workingState = MutableStateFlow(true)
 
 
+        private val cacheSettingsGateway get() = GlobalContext.get().get<DownloadCacheSettingsGateway>()
+
         fun setWorkingState(value: Boolean) {
         fun setWorkingState(value: Boolean) {
             workingState.value = value
             workingState.value = value
         }
         }
 
 
         suspend fun startProcessJob(context: CoroutineContext) = processMutex.withLock {
         suspend fun startProcessJob(context: CoroutineContext) = processMutex.withLock {
             setWorkingState(true)
             setWorkingState(true)
-            val concurrency = OtherConfig.cacheBookThreadCount.coerceIn(1, maxDownloadConcurrency)
+            val concurrency = cacheSettingsGateway.currentSettings.cacheBookThreadCount.coerceIn(1, maxDownloadConcurrency)
             flow {
             flow {
                 while (currentCoroutineContext().isActive && taskMap.isNotEmpty() && !isPaused) {
                 while (currentCoroutineContext().isActive && taskMap.isNotEmpty() && !isPaused) {
                     if (!workingState.value) {
                     if (!workingState.value) {

+ 8 - 3
app/src/main/java/io/legado/app/model/ImageProvider.kt

@@ -12,8 +12,9 @@ import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.book.BookHelp
 import io.legado.app.help.book.BookHelp
 import io.legado.app.help.book.isEpub
 import io.legado.app.help.book.isEpub
 import io.legado.app.help.book.isMobi
 import io.legado.app.help.book.isMobi
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
+import io.legado.app.domain.gateway.OtherSettingsGateway
 import io.legado.app.help.book.isPdf
 import io.legado.app.help.book.isPdf
-import io.legado.app.help.config.AppConfig
 import io.legado.app.model.localBook.EpubFile
 import io.legado.app.model.localBook.EpubFile
 import io.legado.app.model.localBook.MobiFile
 import io.legado.app.model.localBook.MobiFile
 import io.legado.app.model.localBook.PdfFile
 import io.legado.app.model.localBook.PdfFile
@@ -23,6 +24,7 @@ import io.legado.app.utils.SvgUtils
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.toastOnUi
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.withContext
 import kotlinx.coroutines.withContext
+import org.koin.core.context.GlobalContext
 import splitties.init.appCtx
 import splitties.init.appCtx
 import java.io.File
 import java.io.File
 import java.io.FileOutputStream
 import java.io.FileOutputStream
@@ -30,6 +32,9 @@ import kotlin.math.min
 
 
 object ImageProvider {
 object ImageProvider {
 
 
+    private val cacheSettingsGateway get() = GlobalContext.get().get<DownloadCacheSettingsGateway>()
+    private val otherSettingsGateway get() = GlobalContext.get().get<OtherSettingsGateway>()
+
     private val errorBitmap: Bitmap by lazy {
     private val errorBitmap: Bitmap by lazy {
         BitmapFactory.decodeResource(appCtx.resources, R.drawable.image_loading_error)
         BitmapFactory.decodeResource(appCtx.resources, R.drawable.image_loading_error)
     }
     }
@@ -41,7 +46,7 @@ object ImageProvider {
     private const val M = 1024 * 1024
     private const val M = 1024 * 1024
     val cacheSize: Int
     val cacheSize: Int
         get() {
         get() {
-            return AppConfig.bitmapCacheSize.takeIf { it in 1..<2048 }
+            return cacheSettingsGateway.currentSettings.bitmapCacheSize.takeIf { it in 1..<2048 }
                 ?.times(M)
                 ?.times(M)
                 ?: (50 * M)
                 ?: (50 * M)
         }
         }
@@ -184,7 +189,7 @@ object ImageProvider {
         height: Int? = null
         height: Int? = null
     ): Bitmap {
     ): Bitmap {
         //src为空白时 可能被净化替换掉了 或者规则失效
         //src为空白时 可能被净化替换掉了 或者规则失效
-        if (book.getUseReplaceRule(AppConfig.replaceEnableDefault) && src.isBlank()) {
+        if (book.getUseReplaceRule(otherSettingsGateway.currentSettings.replaceEnableDefault) && src.isBlank()) {
             book.setUseReplaceRule(false)
             book.setUseReplaceRule(false)
             appCtx.toastOnUi(R.string.error_image_url_empty)
             appCtx.toastOnUi(R.string.error_image_url_empty)
         }
         }

+ 3 - 3
app/src/main/java/io/legado/app/model/ReadAloud.kt

@@ -15,7 +15,6 @@ import io.legado.app.lib.dialogs.SelectItem
 import io.legado.app.service.BaseReadAloudService
 import io.legado.app.service.BaseReadAloudService
 import io.legado.app.service.HttpReadAloudService
 import io.legado.app.service.HttpReadAloudService
 import io.legado.app.service.TTSReadAloudService
 import io.legado.app.service.TTSReadAloudService
-import io.legado.app.ui.config.readConfig.ReadConfig
 import io.legado.app.utils.GSON
 import io.legado.app.utils.GSON
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.StringUtils
 import io.legado.app.utils.StringUtils
@@ -27,8 +26,9 @@ import kotlinx.coroutines.runBlocking
 import splitties.init.appCtx
 import splitties.init.appCtx
 
 
 object ReadAloud {
 object ReadAloud {
+    private val aloudSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.ReadAloudSettingsGateway>()
     private var aloudClass: Class<*> = getReadAloudClass()
     private var aloudClass: Class<*> = getReadAloudClass()
-    val ttsEngine get() = ReadBook.book?.getTtsEngine() ?: ReadConfig.ttsEngine
+    val ttsEngine get() = ReadBook.book?.getTtsEngine() ?: aloudSettingsGateway.currentSettings.ttsEngine
     var httpTTS: HttpTTS? = null
     var httpTTS: HttpTTS? = null
     var coordinatorDefaultEngineType: String = ReadAloudVoice.ENGINE_SYSTEM
     var coordinatorDefaultEngineType: String = ReadAloudVoice.ENGINE_SYSTEM
         private set
         private set
@@ -83,7 +83,7 @@ object ReadAloud {
     }
     }
 
 
     private fun findCoordinatorHttpSeed(): HttpTTS? {
     private fun findCoordinatorHttpSeed(): HttpTTS? {
-        if (!ReadConfig.useMultiSpeaker) return null
+        if (!aloudSettingsGateway.currentSettings.useMultiSpeaker) return null
         return runCatching {
         return runCatching {
             val bookUrl = ReadBook.book?.bookUrl ?: return@runCatching null
             val bookUrl = ReadBook.book?.bookUrl ?: return@runCatching null
             val boundVoices = runBlocking {
             val boundVoices = runBlocking {

+ 21 - 17
app/src/main/java/io/legado/app/model/ReadBook.kt

@@ -11,6 +11,9 @@ import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.readRecord.ReadRecord
 import io.legado.app.data.entities.readRecord.ReadRecord
 import io.legado.app.data.entities.readRecord.ReadRecordSession
 import io.legado.app.data.entities.readRecord.ReadRecordSession
 import io.legado.app.data.repository.ReadRecordRepository
 import io.legado.app.data.repository.ReadRecordRepository
+import io.legado.app.domain.gateway.BackupSettingsGateway
+import io.legado.app.domain.gateway.OtherSettingsGateway
+import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.help.AppWebDav
 import io.legado.app.help.AppWebDav
 import io.legado.app.help.book.BookHelp
 import io.legado.app.help.book.BookHelp
 import io.legado.app.help.book.ContentProcessor
 import io.legado.app.help.book.ContentProcessor
@@ -23,7 +26,6 @@ import io.legado.app.help.book.isSameNameAuthor
 import io.legado.app.help.book.readSimulating
 import io.legado.app.help.book.readSimulating
 import io.legado.app.help.book.simulatedTotalChapterNum
 import io.legado.app.help.book.simulatedTotalChapterNum
 import io.legado.app.help.book.update
 import io.legado.app.help.book.update
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.help.config.ReadBookConfig
 import io.legado.app.data.repository.HighlightRuleRepository
 import io.legado.app.data.repository.HighlightRuleRepository
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
@@ -60,7 +62,6 @@ import io.legado.app.ui.book.read.pageestimate.PageEstimateMetrics
 import io.legado.app.ui.book.read.pageestimate.RoomExactChapterPageCountStore
 import io.legado.app.ui.book.read.pageestimate.RoomExactChapterPageCountStore
 import io.legado.app.ui.book.read.pageestimate.WholeBookPageCoordinator
 import io.legado.app.ui.book.read.pageestimate.WholeBookPageCoordinator
 import io.legado.app.ui.book.read.pageestimate.WholeBookPageState
 import io.legado.app.ui.book.read.pageestimate.WholeBookPageState
-import io.legado.app.ui.config.readConfig.ReadConfig
 import io.legado.app.utils.buildMainHandler
 import io.legado.app.utils.buildMainHandler
 import io.legado.app.utils.dpToPx
 import io.legado.app.utils.dpToPx
 import io.legado.app.utils.postEvent
 import io.legado.app.utils.postEvent
@@ -160,6 +161,9 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
     var msg: String? = null
     var msg: String? = null
     private val readRecordRepository: ReadRecordRepository by inject()
     private val readRecordRepository: ReadRecordRepository by inject()
     private val readAloudSessionStore: ReadAloudSessionStore by inject()
     private val readAloudSessionStore: ReadAloudSessionStore by inject()
+    private val readSettingsGateway: ReadSettingsGateway by inject()
+    private val otherSettingsGateway: OtherSettingsGateway by inject()
+    private val backupSettingsGateway: BackupSettingsGateway by inject()
     private var lastReadLength: Long = 0
     private var lastReadLength: Long = 0
     private val loadingChapters = arrayListOf<Int>()
     private val loadingChapters = arrayListOf<Int>()
     private val readRecord = ReadRecord()
     private val readRecord = ReadRecord()
@@ -397,8 +401,8 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
                 append(book.config.useReplaceRule)
                 append(book.config.useReplaceRule)
                 append('|').append(book.config.delTag)
                 append('|').append(book.config.delTag)
                 append('|').append(book.config.translationMode)
                 append('|').append(book.config.translationMode)
-                append('|').append(AppConfig.chineseConverterType)
-                append('|').append(AppConfig.replaceEnableDefault)
+                append('|').append(readSettingsGateway.currentSettings.chineseConverterType)
+                append('|').append(otherSettingsGateway.currentSettings.replaceEnableDefault)
                 append('|').append(processor.getTitleReplaceRules())
                 append('|').append(processor.getTitleReplaceRules())
                 append('|').append(processor.getContentReplaceRules())
                 append('|').append(processor.getContentReplaceRules())
             },
             },
@@ -467,7 +471,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
         if (!wholeBookPageCoordinator.awaitInitialized(layoutGeneration)) return
         if (!wholeBookPageCoordinator.awaitInitialized(layoutGeneration)) return
         val chapters = withContext(IO) { appDb.bookChapterDao.getChapterList(book.bookUrl) }
         val chapters = withContext(IO) { appDb.bookChapterDao.getChapterList(book.bookUrl) }
         val processor = ContentProcessor.get(book)
         val processor = ContentProcessor.get(book)
-        val useReplaceRule = book.getUseReplaceRule(AppConfig.replaceEnableDefault)
+        val useReplaceRule = book.getUseReplaceRule(otherSettingsGateway.currentSettings.replaceEnableDefault)
         val highlightRules = HighlightRuleRepository().loadEnabled(ReadBookConfig.durConfig.name)
         val highlightRules = HighlightRuleRepository().loadEnabled(ReadBookConfig.durConfig.name)
         for (chapter in chapters) {
         for (chapter in chapters) {
             ensureActive()
             ensureActive()
@@ -476,7 +480,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
             val displayTitle = chapter.getDisplayTitle(
             val displayTitle = chapter.getDisplayTitle(
                 processor.getTitleReplaceRules(),
                 processor.getTitleReplaceRules(),
                 useReplaceRule,
                 useReplaceRule,
-                chineseConverterType = AppConfig.chineseConverterType,
+                chineseConverterType = readSettingsGateway.currentSettings.chineseConverterType,
             )
             )
             val processed = processor.getContent(book, chapter, content, includeTitle = false)
             val processed = processor.getContent(book, chapter, content, includeTitle = false)
             wholeBookPageCoordinator.updateChapterContent(
             wholeBookPageCoordinator.updateChapterContent(
@@ -497,7 +501,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
                 title = displayTitle,
                 title = displayTitle,
                 paragraphs = processed.textList,
                 paragraphs = processed.textList,
                 includeTitle = ReadBookConfig.titleMode != 2 || chapter.isVolume || processed.textList.isEmpty(),
                 includeTitle = ReadBookConfig.titleMode != 2 || chapter.isVolume || processed.textList.isEmpty(),
-                adaptSpecialStyle = ReadConfig.adaptSpecialStyle,
+                adaptSpecialStyle = readSettingsGateway.currentSettings.adaptSpecialStyle,
                 htmlSemanticTextResolver = AndroidReaderHtmlSemanticTextResolver,
                 htmlSemanticTextResolver = AndroidReaderHtmlSemanticTextResolver,
             )
             )
             val environment = readerPaginationEnvironment ?: return
             val environment = readerPaginationEnvironment ?: return
@@ -660,7 +664,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
                     ConfigUpdateAction.RebuildWholeBookPageIndex,
                     ConfigUpdateAction.RebuildWholeBookPageIndex,
                 )
                 )
             )
             )
-            if (ReadConfig.readBarStyleFollowPage) {
+            if (readSettingsGateway.currentSettings.readBarStyleFollowPage) {
                 postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
                 postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
             }
             }
         }
         }
@@ -774,7 +778,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
         uploadSuccessAction: (() -> Unit)? = null,
         uploadSuccessAction: (() -> Unit)? = null,
         syncSuccessAction: (() -> Unit)? = null
         syncSuccessAction: (() -> Unit)? = null
     ) {
     ) {
-        if (!ReadConfig.syncBookProgress) return
+        if (!backupSettingsGateway.currentSettings.syncBookProgress) return
         val book = book ?: return
         val book = book ?: return
         Coroutine.async {
         Coroutine.async {
             AppWebDav.getBookProgress(book)
             AppWebDav.getBookProgress(book)
@@ -1541,8 +1545,8 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
         val contentProcessor = ContentProcessor.get(book.name, book.origin)
         val contentProcessor = ContentProcessor.get(book.name, book.origin)
         val displayTitle = chapter.getDisplayTitle(
         val displayTitle = chapter.getDisplayTitle(
             contentProcessor.getTitleReplaceRules(),
             contentProcessor.getTitleReplaceRules(),
-            book.getUseReplaceRule(AppConfig.replaceEnableDefault),
-            chineseConverterType = AppConfig.chineseConverterType,
+            book.getUseReplaceRule(otherSettingsGateway.currentSettings.replaceEnableDefault),
+            chineseConverterType = readSettingsGateway.currentSettings.chineseConverterType,
         )
         )
         val contents = contentProcessor
         val contents = contentProcessor
             .getContent(book, chapter, content, includeTitle = false)
             .getContent(book, chapter, content, includeTitle = false)
@@ -1565,7 +1569,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
             title = displayTitle,
             title = displayTitle,
             paragraphs = contents.textList,
             paragraphs = contents.textList,
             includeTitle = ReadBookConfig.titleMode != 2 || chapter.isVolume || contents.textList.isEmpty(),
             includeTitle = ReadBookConfig.titleMode != 2 || chapter.isVolume || contents.textList.isEmpty(),
-            adaptSpecialStyle = ReadConfig.adaptSpecialStyle,
+            adaptSpecialStyle = readSettingsGateway.currentSettings.adaptSpecialStyle,
             htmlSemanticTextResolver = AndroidReaderHtmlSemanticTextResolver,
             htmlSemanticTextResolver = AndroidReaderHtmlSemanticTextResolver,
         )
         )
         val readerChapterInput = ReaderChapterInput(
         val readerChapterInput = ReaderChapterInput(
@@ -1706,8 +1710,8 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
                     appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let {
                     appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let {
                         book.durChapterTitle = it.getDisplayTitle(
                         book.durChapterTitle = it.getDisplayTitle(
                             ContentProcessor.get(book.name, book.origin).getTitleReplaceRules(),
                             ContentProcessor.get(book.name, book.origin).getTitleReplaceRules(),
-                            book.getUseReplaceRule(AppConfig.replaceEnableDefault),
-                            chineseConverterType = AppConfig.chineseConverterType,
+                            book.getUseReplaceRule(otherSettingsGateway.currentSettings.replaceEnableDefault),
+                            chineseConverterType = readSettingsGateway.currentSettings.chineseConverterType,
                         )
                         )
                         SourceCallBack.callBackBook(SourceCallBack.SAVE_READ, bookSource, book, it)
                         SourceCallBack.callBackBook(SourceCallBack.SAVE_READ, bookSource, book, it)
                     }
                     }
@@ -1742,7 +1746,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
     private fun preDownload() {
     private fun preDownload() {
         if (book?.isLocal == true) return
         if (book?.isLocal == true) return
         executor.execute {
         executor.execute {
-            if (ReadConfig.preDownloadNum < 2) {
+            if (readSettingsGateway.currentSettings.preDownloadNum < 2) {
                 return@execute
                 return@execute
             }
             }
             preDownloadTask?.cancel()
             preDownloadTask?.cancel()
@@ -1750,7 +1754,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
                 //预下载
                 //预下载
                 launch {
                 launch {
                     val maxChapterIndex =
                     val maxChapterIndex =
-                        min(durChapterIndex + ReadConfig.preDownloadNum, chapterSize)
+                        min(durChapterIndex + readSettingsGateway.currentSettings.preDownloadNum, chapterSize)
                     for (i in durChapterIndex.plus(2)..maxChapterIndex) {
                     for (i in durChapterIndex.plus(2)..maxChapterIndex) {
                         if (downloadedChapters.contains(i)) continue
                         if (downloadedChapters.contains(i)) continue
                         if ((downloadFailChapters[i] ?: 0) >= 3) continue
                         if ((downloadFailChapters[i] ?: 0) >= 3) continue
@@ -1758,7 +1762,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
                     }
                     }
                 }
                 }
                 launch {
                 launch {
-                    val minChapterIndex = durChapterIndex - min(5, ReadConfig.preDownloadNum)
+                    val minChapterIndex = durChapterIndex - min(5, readSettingsGateway.currentSettings.preDownloadNum)
                     for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
                     for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
                         if (downloadedChapters.contains(i)) continue
                         if (downloadedChapters.contains(i)) continue
                         if ((downloadFailChapters[i] ?: 0) >= 3) continue
                         if ((downloadFailChapters[i] ?: 0) >= 3) continue

+ 4 - 2
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt

@@ -14,10 +14,10 @@ import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.RssArticle
 import io.legado.app.data.entities.RssArticle
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.CacheManager
 import io.legado.app.help.CacheManager
 import io.legado.app.help.JsExtensions
 import io.legado.app.help.JsExtensions
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.http.BackstageWebView
 import io.legado.app.help.http.BackstageWebView
 import io.legado.app.help.http.CookieStore
 import io.legado.app.help.http.CookieStore
 import io.legado.app.help.source.getShareScope
 import io.legado.app.help.source.getShareScope
@@ -42,6 +42,7 @@ import org.apache.commons.text.StringEscapeUtils
 import org.jsoup.nodes.Node
 import org.jsoup.nodes.Node
 import org.mozilla.javascript.NativeObject
 import org.mozilla.javascript.NativeObject
 import org.mozilla.javascript.Scriptable
 import org.mozilla.javascript.Scriptable
+import org.koin.core.context.GlobalContext
 import java.lang.ref.WeakReference
 import java.lang.ref.WeakReference
 import java.net.URL
 import java.net.URL
 import java.util.Locale
 import java.util.Locale
@@ -78,6 +79,7 @@ class AnalyzeRule(
 
 
     private val stringRuleCache = hashMapOf<String, List<SourceRule>>()
     private val stringRuleCache = hashMapOf<String, List<SourceRule>>()
     private val regexCache = hashMapOf<String, Regex?>()
     private val regexCache = hashMapOf<String, Regex?>()
+    private val cacheSettingsGateway get() = GlobalContext.get().get<DownloadCacheSettingsGateway>()
     private val scriptCache = hashMapOf<String, CompiledScript>()
     private val scriptCache = hashMapOf<String, CompiledScript>()
     private var topScopeRef: WeakReference<Scriptable>? = null
     private var topScopeRef: WeakReference<Scriptable>? = null
     private var evalJSCallCount = 0
     private var evalJSCallCount = 0
@@ -175,7 +177,7 @@ class AnalyzeRule(
                 url = baseUrl,
                 url = baseUrl,
                 html = content.toString(),
                 html = content.toString(),
                 javaScript = jsStr,
                 javaScript = jsStr,
-                headerMap = getSource()?.getHeaderMap(AppConfig.userAgent, true),
+                headerMap = getSource()?.getHeaderMap(cacheSettingsGateway.currentSettings.userAgent, true),
                 tag = getSource()?.getKey(),
                 tag = getSource()?.getKey(),
                 cacheFirst = true,
                 cacheFirst = true,
                 timeout = 10000,
                 timeout = 10000,

+ 6 - 4
app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt

@@ -13,11 +13,11 @@ import io.legado.app.constant.AppPattern
 import io.legado.app.data.entities.BaseSource
 import io.legado.app.data.entities.BaseSource
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookChapter
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.CacheManager
 import io.legado.app.help.CacheManager
 import io.legado.app.help.ConcurrentRateLimiter
 import io.legado.app.help.ConcurrentRateLimiter
 import io.legado.app.help.JsExtensions
 import io.legado.app.help.JsExtensions
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.crypto.toHexString
 import io.legado.app.help.crypto.toHexString
 import io.legado.app.help.exoplayer.ExoPlayerHelper
 import io.legado.app.help.exoplayer.ExoPlayerHelper
 import io.legado.app.help.glide.GlideHeaders
 import io.legado.app.help.glide.GlideHeaders
@@ -57,6 +57,7 @@ import okhttp3.Request
 import okhttp3.RequestBody.Companion.toRequestBody
 import okhttp3.RequestBody.Companion.toRequestBody
 import okhttp3.Response
 import okhttp3.Response
 import okhttp3.ResponseBody.Companion.toResponseBody
 import okhttp3.ResponseBody.Companion.toResponseBody
+import org.koin.core.context.GlobalContext
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayInputStream
 import java.io.InputStream
 import java.io.InputStream
 import java.net.URLEncoder
 import java.net.URLEncoder
@@ -118,6 +119,7 @@ class AnalyzeUrl(
     private val domain: String
     private val domain: String
     private var webViewDelayTime: Long = 0
     private var webViewDelayTime: Long = 0
     private val concurrentRateLimiter = ConcurrentRateLimiter(source)
     private val concurrentRateLimiter = ConcurrentRateLimiter(source)
+    private val cacheSettingsGateway get() = GlobalContext.get().get<DownloadCacheSettingsGateway>()
 
 
     // 服务器ID
     // 服务器ID
     var serverID: Long? = null
     var serverID: Long? = null
@@ -128,7 +130,7 @@ class AnalyzeUrl(
         val urlMatch = paramPattern.find(baseUrl)
         val urlMatch = paramPattern.find(baseUrl)
         if (urlMatch != null) baseUrl = baseUrl.substring(0, urlMatch.range.first)
         if (urlMatch != null) baseUrl = baseUrl.substring(0, urlMatch.range.first)
         (headerMapF ?: runScriptWithContext(coroutineContext) {
         (headerMapF ?: runScriptWithContext(coroutineContext) {
-            source?.getHeaderMap(AppConfig.userAgent, hasLoginHeader)
+            source?.getHeaderMap(cacheSettingsGateway.currentSettings.userAgent, hasLoginHeader)
         })?.let {
         })?.let {
             headerMap.putAll(it)
             headerMap.putAll(it)
             if (it.containsKey("proxy")) {
             if (it.containsKey("proxy")) {
@@ -599,7 +601,7 @@ class AnalyzeUrl(
         if (readTimeout == null && callTimeout == null && dnsIp == null) {
         if (readTimeout == null && callTimeout == null && dnsIp == null) {
             return client
             return client
         }
         }
-        if (AppConfig.isCronet && dnsIp != null) {
+        if (cacheSettingsGateway.currentSettings.cronetEnabled && dnsIp != null) {
             customIp[urlNoQuery] = dnsIp!!
             customIp[urlNoQuery] = dnsIp!!
         }
         }
         return client.newBuilder().run {
         return client.newBuilder().run {
@@ -755,7 +757,7 @@ class AnalyzeUrl(
     }
     }
 
 
     fun getUserAgent(): String {
     fun getUserAgent(): String {
-        return headerMap.get(UA_NAME, true) ?: AppConfig.userAgent
+        return headerMap.get(UA_NAME, true) ?: cacheSettingsGateway.currentSettings.userAgent
     }
     }
 
 
     fun isPost(): Boolean {
     fun isPost(): Boolean {

+ 17 - 11
app/src/main/java/io/legado/app/model/localBook/LocalBook.kt

@@ -18,6 +18,9 @@ import io.legado.app.exception.EmptyFileException
 import io.legado.app.exception.NoBooksDirException
 import io.legado.app.exception.NoBooksDirException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.TocEmptyException
 import io.legado.app.exception.TocEmptyException
+import io.legado.app.domain.gateway.ImportBookSettingsGateway
+import io.legado.app.domain.gateway.OtherSettingsGateway
+import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.help.AppWebDav
 import io.legado.app.help.AppWebDav
 import io.legado.app.help.book.BookHelp
 import io.legado.app.help.book.BookHelp
 import io.legado.app.help.book.ContentProcessor
 import io.legado.app.help.book.ContentProcessor
@@ -36,12 +39,10 @@ import io.legado.app.help.book.isUmd
 import io.legado.app.help.book.removeLocalUriCache
 import io.legado.app.help.book.removeLocalUriCache
 import io.legado.app.help.book.simulatedTotalChapterNum
 import io.legado.app.help.book.simulatedTotalChapterNum
 import io.legado.app.help.book.upKind
 import io.legado.app.help.book.upKind
-import io.legado.app.help.config.AppConfig
 import io.legado.app.lib.webdav.WebDav
 import io.legado.app.lib.webdav.WebDav
 import io.legado.app.lib.webdav.WebDavException
 import io.legado.app.lib.webdav.WebDavException
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.analyzeRule.CustomUrl
 import io.legado.app.model.analyzeRule.CustomUrl
-import io.legado.app.ui.config.otherConfig.OtherConfig
 import io.legado.app.utils.ArchiveUtils
 import io.legado.app.utils.ArchiveUtils
 import io.legado.app.utils.FileDoc
 import io.legado.app.utils.FileDoc
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.FileUtils
@@ -58,6 +59,7 @@ import io.legado.app.utils.printOnDebug
 import kotlinx.coroutines.currentCoroutineContext
 import kotlinx.coroutines.currentCoroutineContext
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.runBlocking
 import org.apache.commons.text.StringEscapeUtils
 import org.apache.commons.text.StringEscapeUtils
+import org.koin.core.context.GlobalContext
 import splitties.init.appCtx
 import splitties.init.appCtx
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayInputStream
 import java.io.File
 import java.io.File
@@ -72,6 +74,10 @@ import java.io.InputStream
  */
  */
 object LocalBook {
 object LocalBook {
 
 
+    private val otherSettingsGateway get() = GlobalContext.get().get<OtherSettingsGateway>()
+    private val readSettingsGateway get() = GlobalContext.get().get<ReadSettingsGateway>()
+    private val importBookSettingsGateway get() = GlobalContext.get().get<ImportBookSettingsGateway>()
+
     private val nameAuthorPatterns = arrayOf(
     private val nameAuthorPatterns = arrayOf(
         Regex("(.*?)《([^《》]+)》.*?作者:(.*)"),
         Regex("(.*?)《([^《》]+)》.*?作者:(.*)"),
         Regex("(.*?)《([^《》]+)》(.*)"),
         Regex("(.*?)《([^《》]+)》(.*)"),
@@ -157,15 +163,15 @@ object LocalBook {
         book.durChapterTitle = list.getOrElse(book.durChapterIndex) { list.last() }
         book.durChapterTitle = list.getOrElse(book.durChapterIndex) { list.last() }
             .getDisplayTitle(
             .getDisplayTitle(
                 replaceRules,
                 replaceRules,
-                book.getUseReplaceRule(AppConfig.replaceEnableDefault),
-                chineseConverterType = AppConfig.chineseConverterType,
+                book.getUseReplaceRule(otherSettingsGateway.currentSettings.replaceEnableDefault),
+                chineseConverterType = readSettingsGateway.currentSettings.chineseConverterType,
             )
             )
         book.latestChapterTitle =
         book.latestChapterTitle =
             list.getOrElse(book.simulatedTotalChapterNum() - 1) { list.last() }
             list.getOrElse(book.simulatedTotalChapterNum() - 1) { list.last() }
                 .getDisplayTitle(
                 .getDisplayTitle(
                     replaceRules,
                     replaceRules,
-                    book.getUseReplaceRule(AppConfig.replaceEnableDefault),
-                    chineseConverterType = AppConfig.chineseConverterType,
+                    book.getUseReplaceRule(otherSettingsGateway.currentSettings.replaceEnableDefault),
+                    chineseConverterType = readSettingsGateway.currentSettings.chineseConverterType,
                 )
                 )
         book.totalChapterNum = list.size
         book.totalChapterNum = list.size
         book.latestChapterTime = System.currentTimeMillis()
         book.latestChapterTime = System.currentTimeMillis()
@@ -385,11 +391,11 @@ object LocalBook {
         val tempFileName = fileName.substringBeforeLast(".")
         val tempFileName = fileName.substringBeforeLast(".")
         var name = ""
         var name = ""
         var author = ""
         var author = ""
-        if (!AppConfig.bookImportFileName.isNullOrBlank()) {
+        if (!importBookSettingsGateway.currentSettings.bookImportFileName.isNullOrBlank()) {
             try {
             try {
                 //在用户脚本后添加捕获author、name的代码,只要脚本中author、name有值就会被捕获
                 //在用户脚本后添加捕获author、name的代码,只要脚本中author、name有值就会被捕获
                 val js =
                 val js =
-                    AppConfig.bookImportFileName + "\nJSON.stringify({author:author,name:name})"
+                    importBookSettingsGateway.currentSettings.bookImportFileName + "\nJSON.stringify({author:author,name:name})"
                 //在脚本中定义如何分解文件名成书名、作者名
                 //在脚本中定义如何分解文件名成书名、作者名
                 val jsonStr = RhinoScriptEngine.run {
                 val jsonStr = RhinoScriptEngine.run {
                     val bindings = ScriptBindings()
                     val bindings = ScriptBindings()
@@ -446,7 +452,7 @@ object LocalBook {
         fileName: String,
         fileName: String,
         source: BaseSource? = null,
         source: BaseSource? = null,
     ): Uri {
     ): Uri {
-        OtherConfig.defaultBookTreeUri
+        otherSettingsGateway.currentSettings.defaultBookTreeUri
             ?: throw NoBooksDirException()
             ?: throw NoBooksDirException()
         val inputStream = when {
         val inputStream = when {
             str.isAbsUrl() -> AnalyzeUrl(
             str.isAbsUrl() -> AnalyzeUrl(
@@ -472,7 +478,7 @@ object LocalBook {
         fileName: String
         fileName: String
     ): Uri {
     ): Uri {
         inputStream.use {
         inputStream.use {
-            val defaultBookTreeUri = OtherConfig.defaultBookTreeUri
+            val defaultBookTreeUri = otherSettingsGateway.currentSettings.defaultBookTreeUri
             if (defaultBookTreeUri.isNullOrBlank()) throw NoBooksDirException()
             if (defaultBookTreeUri.isNullOrBlank()) throw NoBooksDirException()
             val treeUri = defaultBookTreeUri.toUri()
             val treeUri = defaultBookTreeUri.toUri()
             return if (treeUri.isContentScheme()) {
             return if (treeUri.isContentScheme()) {
@@ -526,7 +532,7 @@ object LocalBook {
         val webDavUrl = localBook.getRemoteUrl()
         val webDavUrl = localBook.getRemoteUrl()
         if (webDavUrl.isNullOrBlank()) throw NoStackTraceException("Book file is not webDav File")
         if (webDavUrl.isNullOrBlank()) throw NoStackTraceException("Book file is not webDav File")
         try {
         try {
-            OtherConfig.defaultBookTreeUri
+            otherSettingsGateway.currentSettings.defaultBookTreeUri
                 ?: throw NoBooksDirException()
                 ?: throw NoBooksDirException()
             // 兼容旧版链接
             // 兼容旧版链接
             val webdav: WebDav = kotlin.runCatching {
             val webdav: WebDav = kotlin.runCatching {

+ 6 - 3
app/src/main/java/io/legado/app/model/localBook/TextFile.kt

@@ -5,11 +5,11 @@ import io.legado.app.data.appDb
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.TxtTocRule
 import io.legado.app.data.entities.TxtTocRule
+import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.exception.EmptyFileException
 import io.legado.app.exception.EmptyFileException
 import io.legado.app.help.DefaultData
 import io.legado.app.help.DefaultData
 import io.legado.app.help.book.isLocalModified
 import io.legado.app.help.book.isLocalModified
 import io.legado.app.help.book.upKind
 import io.legado.app.help.book.upKind
-import io.legado.app.ui.config.readConfig.ReadConfig
 import io.legado.app.utils.EncodingDetect
 import io.legado.app.utils.EncodingDetect
 import io.legado.app.utils.MD5Utils
 import io.legado.app.utils.MD5Utils
 import io.legado.app.utils.StringUtils
 import io.legado.app.utils.StringUtils
@@ -18,9 +18,12 @@ import java.io.FileNotFoundException
 import java.nio.charset.Charset
 import java.nio.charset.Charset
 import java.util.regex.PatternSyntaxException
 import java.util.regex.PatternSyntaxException
 import kotlin.math.min
 import kotlin.math.min
+import org.koin.core.context.GlobalContext
 
 
 class TextFile(private var book: Book) {
 class TextFile(private var book: Book) {
 
 
+    private val readSettingsGateway get() = GlobalContext.get().get<ReadSettingsGateway>()
+
     @Suppress("ConstPropertyName")
     @Suppress("ConstPropertyName")
     companion object {
     companion object {
         private val padRegex = "^[\\n\\s]+".toRegex()
         private val padRegex = "^[\\n\\s]+".toRegex()
@@ -359,7 +362,7 @@ class TextFile(private var book: Book) {
         val toc = arrayListOf<BookChapter>()
         val toc = arrayListOf<BookChapter>()
         var bookWordCount = 0
         var bookWordCount = 0
         val maxByteLengthWithNoToc = maxLengthWithNoTocBytes(
         val maxByteLengthWithNoToc = maxLengthWithNoTocBytes(
-            charset, ReadConfig.maxLengthWithNoToc
+            charset, readSettingsGateway.currentSettings.maxLengthWithNoToc
         )
         )
         LocalBook.getBookInputStream(book).use { bis ->
         LocalBook.getBookInputStream(book).use { bis ->
             //block的个数
             //block的个数
@@ -511,7 +514,7 @@ class TextFile(private var book: Book) {
         return try {
         return try {
             Regex(volumeRule, RegexOption.MULTILINE)
             Regex(volumeRule, RegexOption.MULTILINE)
         } catch (e: PatternSyntaxException) {
         } catch (e: PatternSyntaxException) {
-            AppLog.put("TXT分卷规则正则语法错误:${rule?.name}\n$e", e)
+            AppLog.put("TXT分卷规则正则语法错误:${rule.name}\n$e", e)
             null
             null
         }
         }
     }
     }

+ 5 - 2
app/src/main/java/io/legado/app/model/remote/RemoteBookWebDav.kt

@@ -1,11 +1,11 @@
 package io.legado.app.model.remote
 package io.legado.app.model.remote
 
 
-import io.legado.app.ui.config.otherConfig.OtherConfig
 import android.net.Uri
 import android.net.Uri
 import io.legado.app.constant.AppPattern.archiveFileRegex
 import io.legado.app.constant.AppPattern.archiveFileRegex
 import io.legado.app.constant.AppPattern.bookFileRegex
 import io.legado.app.constant.AppPattern.bookFileRegex
 import io.legado.app.constant.BookType
 import io.legado.app.constant.BookType
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.Book
+import io.legado.app.domain.gateway.OtherSettingsGateway
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.book.update
 import io.legado.app.help.book.update
 import io.legado.app.lib.webdav.Authorization
 import io.legado.app.lib.webdav.Authorization
@@ -16,6 +16,7 @@ import io.legado.app.model.localBook.LocalBook
 import io.legado.app.utils.NetworkUtils
 import io.legado.app.utils.NetworkUtils
 import io.legado.app.utils.isContentScheme
 import io.legado.app.utils.isContentScheme
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.runBlocking
+import org.koin.core.context.GlobalContext
 
 
 class RemoteBookWebDav(
 class RemoteBookWebDav(
     val rootBookUrl: String,
     val rootBookUrl: String,
@@ -23,6 +24,8 @@ class RemoteBookWebDav(
     val serverID: Long? = null
     val serverID: Long? = null
 ) : RemoteBookManager() {
 ) : RemoteBookManager() {
 
 
+    private val otherSettingsGateway get() = GlobalContext.get().get<OtherSettingsGateway>()
+
     init {
     init {
         runBlocking {
         runBlocking {
             WebDav(rootBookUrl, authorization).makeAsDir()
             WebDav(rootBookUrl, authorization).makeAsDir()
@@ -57,7 +60,7 @@ class RemoteBookWebDav(
     }
     }
 
 
     override suspend fun downloadRemoteBook(remoteBook: RemoteBook): Uri {
     override suspend fun downloadRemoteBook(remoteBook: RemoteBook): Uri {
-        OtherConfig.defaultBookTreeUri
+        otherSettingsGateway.currentSettings.defaultBookTreeUri
             ?: throw NoStackTraceException("没有设置书籍保存位置!")
             ?: throw NoStackTraceException("没有设置书籍保存位置!")
         if (!NetworkUtils.isAvailable()) throw NoStackTraceException("网络不可用")
         if (!NetworkUtils.isAvailable()) throw NoStackTraceException("网络不可用")
         val webdav = WebDav(remoteBook.path, authorization)
         val webdav = WebDav(remoteBook.path, authorization)

+ 13 - 7
app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt

@@ -9,21 +9,23 @@ import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.rule.TocRule
 import io.legado.app.data.entities.rule.TocRule
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
+import io.legado.app.domain.gateway.OtherSettingsGateway
+import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.TocEmptyException
 import io.legado.app.exception.TocEmptyException
 import io.legado.app.help.book.ContentProcessor
 import io.legado.app.help.book.ContentProcessor
 import io.legado.app.help.book.simulatedTotalChapterNum
 import io.legado.app.help.book.simulatedTotalChapterNum
-import io.legado.app.help.config.AppConfig
 import io.legado.app.model.Debug
 import io.legado.app.model.Debug
 import io.legado.app.model.analyzeRule.AnalyzeRule
 import io.legado.app.model.analyzeRule.AnalyzeRule
 import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setChapter
 import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setChapter
 import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setCoroutineContext
 import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setCoroutineContext
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.analyzeRule.AnalyzeUrl
-import io.legado.app.ui.config.otherConfig.OtherConfig
 import io.legado.app.utils.isTrue
 import io.legado.app.utils.isTrue
 import io.legado.app.utils.mapAsync
 import io.legado.app.utils.mapAsync
 import kotlinx.coroutines.ensureActive
 import kotlinx.coroutines.ensureActive
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flow
+import org.koin.core.context.GlobalContext
 import org.mozilla.javascript.Context
 import org.mozilla.javascript.Context
 import splitties.init.appCtx
 import splitties.init.appCtx
 import kotlin.coroutines.coroutineContext
 import kotlin.coroutines.coroutineContext
@@ -33,6 +35,10 @@ import kotlin.coroutines.coroutineContext
  */
  */
 object BookChapterList {
 object BookChapterList {
 
 
+    private val cacheSettingsGateway get() = GlobalContext.get().get<DownloadCacheSettingsGateway>()
+    private val otherSettingsGateway get() = GlobalContext.get().get<OtherSettingsGateway>()
+    private val readSettingsGateway get() = GlobalContext.get().get<ReadSettingsGateway>()
+
     suspend fun analyzeChapterList(
     suspend fun analyzeChapterList(
         bookSource: BookSource,
         bookSource: BookSource,
         book: Book,
         book: Book,
@@ -97,7 +103,7 @@ object BookChapterList {
                     for (urlStr in chapterData.second) {
                     for (urlStr in chapterData.second) {
                         emit(urlStr)
                         emit(urlStr)
                     }
                     }
-                }.mapAsync(OtherConfig.threadCount) { urlStr ->
+                }.mapAsync(cacheSettingsGateway.currentSettings.threadCount) { urlStr ->
                     val analyzeUrl = AnalyzeUrl(
                     val analyzeUrl = AnalyzeUrl(
                         mUrl = urlStr,
                         mUrl = urlStr,
                         source = bookSource,
                         source = bookSource,
@@ -155,8 +161,8 @@ object BookChapterList {
         book.durChapterTitle = list.getOrElse(book.durChapterIndex) { list.last() }
         book.durChapterTitle = list.getOrElse(book.durChapterIndex) { list.last() }
             .getDisplayTitle(
             .getDisplayTitle(
                 replaceRules,
                 replaceRules,
-                book.getUseReplaceRule(AppConfig.replaceEnableDefault),
-                chineseConverterType = AppConfig.chineseConverterType,
+                book.getUseReplaceRule(otherSettingsGateway.currentSettings.replaceEnableDefault),
+                chineseConverterType = readSettingsGateway.currentSettings.chineseConverterType,
             )
             )
         if (book.totalChapterNum < list.size) {
         if (book.totalChapterNum < list.size) {
             book.lastCheckCount = list.size - book.totalChapterNum
             book.lastCheckCount = list.size - book.totalChapterNum
@@ -168,8 +174,8 @@ object BookChapterList {
             list.getOrElse(book.simulatedTotalChapterNum() - 1) { list.last() }
             list.getOrElse(book.simulatedTotalChapterNum() - 1) { list.last() }
                 .getDisplayTitle(
                 .getDisplayTitle(
                     replaceRules,
                     replaceRules,
-                    book.getUseReplaceRule(AppConfig.replaceEnableDefault),
-                    chineseConverterType = AppConfig.chineseConverterType,
+                    book.getUseReplaceRule(otherSettingsGateway.currentSettings.replaceEnableDefault),
+                    chineseConverterType = readSettingsGateway.currentSettings.chineseConverterType,
                 )
                 )
         coroutineContext.ensureActive()
         coroutineContext.ensureActive()
         preserveChapterMetadata(list, book)
         preserveChapterMetadata(list, book)

+ 7 - 4
app/src/main/java/io/legado/app/model/webBook/BookContent.kt

@@ -7,20 +7,20 @@ import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.rule.ContentRule
 import io.legado.app.data.entities.rule.ContentRule
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
+import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.exception.ContentEmptyException
 import io.legado.app.exception.ContentEmptyException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.book.BookHelp
 import io.legado.app.help.book.BookHelp
 import io.legado.app.help.book.isAudio
 import io.legado.app.help.book.isAudio
 import io.legado.app.help.book.isOnLineTxt
 import io.legado.app.help.book.isOnLineTxt
 import io.legado.app.help.book.isVideo
 import io.legado.app.help.book.isVideo
-import io.legado.app.help.config.AppConfig
 import io.legado.app.model.Debug
 import io.legado.app.model.Debug
 import io.legado.app.model.analyzeRule.AnalyzeRule
 import io.legado.app.model.analyzeRule.AnalyzeRule
 import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setChapter
 import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setChapter
 import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setCoroutineContext
 import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setCoroutineContext
 import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setNextChapterUrl
 import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setNextChapterUrl
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.analyzeRule.AnalyzeUrl
-import io.legado.app.ui.config.otherConfig.OtherConfig
 import io.legado.app.utils.HtmlFormatter
 import io.legado.app.utils.HtmlFormatter
 import io.legado.app.utils.NetworkUtils
 import io.legado.app.utils.NetworkUtils
 import io.legado.app.utils.mapAsync
 import io.legado.app.utils.mapAsync
@@ -28,6 +28,7 @@ import kotlinx.coroutines.currentCoroutineContext
 import kotlinx.coroutines.ensureActive
 import kotlinx.coroutines.ensureActive
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flow
 import org.apache.commons.text.StringEscapeUtils
 import org.apache.commons.text.StringEscapeUtils
+import org.koin.core.context.GlobalContext
 import splitties.init.appCtx
 import splitties.init.appCtx
 import kotlin.coroutines.coroutineContext
 import kotlin.coroutines.coroutineContext
 
 
@@ -37,6 +38,8 @@ import kotlin.coroutines.coroutineContext
 object BookContent {
 object BookContent {
 
 
     private const val maxNextPageConcurrency = 4
     private const val maxNextPageConcurrency = 4
+    private val cacheSettingsGateway get() = GlobalContext.get().get<DownloadCacheSettingsGateway>()
+    private val readSettingsGateway get() = GlobalContext.get().get<ReadSettingsGateway>()
 
 
     @Throws(Exception::class)
     @Throws(Exception::class)
     suspend fun analyzeContent(
     suspend fun analyzeContent(
@@ -119,7 +122,7 @@ object BookContent {
                 for (urlStr in contentData.second) {
                 for (urlStr in contentData.second) {
                     emit(urlStr)
                     emit(urlStr)
                 }
                 }
-            }.mapAsync(OtherConfig.threadCount.coerceIn(1, maxNextPageConcurrency)) { urlStr ->
+            }.mapAsync(cacheSettingsGateway.currentSettings.threadCount.coerceIn(1, maxNextPageConcurrency)) { urlStr ->
                 val analyzeUrl = AnalyzeUrl(
                 val analyzeUrl = AnalyzeUrl(
                     mUrl = urlStr,
                     mUrl = urlStr,
                     source = bookSource,
                     source = bookSource,
@@ -231,7 +234,7 @@ object BookContent {
         var content = analyzeRule.getString(contentRule.content, unescape = false)
         var content = analyzeRule.getString(contentRule.content, unescape = false)
         if (!book.isAudio && !book.isVideo) { //音频和视频获取的是链接,不需要html格式化 && !book.isVideo
         if (!book.isAudio && !book.isVideo) { //音频和视频获取的是链接,不需要html格式化 && !book.isVideo
             val useHtmlMap = mutableMapOf<String, String>()
             val useHtmlMap = mutableMapOf<String, String>()
-            if (AppConfig.adaptSpecialStyle) {
+            if (readSettingsGateway.currentSettings.adaptSpecialStyle) {
                 content = AppPattern.useHtmlRegex.replace(content) {
                 content = AppPattern.useHtmlRegex.replace(content) {
                     val placeholder = "{usehtml_${useHtmlMap.size}}"
                     val placeholder = "{usehtml_${useHtmlMap.size}}"
                     useHtmlMap[placeholder] = it.value
                     useHtmlMap[placeholder] = it.value

+ 6 - 3
app/src/main/java/io/legado/app/receiver/MediaButtonReceiver.kt

@@ -7,8 +7,8 @@ import android.view.KeyEvent
 import io.legado.app.constant.EventBus
 import io.legado.app.constant.EventBus
 import io.legado.app.constant.PreferKey
 import io.legado.app.constant.PreferKey
 import io.legado.app.data.appDb
 import io.legado.app.data.appDb
+import io.legado.app.domain.gateway.ReadAloudSettingsGateway
 import io.legado.app.help.LifecycleHelp
 import io.legado.app.help.LifecycleHelp
-import io.legado.app.help.config.AppConfig
 import io.legado.app.model.AudioPlay
 import io.legado.app.model.AudioPlay
 import io.legado.app.model.ReadAloud
 import io.legado.app.model.ReadAloud
 import io.legado.app.model.ReadBook
 import io.legado.app.model.ReadBook
@@ -18,6 +18,7 @@ import io.legado.app.ui.main.MainActivity
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.getPrefBoolean
 import io.legado.app.utils.getPrefBoolean
 import io.legado.app.utils.postEvent
 import io.legado.app.utils.postEvent
+import org.koin.core.context.GlobalContext
 
 
 
 
 /**
 /**
@@ -36,6 +37,8 @@ class MediaButtonReceiver : BroadcastReceiver() {
 
 
         private const val TAG = "MediaButtonReceiver"
         private const val TAG = "MediaButtonReceiver"
 
 
+        private val aloudSettingsGateway get() = GlobalContext.get().get<ReadAloudSettingsGateway>()
+
         fun handleIntent(context: Context, intent: Intent): Boolean {
         fun handleIntent(context: Context, intent: Intent): Boolean {
             val intentAction = intent.action
             val intentAction = intent.action
             if (Intent.ACTION_MEDIA_BUTTON == intentAction) {
             if (Intent.ACTION_MEDIA_BUTTON == intentAction) {
@@ -90,14 +93,14 @@ class MediaButtonReceiver : BroadcastReceiver() {
                     }
                     }
                 }
                 }
 
 
-                isMediaKey && !AppConfig.readAloudByMediaButton -> {
+                isMediaKey && !aloudSettingsGateway.currentSettings.readAloudByMediaButton -> {
                     // break
                     // break
                 }
                 }
 
 
                 MainActivity.hasActiveReadBookRoute || MainActivity.hasActiveAudioPlayRoute ->
                 MainActivity.hasActiveReadBookRoute || MainActivity.hasActiveAudioPlayRoute ->
                     postEvent(EventBus.MEDIA_BUTTON, true)
                     postEvent(EventBus.MEDIA_BUTTON, true)
 
 
-                else -> if (AppConfig.mediaButtonOnExit || LifecycleHelp.activitySize() > 0 || !isMediaKey) {
+                else -> if (aloudSettingsGateway.currentSettings.mediaButtonOnExit || LifecycleHelp.activitySize() > 0 || !isMediaKey) {
                     ReadAloud.upReadAloudClass()
                     ReadAloud.upReadAloudClass()
                     if (ReadBook.book != null) {
                     if (ReadBook.book != null) {
                         ReadBook.readAloud()
                         ReadBook.readAloud()

+ 10 - 7
app/src/main/java/io/legado/app/service/AudioPlayService.kt

@@ -31,9 +31,10 @@ import io.legado.app.constant.EventBus
 import io.legado.app.constant.IntentAction
 import io.legado.app.constant.IntentAction
 import io.legado.app.constant.NotificationId
 import io.legado.app.constant.NotificationId
 import io.legado.app.constant.Status
 import io.legado.app.constant.Status
+import io.legado.app.domain.gateway.OtherSettingsGateway
+import io.legado.app.domain.gateway.ReadAloudSettingsGateway
 import io.legado.app.domain.model.PlaybackTimer
 import io.legado.app.domain.model.PlaybackTimer
 import io.legado.app.help.MediaHelp
 import io.legado.app.help.MediaHelp
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.exoplayer.ExoPlayerHelper
 import io.legado.app.help.exoplayer.ExoPlayerHelper
 import io.legado.app.help.glide.ImageLoader
 import io.legado.app.help.glide.ImageLoader
@@ -41,7 +42,6 @@ import io.legado.app.model.AudioPlay
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.analyzeRule.AnalyzeUrl.Companion.getMediaItem
 import io.legado.app.model.analyzeRule.AnalyzeUrl.Companion.getMediaItem
 import io.legado.app.receiver.MediaButtonReceiver
 import io.legado.app.receiver.MediaButtonReceiver
-import io.legado.app.ui.config.readConfig.ReadConfig
 import io.legado.app.ui.main.MainActivity
 import io.legado.app.ui.main.MainActivity
 import io.legado.app.utils.activityPendingIntent
 import io.legado.app.utils.activityPendingIntent
 import io.legado.app.utils.postEvent
 import io.legado.app.utils.postEvent
@@ -53,6 +53,7 @@ import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
+import org.koin.core.context.GlobalContext
 import splitties.init.appCtx
 import splitties.init.appCtx
 import splitties.systemservices.audioManager
 import splitties.systemservices.audioManager
 import splitties.systemservices.notificationManager
 import splitties.systemservices.notificationManager
@@ -96,7 +97,9 @@ class AudioPlayService : BaseService(),
         private const val APP_ACTION_TIMER = "Timer"
         private const val APP_ACTION_TIMER = "Timer"
     }
     }
 
 
-    private val useWakeLock = AppConfig.audioPlayUseWakeLock
+    private val otherSettingsGateway get() = GlobalContext.get().get<OtherSettingsGateway>()
+    private val aloudSettingsGateway get() = GlobalContext.get().get<ReadAloudSettingsGateway>()
+    private val useWakeLock = otherSettingsGateway.currentSettings.audioPlayUseWakeLock
     private val wakeLock by lazy {
     private val wakeLock by lazy {
         powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "legado:AudioPlayService")
         powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "legado:AudioPlayService")
             .apply {
             .apply {
@@ -515,7 +518,7 @@ class AudioPlayService : BaseService(),
     @SuppressLint("UnspecifiedImmutableFlag")
     @SuppressLint("UnspecifiedImmutableFlag")
     private fun initMediaSession() {
     private fun initMediaSession() {
         mediaSessionCompat = MediaSessionCompat(this, "readAloud")
         mediaSessionCompat = MediaSessionCompat(this, "readAloud")
-        if (ReadConfig.systemMediaControlCompatibilityChange) {
+        if (aloudSettingsGateway.currentSettings.systemMediaControlCompatibilityChange) {
             mediaSessionCompat?.setCallback(object : MediaSessionCompat.Callback() {
             mediaSessionCompat?.setCallback(object : MediaSessionCompat.Callback() {
                 override fun onSeekTo(pos: Long) {
                 override fun onSeekTo(pos: Long) {
                     position = pos.toInt()
                     position = pos.toInt()
@@ -580,7 +583,7 @@ class AudioPlayService : BaseService(),
      * 音频焦点变化
      * 音频焦点变化
      */
      */
     override fun onAudioFocusChange(focusChange: Int) {
     override fun onAudioFocusChange(focusChange: Int) {
-        if (ReadConfig.ignoreAudioFocus) {
+        if (aloudSettingsGateway.currentSettings.ignoreAudioFocus) {
             AppLog.put("忽略音频焦点处理(有声)")
             AppLog.put("忽略音频焦点处理(有声)")
             return
             return
         }
         }
@@ -680,7 +683,7 @@ class AudioPlayService : BaseService(),
     private fun choiceMediaStyle(): androidx.media.app.NotificationCompat.MediaStyle {
     private fun choiceMediaStyle(): androidx.media.app.NotificationCompat.MediaStyle {
         val mediaStyle = androidx.media.app.NotificationCompat.MediaStyle()
         val mediaStyle = androidx.media.app.NotificationCompat.MediaStyle()
         mediaStyle.setShowActionsInCompactView(1,2,4)
         mediaStyle.setShowActionsInCompactView(1,2,4)
-        if (ReadConfig.systemMediaControlCompatibilityChange) {
+        if (aloudSettingsGateway.currentSettings.systemMediaControlCompatibilityChange) {
             //fix #4090 android 14 can not show play control in lock screen
             //fix #4090 android 14 can not show play control in lock screen
             mediaStyle.setMediaSession(mediaSessionCompat?.sessionToken)
             mediaStyle.setMediaSession(mediaSessionCompat?.sessionToken)
         }
         }
@@ -717,7 +720,7 @@ class AudioPlayService : BaseService(),
      * @return 音频焦点
      * @return 音频焦点
      */
      */
     private fun requestFocus(): Boolean {
     private fun requestFocus(): Boolean {
-        if (ReadConfig.ignoreAudioFocus) {
+        if (aloudSettingsGateway.currentSettings.ignoreAudioFocus) {
             return true
             return true
         }
         }
         return MediaHelp.requestFocus(mFocusRequest)
         return MediaHelp.requestFocus(mFocusRequest)

+ 4 - 2
app/src/main/java/io/legado/app/service/CacheBookService.kt

@@ -10,6 +10,7 @@ import io.legado.app.constant.AppLog
 import io.legado.app.constant.IntentAction
 import io.legado.app.constant.IntentAction
 import io.legado.app.constant.NotificationId
 import io.legado.app.constant.NotificationId
 import io.legado.app.data.appDb
 import io.legado.app.data.appDb
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.help.book.update
 import io.legado.app.help.book.update
 import io.legado.app.model.CacheBook
 import io.legado.app.model.CacheBook
 import io.legado.app.model.CacheBookModel
 import io.legado.app.model.CacheBookModel
@@ -18,7 +19,6 @@ import io.legado.app.model.cache.CacheDownloadRequest
 import io.legado.app.model.cache.CacheDownloadSource
 import io.legado.app.model.cache.CacheDownloadSource
 import io.legado.app.model.cache.ChapterSelection
 import io.legado.app.model.cache.ChapterSelection
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.model.webBook.WebBook
-import io.legado.app.ui.config.otherConfig.OtherConfig
 import io.legado.app.ui.main.MainActivity
 import io.legado.app.ui.main.MainActivity
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.LogUtils
 import io.legado.app.utils.activityPendingIntent
 import io.legado.app.utils.activityPendingIntent
@@ -37,6 +37,7 @@ import kotlinx.coroutines.isActive
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.sync.withLock
+import org.koin.core.context.GlobalContext
 import splitties.init.appCtx
 import splitties.init.appCtx
 import splitties.systemservices.notificationManager
 import splitties.systemservices.notificationManager
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.ConcurrentHashMap
@@ -58,7 +59,8 @@ class CacheBookService : BaseService() {
             private set
             private set
     }
     }
 
 
-    private val threadCount = OtherConfig.cacheBookThreadCount.coerceIn(1, CacheBook.maxDownloadConcurrency)
+    private val cacheSettingsGateway get() = GlobalContext.get().get<DownloadCacheSettingsGateway>()
+    private val threadCount = cacheSettingsGateway.currentSettings.cacheBookThreadCount.coerceIn(1, CacheBook.maxDownloadConcurrency)
     // 显式离线缓存:书籍级 FIFO,同时只准入一本;单书内仍用 threadCount 并发章节
     // 显式离线缓存:书籍级 FIFO,同时只准入一本;单书内仍用 threadCount 并发章节
     private val maxActiveBookCount = 1
     private val maxActiveBookCount = 1
     private val admissionQueue = CacheDownloadAdmissionQueue(maxActiveBookCount)
     private val admissionQueue = CacheDownloadAdmissionQueue(maxActiveBookCount)

+ 9 - 8
app/src/main/java/io/legado/app/ui/about/AboutViewModel.kt

@@ -7,9 +7,9 @@ import androidx.lifecycle.viewModelScope
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.base.BaseViewModel
 import io.legado.app.base.BaseViewModel
 import io.legado.app.constant.AppLog
 import io.legado.app.constant.AppLog
+import io.legado.app.domain.gateway.BackupSettingsGateway
 import io.legado.app.domain.gateway.OtherSettingsGateway
 import io.legado.app.domain.gateway.OtherSettingsGateway
 import io.legado.app.help.CrashHandler
 import io.legado.app.help.CrashHandler
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.update.AppUpdate
 import io.legado.app.help.update.AppUpdate
 import io.legado.app.utils.FileDoc
 import io.legado.app.utils.FileDoc
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.FileUtils
@@ -38,7 +38,8 @@ import java.io.FileFilter
 
 
 class AboutViewModel(
 class AboutViewModel(
     application: Application,
     application: Application,
-    otherSettingsGateway: OtherSettingsGateway,
+    private val otherSettingsGateway: OtherSettingsGateway,
+    private val backupSettingsGateway: BackupSettingsGateway,
 ) : BaseViewModel(application) {
 ) : BaseViewModel(application) {
 
 
     private val _uiState = MutableStateFlow(
     private val _uiState = MutableStateFlow(
@@ -134,7 +135,7 @@ class AboutViewModel(
             context.externalCacheDir
             context.externalCacheDir
                 ?.getFile("crash")
                 ?.getFile("crash")
                 ?.let { FileUtils.delete(it, false) }
                 ?.let { FileUtils.delete(it, false) }
-            val backupPath = AppConfig.backupPath
+            val backupPath = backupSettingsGateway.currentSettings.backupPath
             if (!backupPath.isNullOrEmpty()) {
             if (!backupPath.isNullOrEmpty()) {
                 val uri = Uri.parse(backupPath)
                 val uri = Uri.parse(backupPath)
                 FileDoc.fromUri(uri, true)
                 FileDoc.fromUri(uri, true)
@@ -158,7 +159,7 @@ class AboutViewModel(
             ?.getFile("crash")
             ?.getFile("crash")
             ?.listFiles(FileFilter { it.isFile })
             ?.listFiles(FileFilter { it.isFile })
             ?.forEach { list.add(FileDoc.fromFile(it)) }
             ?.forEach { list.add(FileDoc.fromFile(it)) }
-        val backupPath = AppConfig.backupPath
+        val backupPath = backupSettingsGateway.currentSettings.backupPath
         if (!backupPath.isNullOrEmpty()) {
         if (!backupPath.isNullOrEmpty()) {
             val uri = Uri.parse(backupPath)
             val uri = Uri.parse(backupPath)
             FileDoc.fromUri(uri, true)
             FileDoc.fromUri(uri, true)
@@ -171,11 +172,11 @@ class AboutViewModel(
 
 
     private fun saveLog() {
     private fun saveLog() {
         execute {
         execute {
-            val backupPath = AppConfig.backupPath ?: run {
+            val backupPath = backupSettingsGateway.currentSettings.backupPath ?: run {
                 _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_backup_dir_not_set)))
                 _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_backup_dir_not_set)))
                 return@execute
                 return@execute
             }
             }
-            if (!AppConfig.recordLog) {
+            if (!otherSettingsGateway.currentSettings.recordLog) {
                 _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_log_recording_disabled)))
                 _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_log_recording_disabled)))
                 delay(3000)
                 delay(3000)
             }
             }
@@ -190,11 +191,11 @@ class AboutViewModel(
 
 
     private fun createHeapDump() {
     private fun createHeapDump() {
         execute {
         execute {
-            val backupPath = AppConfig.backupPath ?: run {
+            val backupPath = backupSettingsGateway.currentSettings.backupPath ?: run {
                 _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_backup_dir_not_set)))
                 _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_backup_dir_not_set)))
                 return@execute
                 return@execute
             }
             }
-            if (!AppConfig.recordHeapDump) {
+            if (!otherSettingsGateway.currentSettings.recordHeapDump) {
                 _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_heap_dump_recording_disabled)))
                 _effects.tryEmit(AboutEffect.ShowToast(context.getString(R.string.about_heap_dump_recording_disabled)))
                 delay(3000)
                 delay(3000)
             }
             }

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

@@ -18,7 +18,6 @@ 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.Item1lineTextBinding
 import io.legado.app.databinding.Item1lineTextBinding
-import io.legado.app.help.config.AppConfig
 //import io.legado.app.lib.theme.primaryColor
 //import io.legado.app.lib.theme.primaryColor
 import io.legado.app.ui.widget.dialog.TextDialog
 import io.legado.app.ui.widget.dialog.TextDialog
 import io.legado.app.utils.FileDoc
 import io.legado.app.utils.FileDoc
@@ -112,7 +111,7 @@ class CrashLogsDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
                     ?.forEach {
                     ?.forEach {
                         list.add(FileDoc.fromFile(it))
                         list.add(FileDoc.fromFile(it))
                     }
                     }
-                val backupPath = AppConfig.backupPath
+                val backupPath = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.BackupSettingsGateway>().currentSettings.backupPath
                 if (!backupPath.isNullOrEmpty()) {
                 if (!backupPath.isNullOrEmpty()) {
                     val uri = Uri.parse(backupPath)
                     val uri = Uri.parse(backupPath)
                     FileDoc.fromUri(uri, true)
                     FileDoc.fromUri(uri, true)
@@ -146,7 +145,7 @@ class CrashLogsDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_v
                     ?.let {
                     ?.let {
                         FileUtils.delete(it, false)
                         FileUtils.delete(it, false)
                     }
                     }
-                val backupPath = AppConfig.backupPath
+                val backupPath = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.BackupSettingsGateway>().currentSettings.backupPath
                 if (!backupPath.isNullOrEmpty()) {
                 if (!backupPath.isNullOrEmpty()) {
                     val uri = Uri.parse(backupPath)
                     val uri = Uri.parse(backupPath)
                     FileDoc.fromUri(uri, true)
                     FileDoc.fromUri(uri, true)

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

@@ -8,7 +8,6 @@ import io.legado.app.R
 import io.legado.app.base.BaseBottomSheetDialogFragment
 import io.legado.app.base.BaseBottomSheetDialogFragment
 import io.legado.app.constant.AppConst
 import io.legado.app.constant.AppConst
 import io.legado.app.databinding.DialogUpdateBinding
 import io.legado.app.databinding.DialogUpdateBinding
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.update.AppUpdate
 import io.legado.app.help.update.AppUpdate
 import io.legado.app.help.update.AppVariant
 import io.legado.app.help.update.AppVariant
 import io.legado.app.model.Download
 import io.legado.app.model.Download
@@ -22,6 +21,8 @@ import io.noties.markwon.image.glide.GlideImagesPlugin
 
 
 class UpdateDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_update) {
 class UpdateDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_update) {
 
 
+    private val otherSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.OtherSettingsGateway>()
+
     enum class Mode { UPDATE, VIEW_LOG }
     enum class Mode { UPDATE, VIEW_LOG }
 
 
     constructor(updateInfo: AppUpdate.UpdateInfo, mode: Mode = Mode.UPDATE) : this() {
     constructor(updateInfo: AppUpdate.UpdateInfo, mode: Mode = Mode.UPDATE) : this() {
@@ -35,7 +36,7 @@ class UpdateDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_update) {
     }
     }
 
 
     private val checkVariant: AppVariant
     private val checkVariant: AppVariant
-        get() = when (AppConfig.updateToVariant) {
+        get() = when (otherSettingsGateway.currentSettings.updateToVariant) {
             "official_version" -> AppVariant.OFFICIAL
             "official_version" -> AppVariant.OFFICIAL
             "beta_release_version" -> AppVariant.BETA_RELEASE
             "beta_release_version" -> AppVariant.BETA_RELEASE
             "all_version" -> AppVariant.ALL
             "all_version" -> AppVariant.ALL

+ 9 - 4
app/src/main/java/io/legado/app/ui/ai/chat/AiChatScreen.kt

@@ -69,10 +69,11 @@ import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.lerp
 import androidx.compose.ui.graphics.lerp
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.platform.LocalClipboardManager
+import android.content.ClipData
+import androidx.compose.ui.platform.ClipEntry
+import androidx.compose.ui.platform.LocalClipboard
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
@@ -127,7 +128,7 @@ fun AiChatScreen(
     val snackbarHostState = remember { SnackbarHostState() }
     val snackbarHostState = remember { SnackbarHostState() }
     val drawerState = rememberDrawerState(DrawerValue.Closed)
     val drawerState = rememberDrawerState(DrawerValue.Closed)
     val scope = rememberCoroutineScope()
     val scope = rememberCoroutineScope()
-    val clipboardManager = LocalClipboardManager.current
+    val clipboard = LocalClipboard.current
     var draft by rememberSaveable { mutableStateOf("") }
     var draft by rememberSaveable { mutableStateOf("") }
     val listState = rememberLazyListState()
     val listState = rememberLazyListState()
     val density = LocalDensity.current
     val density = LocalDensity.current
@@ -329,7 +330,11 @@ fun AiChatScreen(
                             assistantLabel = assistantLabel,
                             assistantLabel = assistantLabel,
                             onOpenBookInfo = onOpenBookInfo,
                             onOpenBookInfo = onOpenBookInfo,
                             onCopy = {
                             onCopy = {
-                                clipboardManager.setText(AnnotatedString(message.content))
+                                scope.launch {
+                                    clipboard.setClipEntry(
+                                        ClipEntry(ClipData.newPlainText("content", message.content))
+                                    )
+                                }
                             },
                             },
                             onRegenerate = if (message.role == AiMessageRole.ASSISTANT && message.parentMessageId != null) {
                             onRegenerate = if (message.role == AiMessageRole.ASSISTANT && message.parentMessageId != null) {
                                 { onIntent(AiChatIntent.RegenerateMessage(message.id)) }
                                 { onIntent(AiChatIntent.RegenerateMessage(message.id)) }

+ 2 - 2
app/src/main/java/io/legado/app/ui/ai/chat/AiChatViewModel.kt

@@ -373,7 +373,7 @@ class AiChatViewModel(
                                 reasoning = fullReasoning.toString(),
                                 reasoning = fullReasoning.toString(),
                                 toolTrace = toolTrace
                                 toolTrace = toolTrace
                             ),
                             ),
-                            parentMessageId = message.parentMessageId!!,
+                            parentMessageId = message.parentMessageId,
                             thinkingDuration = duration
                             thinkingDuration = duration
                         )
                         )
                     }
                     }
@@ -482,7 +482,7 @@ class AiChatViewModel(
                         )
                         )
                     }
                     }
                     // Generate AI title for first conversation
                     // Generate AI title for first conversation
-                    if (conversationIdForMsg != null && historySnapshot.none { it.role == AiMessageRole.ASSISTANT }) {
+                    if (historySnapshot.none { it.role == AiMessageRole.ASSISTANT }) {
                         generateConversationTitle(conversationIdForMsg, content, assistantContent)
                         generateConversationTitle(conversationIdForMsg, content, assistantContent)
                     }
                     }
                 }
                 }

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

@@ -19,7 +19,6 @@ import io.legado.app.data.entities.RssSource
 import io.legado.app.databinding.DialogCustomGroupBinding
 import io.legado.app.databinding.DialogCustomGroupBinding
 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.AppConfig
 import io.legado.app.domain.gateway.OtherSettingsGateway
 import io.legado.app.domain.gateway.OtherSettingsGateway
 import io.legado.app.domain.model.settings.OtherSettings
 import io.legado.app.domain.model.settings.OtherSettings
 import io.legado.app.lib.dialogs.alert
 import io.legado.app.lib.dialogs.alert
@@ -143,11 +142,11 @@ class ImportRssSourceDialog() : BaseDialogFragment(R.layout.dialog_recycler_view
         binding.toolBar.setOnMenuItemClickListener(this)
         binding.toolBar.setOnMenuItemClickListener(this)
         binding.toolBar.inflateMenu(R.menu.import_source)
         binding.toolBar.inflateMenu(R.menu.import_source)
         binding.toolBar.menu.findItem(R.id.menu_keep_original_name)?.isChecked =
         binding.toolBar.menu.findItem(R.id.menu_keep_original_name)?.isChecked =
-            AppConfig.importKeepName
+            otherSettingsGateway.currentSettings.importKeepName
         binding.toolBar.menu.findItem(R.id.menu_keep_group)?.isChecked =
         binding.toolBar.menu.findItem(R.id.menu_keep_group)?.isChecked =
-            AppConfig.importKeepGroup
+            otherSettingsGateway.currentSettings.importKeepGroup
         binding.toolBar.menu.findItem(R.id.menu_keep_enable)?.isChecked =
         binding.toolBar.menu.findItem(R.id.menu_keep_enable)?.isChecked =
-            AppConfig.importKeepEnable
+            otherSettingsGateway.currentSettings.importKeepEnable
         binding.toolBar.menu.findItem(R.id.menu_select_new_source)?.isVisible = false
         binding.toolBar.menu.findItem(R.id.menu_select_new_source)?.isVisible = false
         binding.toolBar.menu.findItem(R.id.menu_select_update_source)?.isVisible = false
         binding.toolBar.menu.findItem(R.id.menu_select_update_source)?.isVisible = false
     }
     }

+ 5 - 4
app/src/main/java/io/legado/app/ui/association/ImportRssSourceViewModel.kt

@@ -6,13 +6,13 @@ import androidx.lifecycle.MutableLiveData
 import com.jayway.jsonpath.JsonPath
 import com.jayway.jsonpath.JsonPath
 import io.legado.app.R
 import io.legado.app.R
 import io.legado.app.base.BaseViewModel
 import io.legado.app.base.BaseViewModel
+import io.legado.app.domain.gateway.OtherSettingsGateway
 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.constant.AppPattern
 import io.legado.app.constant.AppPattern
 import io.legado.app.data.entities.RssSource
 import io.legado.app.data.entities.RssSource
 import io.legado.app.data.repository.RssRepository
 import io.legado.app.data.repository.RssRepository
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
-import io.legado.app.help.config.AppConfig
 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
@@ -32,6 +32,7 @@ import splitties.init.appCtx
 class ImportRssSourceViewModel(
 class ImportRssSourceViewModel(
     app: Application,
     app: Application,
     private val repository: RssRepository,
     private val repository: RssRepository,
+    private val otherSettingsGateway: OtherSettingsGateway,
 ) : BaseViewModel(app) {
 ) : BaseViewModel(app) {
     var isAddGroup = false
     var isAddGroup = false
     var groupName: String? = null
     var groupName: String? = null
@@ -66,9 +67,9 @@ class ImportRssSourceViewModel(
     fun importSelect(finally: () -> Unit) {
     fun importSelect(finally: () -> Unit) {
         execute {
         execute {
             val group = groupName?.trim()
             val group = groupName?.trim()
-            val keepName = AppConfig.importKeepName
-            val keepGroup = AppConfig.importKeepGroup
-            val keepEnable = AppConfig.importKeepEnable
+            val keepName = otherSettingsGateway.currentSettings.importKeepName
+            val keepGroup = otherSettingsGateway.currentSettings.importKeepGroup
+            val keepEnable = otherSettingsGateway.currentSettings.importKeepEnable
             val selectSource = arrayListOf<RssSource>()
             val selectSource = arrayListOf<RssSource>()
             selectStatus.forEachIndexed { index, b ->
             selectStatus.forEachIndexed { index, b ->
                 if (b) {
                 if (b) {

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

@@ -428,13 +428,6 @@ private fun BookmarkGroupHeaderContent(
             supportingColor = LegadoTheme.colorScheme.onSurfaceVariant,
             supportingColor = LegadoTheme.colorScheme.onSurfaceVariant,
             trailingIconColor = LegadoTheme.colorScheme.onSurfaceVariant
             trailingIconColor = LegadoTheme.colorScheme.onSurfaceVariant
         ),
         ),
-        headlineContent = {
-            AppText(
-                text = title,
-                style = LegadoTheme.typography.titleMedium,
-                color = contentColor
-            )
-        },
         supportingContent = {
         supportingContent = {
             subtitle?.let {
             subtitle?.let {
                 AppText(
                 AppText(
@@ -444,5 +437,11 @@ private fun BookmarkGroupHeaderContent(
                 )
                 )
             }
             }
         }
         }
-    )
+    ) {
+        AppText(
+            text = title,
+            style = LegadoTheme.typography.titleMedium,
+            color = contentColor
+        )
+    }
 }
 }

+ 3 - 2
app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt

@@ -4,6 +4,7 @@ import android.app.Application
 import android.os.Bundle
 import android.os.Bundle
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.viewModelScope
 import androidx.lifecycle.viewModelScope
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.base.BaseViewModel
 import io.legado.app.base.BaseViewModel
 import io.legado.app.constant.AppLog
 import io.legado.app.constant.AppLog
 import io.legado.app.constant.AppPattern
 import io.legado.app.constant.AppPattern
@@ -13,7 +14,6 @@ import io.legado.app.data.entities.SearchBook
 import io.legado.app.data.repository.BookSourceRepository
 import io.legado.app.data.repository.BookSourceRepository
 import io.legado.app.data.repository.SearchRepository
 import io.legado.app.data.repository.SearchRepository
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.model.webBook.WebBook
-import io.legado.app.ui.config.otherConfig.OtherConfig
 import io.legado.app.utils.mapParallelSafe
 import io.legado.app.utils.mapParallelSafe
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.ExecutorCoroutineDispatcher
 import kotlinx.coroutines.ExecutorCoroutineDispatcher
@@ -38,8 +38,9 @@ class ChangeCoverViewModel(
     application: Application,
     application: Application,
     private val searchRepository: SearchRepository,
     private val searchRepository: SearchRepository,
     private val bookSourceRepository: BookSourceRepository,
     private val bookSourceRepository: BookSourceRepository,
+    private val downloadCacheSettingsGateway: DownloadCacheSettingsGateway,
 ) : BaseViewModel(application) {
 ) : BaseViewModel(application) {
-    private val threadCount = OtherConfig.threadCount
+    private val threadCount = downloadCacheSettingsGateway.currentSettings.threadCount
     private var searchPool: ExecutorCoroutineDispatcher? = null
     private var searchPool: ExecutorCoroutineDispatcher? = null
     private var searchSuccess: ((SearchBook) -> Unit)? = null
     private var searchSuccess: ((SearchBook) -> Unit)? = null
     private var upAdapter: (() -> Unit)? = null
     private var upAdapter: (() -> Unit)? = null

+ 4 - 2
app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceAdapter.kt

@@ -25,6 +25,8 @@ class ChangeBookSourceAdapter(
     val callBack: CallBack
     val callBack: CallBack
 ) : DiffRecyclerAdapter<SearchBook, ItemChangeSourceBinding>(context) {
 ) : DiffRecyclerAdapter<SearchBook, ItemChangeSourceBinding>(context) {
 
 
+    private val changeSourceSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.ChangeSourceSettingsGateway>()
+
     override val diffItemCallback = object : DiffUtil.ItemCallback<SearchBook>() {
     override val diffItemCallback = object : DiffUtil.ItemCallback<SearchBook>() {
         override fun areItemsTheSame(oldItem: SearchBook, newItem: SearchBook): Boolean {
         override fun areItemsTheSame(oldItem: SearchBook, newItem: SearchBook): Boolean {
             return oldItem.bookUrl == newItem.bookUrl
             return oldItem.bookUrl == newItem.bookUrl
@@ -90,13 +92,13 @@ class ChangeBookSourceAdapter(
                 binding.ivGood.setImageResource(R.drawable.ic_praise)
                 binding.ivGood.setImageResource(R.drawable.ic_praise)
             }
             }
 
 
-            if (ChangeSourceConfig.loadWordCount && !item.chapterWordCountText.isNullOrBlank()) {
+            if (changeSourceSettingsGateway.currentSettings.loadWordCount && !item.chapterWordCountText.isNullOrBlank()) {
                 tvCurrentChapterWordCount.visible()
                 tvCurrentChapterWordCount.visible()
             } else {
             } else {
                 tvCurrentChapterWordCount.gone()
                 tvCurrentChapterWordCount.gone()
             }
             }
 
 
-            if (ChangeSourceConfig.loadWordCount && item.respondTime >= 0) {
+            if (changeSourceSettingsGateway.currentSettings.loadWordCount && item.respondTime >= 0) {
                 tvRespondTime.visible()
                 tvRespondTime.visible()
             } else {
             } else {
                 tvRespondTime.gone()
                 tvRespondTime.gone()

+ 13 - 10
app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceDialog.kt

@@ -29,8 +29,9 @@ import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.SearchBook
 import io.legado.app.data.entities.SearchBook
 import io.legado.app.databinding.DialogBookChangeSourceBinding
 import io.legado.app.databinding.DialogBookChangeSourceBinding
 import io.legado.app.domain.gateway.ChangeSourceSettingsGateway
 import io.legado.app.domain.gateway.ChangeSourceSettingsGateway
+import io.legado.app.domain.gateway.OtherSettingsGateway
+import io.legado.app.domain.gateway.ReadSettingsGateway
 import io.legado.app.domain.model.settings.ChangeSourceSettings
 import io.legado.app.domain.model.settings.ChangeSourceSettings
-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.model.ReadBook
 import io.legado.app.model.ReadBook
 import io.legado.app.ui.book.search.SearchScope
 import io.legado.app.ui.book.search.SearchScope
@@ -73,6 +74,8 @@ class ChangeBookSourceDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_b
     private val callBack: CallBack? get() = activity as? CallBack
     private val callBack: CallBack? get() = activity as? CallBack
     private val viewModel: ChangeBookSourceViewModel by viewModel()
     private val viewModel: ChangeBookSourceViewModel by viewModel()
     private val changeSourceSettingsGateway by inject<ChangeSourceSettingsGateway>()
     private val changeSourceSettingsGateway by inject<ChangeSourceSettingsGateway>()
+    private val otherSettingsGateway by inject<OtherSettingsGateway>()
+    private val readSettingsGateway by inject<ReadSettingsGateway>()
     private val waitDialog by lazy { WaitDialog(requireContext()) }
     private val waitDialog by lazy { WaitDialog(requireContext()) }
     private val adapter by lazy { ChangeBookSourceAdapter(requireContext(), viewModel, this) }
     private val adapter by lazy { ChangeBookSourceAdapter(requireContext(), viewModel, this) }
     private val editSourceResult =
     private val editSourceResult =
@@ -83,7 +86,7 @@ class ChangeBookSourceDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_b
     private var currentSelectedSearchBook: SearchBook? = null
     private var currentSelectedSearchBook: SearchBook? = null
     private val searchFinishCallback: (isEmpty: Boolean) -> Unit = {
     private val searchFinishCallback: (isEmpty: Boolean) -> Unit = {
         if (it) {
         if (it) {
-            val searchScope = SearchScope(ChangeSourceConfig.searchScope)
+            val searchScope = SearchScope(changeSourceSettingsGateway.currentSettings.searchScope)
             val group = searchScope.display
             val group = searchScope.display
             if (!searchScope.isAll()) {
             if (!searchScope.isAll()) {
                 lifecycleScope.launch {
                 lifecycleScope.launch {
@@ -134,13 +137,13 @@ class ChangeBookSourceDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_b
         binding.toolBar.menu.applyTint(requireContext())
         binding.toolBar.menu.applyTint(requireContext())
         binding.toolBar.setOnMenuItemClickListener(this)
         binding.toolBar.setOnMenuItemClickListener(this)
         binding.toolBar.menu.findItem(R.id.menu_check_author)
         binding.toolBar.menu.findItem(R.id.menu_check_author)
-            ?.isChecked = ChangeSourceConfig.checkAuthor
+            ?.isChecked = changeSourceSettingsGateway.currentSettings.checkAuthor
         binding.toolBar.menu.findItem(R.id.menu_load_info)
         binding.toolBar.menu.findItem(R.id.menu_load_info)
-            ?.isChecked = ChangeSourceConfig.loadInfo
+            ?.isChecked = changeSourceSettingsGateway.currentSettings.loadInfo
         binding.toolBar.menu.findItem(R.id.menu_load_toc)
         binding.toolBar.menu.findItem(R.id.menu_load_toc)
-            ?.isChecked = ChangeSourceConfig.loadToc
+            ?.isChecked = changeSourceSettingsGateway.currentSettings.loadToc
         binding.toolBar.menu.findItem(R.id.menu_load_word_count)
         binding.toolBar.menu.findItem(R.id.menu_load_word_count)
-            ?.isChecked = ChangeSourceConfig.loadWordCount
+            ?.isChecked = changeSourceSettingsGateway.currentSettings.loadWordCount
     }
     }
 
 
     private fun initRecyclerView() {
     private fun initRecyclerView() {
@@ -430,8 +433,8 @@ class ChangeBookSourceDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_b
                 ReadBook.book?.migrateTo(
                 ReadBook.book?.migrateTo(
                     book,
                     book,
                     toc,
                     toc,
-                    AppConfig.replaceEnableDefault,
-                    AppConfig.chineseConverterType,
+                    otherSettingsGateway.currentSettings.replaceEnableDefault,
+                    readSettingsGateway.currentSettings.chineseConverterType,
                 )
                 )
                 callBack?.addToBookshelf(book, toc)
                 callBack?.addToBookshelf(book, toc)
                 context?.toastOnUi(getString(R.string.book_added_to_shelf))
                 context?.toastOnUi(getString(R.string.book_added_to_shelf))
@@ -508,7 +511,7 @@ class ChangeBookSourceDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_b
     private fun upGroupMenu() {
     private fun upGroupMenu() {
         binding.toolBar.menu.findItem(R.id.menu_group)?.run {
         binding.toolBar.menu.findItem(R.id.menu_group)?.run {
             subMenu?.transaction { menu ->
             subMenu?.transaction { menu ->
-                val searchScope = SearchScope(ChangeSourceConfig.searchScope)
+                val searchScope = SearchScope(changeSourceSettingsGateway.currentSettings.searchScope)
                 val selectedGroup = searchScope.displayNames.firstOrNull() ?: ""
                 val selectedGroup = searchScope.displayNames.firstOrNull() ?: ""
                 menu.removeGroup(R.id.source_group)
                 menu.removeGroup(R.id.source_group)
                 val allItem = menu.add(R.id.source_group, Menu.NONE, Menu.NONE, R.string.all_source)
                 val allItem = menu.add(R.id.source_group, Menu.NONE, Menu.NONE, R.string.all_source)
@@ -537,7 +540,7 @@ class ChangeBookSourceDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_b
      */
      */
     private fun upGroupMenuName() {
     private fun upGroupMenuName() {
         val menuGroup = binding.toolBar.menu.findItem(R.id.menu_group)
         val menuGroup = binding.toolBar.menu.findItem(R.id.menu_group)
-        val searchScope = SearchScope(ChangeSourceConfig.searchScope)
+        val searchScope = SearchScope(changeSourceSettingsGateway.currentSettings.searchScope)
         if (searchScope.isAll()) {
         if (searchScope.isAll()) {
             menuGroup?.title = getString(R.string.group)
             menuGroup?.title = getString(R.string.group)
         } else {
         } else {

+ 15 - 12
app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt

@@ -23,7 +23,8 @@ import io.legado.app.help.book.releaseHtmlData
 import io.legado.app.help.config.SourceConfig
 import io.legado.app.help.config.SourceConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.model.webBook.WebBook
-import io.legado.app.ui.config.otherConfig.OtherConfig
+import io.legado.app.domain.gateway.ChangeSourceSettingsGateway
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.utils.internString
 import io.legado.app.utils.internString
 import io.legado.app.utils.mapParallel
 import io.legado.app.utils.mapParallel
 import io.legado.app.utils.mapParallelSafe
 import io.legado.app.utils.mapParallelSafe
@@ -60,8 +61,10 @@ open class ChangeBookSourceViewModel(
     application: Application,
     application: Application,
     private val searchRepository: SearchRepository,
     private val searchRepository: SearchRepository,
     private val bookSourceRepository: BookSourceRepository,
     private val bookSourceRepository: BookSourceRepository,
+    private val downloadCacheSettingsGateway: DownloadCacheSettingsGateway,
+    private val changeSourceSettingsGateway: ChangeSourceSettingsGateway,
 ) : BaseViewModel(application) {
 ) : BaseViewModel(application) {
-    private val threadCount = OtherConfig.threadCount
+    private val threadCount = downloadCacheSettingsGateway.currentSettings.threadCount
     private var searchPool: ExecutorCoroutineDispatcher? = null
     private var searchPool: ExecutorCoroutineDispatcher? = null
     val searchStateData = MutableLiveData<Boolean>()
     val searchStateData = MutableLiveData<Boolean>()
     private val _isSearching = MutableStateFlow(false)
     private val _isSearching = MutableStateFlow(false)
@@ -140,7 +143,7 @@ open class ChangeBookSourceViewModel(
         }
         }
     }.map {
     }.map {
         kotlin.runCatching {
         kotlin.runCatching {
-            val comparator = if (ChangeSourceConfig.loadWordCount) {
+            val comparator = if (changeSourceSettingsGateway.currentSettings.loadWordCount) {
                 wordCountComparator
                 wordCountComparator
             } else {
             } else {
                 defaultComparator
                 defaultComparator
@@ -213,7 +216,7 @@ open class ChangeBookSourceViewModel(
             bookMap.clear()
             bookMap.clear()
             tocMapChapterCount = 0
             tocMapChapterCount = 0
             _changeSourceProgress.value = 0 to ""
             _changeSourceProgress.value = 0 to ""
-            bookSourceParts.addAll(io.legado.app.ui.book.search.SearchScope(ChangeSourceConfig.searchScope).getBookSourceParts())
+            bookSourceParts.addAll(io.legado.app.ui.book.search.SearchScope(changeSourceSettingsGateway.currentSettings.searchScope).getBookSourceParts())
             initSearchPool()
             initSearchPool()
             search()
             search()
         }
         }
@@ -272,10 +275,10 @@ open class ChangeBookSourceViewModel(
     }
     }
 
 
     private suspend fun search(source: BookSource) {
     private suspend fun search(source: BookSource) {
-        val checkAuthor = ChangeSourceConfig.checkAuthor
-        val loadInfo = ChangeSourceConfig.loadInfo
-        val loadToc = ChangeSourceConfig.loadToc
-        val loadWordCount = ChangeSourceConfig.loadWordCount
+        val checkAuthor = changeSourceSettingsGateway.currentSettings.checkAuthor
+        val loadInfo = changeSourceSettingsGateway.currentSettings.loadInfo
+        val loadToc = changeSourceSettingsGateway.currentSettings.loadToc
+        val loadWordCount = changeSourceSettingsGateway.currentSettings.loadWordCount
         val resultBooks = WebBook.searchBookAwait(
         val resultBooks = WebBook.searchBookAwait(
             source, name,
             source, name,
             filter = { fName, fAuthor, _ ->
             filter = { fName, fAuthor, _ ->
@@ -298,7 +301,7 @@ open class ChangeBookSourceViewModel(
         if (book.tocUrl.isEmpty()) {
         if (book.tocUrl.isEmpty()) {
             WebBook.getBookInfoAwait(source, book)
             WebBook.getBookInfoAwait(source, book)
         }
         }
-        if (ChangeSourceConfig.loadToc || ChangeSourceConfig.loadWordCount) {
+        if (changeSourceSettingsGateway.currentSettings.loadToc || changeSourceSettingsGateway.currentSettings.loadWordCount) {
             loadBookToc(source, book)
             loadBookToc(source, book)
         } else {
         } else {
             //从详情页里获取最新章节
             //从详情页里获取最新章节
@@ -318,7 +321,7 @@ open class ChangeBookSourceViewModel(
         }
         }
         bookMap[book.primaryStr()] = book
         bookMap[book.primaryStr()] = book
         book.releaseHtmlData()
         book.releaseHtmlData()
-        if (ChangeSourceConfig.loadWordCount) {
+        if (changeSourceSettingsGateway.currentSettings.loadWordCount) {
             loadBookWordCount(source, book, chapters)
             loadBookWordCount(source, book, chapters)
         } else {
         } else {
             val searchBook = book.toSearchBook()
             val searchBook = book.toSearchBook()
@@ -414,13 +417,13 @@ open class ChangeBookSourceViewModel(
 
 
     private suspend fun getDbSearchBooks(): List<SearchBook> {
     private suspend fun getDbSearchBooks(): List<SearchBook> {
         val searchScope = io.legado.app.ui.book.search.SearchScope(
         val searchScope = io.legado.app.ui.book.search.SearchScope(
-            ChangeSourceConfig.searchScope
+            changeSourceSettingsGateway.currentSettings.searchScope
         )
         )
         val group = when {
         val group = when {
             searchScope.isAll() || searchScope.isSource() -> ""
             searchScope.isAll() || searchScope.isSource() -> ""
             else -> searchScope.displayNames.firstOrNull() ?: ""
             else -> searchScope.displayNames.firstOrNull() ?: ""
         }
         }
-        val bookAuthor = if (ChangeSourceConfig.checkAuthor) author else ""
+        val bookAuthor = if (changeSourceSettingsGateway.currentSettings.checkAuthor) author else ""
         return searchRepository.findChangeSourceBooks(name, bookAuthor, screenKey, group)
         return searchRepository.findChangeSourceBooks(name, bookAuthor, screenKey, group)
     }
     }
 
 

+ 3 - 4
app/src/main/java/io/legado/app/ui/book/info/BookInfoBackdropStyle.kt

@@ -1,7 +1,5 @@
 package io.legado.app.ui.book.info
 package io.legado.app.ui.book.info
 
 
-import io.legado.app.ui.config.themeConfig.ThemeConfig
-
 internal data class BookInfoBackdropStyle(
 internal data class BookInfoBackdropStyle(
     val showCover: Boolean,
     val showCover: Boolean,
     val blurCover: Boolean,
     val blurCover: Boolean,
@@ -9,14 +7,15 @@ internal data class BookInfoBackdropStyle(
 )
 )
 
 
 internal fun resolveBookInfoBackdropStyle(backgroundMode: String): BookInfoBackdropStyle {
 internal fun resolveBookInfoBackdropStyle(backgroundMode: String): BookInfoBackdropStyle {
+    // 取值与主题设置持久化的 bookInfoBackground 一致:off/ off_for_default / on
     return when (backgroundMode) {
     return when (backgroundMode) {
-        ThemeConfig.BOOK_INFO_BACKGROUND_BLUR_OFF -> BookInfoBackdropStyle(
+        "off" -> BookInfoBackdropStyle(
             showCover = true,
             showCover = true,
             blurCover = false,
             blurCover = false,
             applySeedOverlay = true,
             applySeedOverlay = true,
         )
         )
 
 
-        ThemeConfig.BOOK_INFO_BACKGROUND_COVER_HIDDEN -> BookInfoBackdropStyle(
+        "off_for_default" -> BookInfoBackdropStyle(
             showCover = false,
             showCover = false,
             blurCover = false,
             blurCover = false,
             applySeedOverlay = false,
             applySeedOverlay = false,

+ 1 - 2
app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt

@@ -61,7 +61,6 @@ import io.legado.app.model.SourceCallBack
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.model.webBook.WebBook
 import io.legado.app.model.webBook.WebBook
-import io.legado.app.ui.config.coverConfig.CoverConfig
 import io.legado.app.ui.main.MainIntent
 import io.legado.app.ui.main.MainIntent
 import io.legado.app.ui.widget.components.image.cover.buildCoverImageRequest
 import io.legado.app.ui.widget.components.image.cover.buildCoverImageRequest
 import io.legado.app.utils.ArchiveUtils
 import io.legado.app.utils.ArchiveUtils
@@ -640,7 +639,7 @@ class BookInfoViewModel(
                 context = context,
                 context = context,
                 data = path,
                 data = path,
                 sourceOrigin = sourceOrigin,
                 sourceOrigin = sourceOrigin,
-                loadOnlyWifi = CoverConfig.loadCoverOnlyWifi,
+                loadOnlyWifi = coverSettingsGateway.currentSettings.loadOnlyOnWifi,
                 crossfade = false
                 crossfade = false
             )
             )
             val result = imageLoader.execute(request)
             val result = imageLoader.execute(request)

+ 10 - 6
app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreenViewModel.kt

@@ -31,9 +31,10 @@ import io.legado.app.help.book.removeType
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.model.CacheBook
 import io.legado.app.model.CacheBook
 import io.legado.app.service.ExportBookService
 import io.legado.app.service.ExportBookService
-import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
+import io.legado.app.domain.gateway.BookshelfSettingsGateway
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
+import org.koin.core.context.GlobalContext
 import io.legado.app.ui.config.bookshelfConfig.BookshelfManageScreenConfig
 import io.legado.app.ui.config.bookshelfConfig.BookshelfManageScreenConfig
-import io.legado.app.ui.config.otherConfig.OtherConfig
 import io.legado.app.ui.main.bookshelf.toLightBook
 import io.legado.app.ui.main.bookshelf.toLightBook
 import io.legado.app.utils.cnCompare
 import io.legado.app.utils.cnCompare
 import io.legado.app.utils.move
 import io.legado.app.utils.move
@@ -56,6 +57,8 @@ import java.util.concurrent.ConcurrentHashMap
 import kotlin.math.max
 import kotlin.math.max
 import kotlin.math.min
 import kotlin.math.min
 
 
+private val bookshelfSettingsGateway get() = GlobalContext.get().get<BookshelfSettingsGateway>()
+
 data class BookshelfManageScreenExportConfig(
 data class BookshelfManageScreenExportConfig(
     val exportUseReplace: Boolean = true,
     val exportUseReplace: Boolean = true,
     val enableCustomExport: Boolean = false,
     val enableCustomExport: Boolean = false,
@@ -77,8 +80,8 @@ data class BookshelfManageScreenUiState(
     val groupName: String? = null,
     val groupName: String? = null,
     val groupList: List<BookGroup> = emptyList(),
     val groupList: List<BookGroup> = emptyList(),
     val books: List<Book> = emptyList(),
     val books: List<Book> = emptyList(),
-    val bookSort: Int = BookshelfConfig.bookshelfSort,
-    val bookSortOrder: Int = BookshelfConfig.bookshelfSortOrder,
+    val bookSort: Int = bookshelfSettingsGateway.currentSettings.bookshelfSort,
+    val bookSortOrder: Int = bookshelfSettingsGateway.currentSettings.bookshelfSortOrder,
     val isDownloadRunning: Boolean = false,
     val isDownloadRunning: Boolean = false,
     val isChangingSource: Boolean = false,
     val isChangingSource: Boolean = false,
     val changeSourceProgress: String? = null,
     val changeSourceProgress: String? = null,
@@ -166,7 +169,8 @@ class BookshelfManageScreenViewModel(
     private val changeBookSourceUseCase: ChangeBookSourceUseCase,
     private val changeBookSourceUseCase: ChangeBookSourceUseCase,
     private val clearBookCacheUseCase: ClearBookCacheUseCase,
     private val clearBookCacheUseCase: ClearBookCacheUseCase,
     private val deleteBooksUseCase: DeleteBooksUseCase,
     private val deleteBooksUseCase: DeleteBooksUseCase,
-    private val updateBooksGroupUseCase: UpdateBooksGroupUseCase
+    private val updateBooksGroupUseCase: UpdateBooksGroupUseCase,
+    private val downloadCacheSettingsGateway: DownloadCacheSettingsGateway,
 ) : BaseViewModel(application) {
 ) : BaseViewModel(application) {
 
 
     private companion object {
     private companion object {
@@ -742,7 +746,7 @@ class BookshelfManageScreenViewModel(
             return
             return
         }
         }
         execute {
         execute {
-            val concurrency = OtherConfig.threadCount.coerceAtLeast(1)
+            val concurrency = downloadCacheSettingsGateway.currentSettings.threadCount.coerceAtLeast(1)
             _uiState.update {
             _uiState.update {
                 it.copy(
                 it.copy(
                     isChangingSource = true,
                     isChangingSource = true,

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/manga/MangaReaderViewModel.kt

@@ -447,7 +447,7 @@ class MangaReaderViewModel(
                 MangaChapterTransitionDirection.NEXT
                 MangaChapterTransitionDirection.NEXT
             }
             }
             val targetExists = targetChapterIndex in 0 until session.chapterCount
             val targetExists = targetChapterIndex in 0 until session.chapterCount
-            val targetName = session.book?.chapterTitles?.getOrNull(targetChapterIndex)
+            val targetName = session.book.chapterTitles.getOrNull(targetChapterIndex)
                 ?: appCtx.getString(
                 ?: appCtx.getString(
                     R.string.manga_reader_transition_chapter_number,
                     R.string.manga_reader_transition_chapter_number,
                     targetChapterIndex + 1,
                     targetChapterIndex + 1,

+ 12 - 10
app/src/main/java/io/legado/app/ui/book/read/ReadBookController.kt

@@ -66,7 +66,6 @@ import io.legado.app.receiver.NetworkChangedListener
 import io.legado.app.receiver.TimeBatteryReceiver
 import io.legado.app.receiver.TimeBatteryReceiver
 import io.legado.app.service.BaseReadAloudService
 import io.legado.app.service.BaseReadAloudService
 import io.legado.app.ui.book.read.page.entities.PageDirection
 import io.legado.app.ui.book.read.page.entities.PageDirection
-import io.legado.app.ui.config.readConfig.ReadConfig
 import io.legado.app.ui.login.SourceLoginJsExtensions
 import io.legado.app.ui.login.SourceLoginJsExtensions
 import io.legado.app.ui.association.OpenUrlConfirmActivity
 import io.legado.app.ui.association.OpenUrlConfirmActivity
 import io.legado.app.ui.widget.PopupAction
 import io.legado.app.ui.widget.PopupAction
@@ -116,6 +115,9 @@ class ReadBookController(
     ReadBookInputHandler,
     ReadBookInputHandler,
     ReadBook.ReaderRenderCallback {
     ReadBook.ReaderRenderCallback {
 
 
+    private val readSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.ReadSettingsGateway>()
+    private val aloudSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.ReadAloudSettingsGateway>()
+
     internal val layoutController = ReaderLayoutCoordinator(
     internal val layoutController = ReaderLayoutCoordinator(
         updateLayoutSize = { _, _ -> },
         updateLayoutSize = { _, _ -> },
         relayoutContent = ReadBook::relayoutContent,
         relayoutContent = ReadBook::relayoutContent,
@@ -467,7 +469,7 @@ class ReadBookController(
         val debounce = now - composeImageClickAt < 300L
         val debounce = now - composeImageClickAt < 300L
         composeImageClickAt = now
         composeImageClickAt = now
         composeImageDoubleClick = if (debounce) !composeImageDoubleClick else false
         composeImageDoubleClick = if (debounce) !composeImageDoubleClick else false
-        return when (ReadConfig.clickImgWay) {
+        return when (readSettingsGateway.currentSettings.clickImgWay) {
             "1" -> { viewModel.onIntent(ReadBookIntent.ShowSheet(ReadBookSheet.Photo(image.source))); true }
             "1" -> { viewModel.onIntent(ReadBookIntent.ShowSheet(ReadBookSheet.Photo(image.source))); true }
             "2" -> if (!debounce && ReadBook.book?.isOnLineTxt == true) {
             "2" -> if (!debounce && ReadBook.book?.isOnLineTxt == true) {
                 image.action?.takeIf(String::isNotBlank)?.let { clickImg(it, image.source); true }
                 image.action?.takeIf(String::isNotBlank)?.let { clickImg(it, image.source); true }
@@ -1279,7 +1281,7 @@ class ReadBookController(
                         .setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)
                         .setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)
                     
                     
                     val title = resolveInfo.loadLabel(pm).toString()
                     val title = resolveInfo.loadLabel(pm).toString()
-                    val icon = if (ReadConfig.showSelectMenuIcon) {
+                    val icon = if (readSettingsGateway.currentSettings.showSelectMenuIcon) {
                         runCatching { resolveInfo.loadIcon(pm) }.getOrNull()
                         runCatching { resolveInfo.loadIcon(pm) }.getOrNull()
                     } else null
                     } else null
 
 
@@ -1288,7 +1290,7 @@ class ReadBookController(
             }
             }
 
 
             val allItems = items + thirdPartyItems
             val allItems = items + thirdPartyItems
-            val configStr = ReadConfig.textSelectMenuConfig
+            val configStr = readSettingsGateway.currentSettings.textSelectMenuConfig
             val result = if (configStr.isEmpty()) {
             val result = if (configStr.isEmpty()) {
                 allItems
                 allItems
             } else {
             } else {
@@ -1829,17 +1831,17 @@ class ReadBookController(
     }
     }
 
 
     override fun mouseWheelPage(direction: PageDirection) {
     override fun mouseWheelPage(direction: PageDirection) {
-        if (menuLayoutIsVisible || !ReadConfig.mouseWheelPage) {
+        if (menuLayoutIsVisible || !readSettingsGateway.currentSettings.mouseWheelPage) {
             return
             return
         }
         }
         keyPageDebounce(direction, mouseWheel = true, longPress = false)
         keyPageDebounce(direction, mouseWheel = true, longPress = false)
     }
     }
 
 
     private fun volumeKeyPage(direction: PageDirection, longPress: Boolean): Boolean {
     private fun volumeKeyPage(direction: PageDirection, longPress: Boolean): Boolean {
-        if (!ReadConfig.volumeKeyPage) {
+        if (!readSettingsGateway.currentSettings.volumeKeyPage) {
             return false
             return false
         }
         }
-        if (!ReadConfig.volumeKeyPageOnPlay && BaseReadAloudService.isPlay()) {
+        if (!readSettingsGateway.currentSettings.volumeKeyPageOnPlay && BaseReadAloudService.isPlay()) {
             return false
             return false
         }
         }
         handleKeyPage(direction, longPress)
         handleKeyPage(direction, longPress)
@@ -1847,7 +1849,7 @@ class ReadBookController(
     }
     }
 
 
     override fun handleKeyPage(direction: PageDirection, longPress: Boolean) {
     override fun handleKeyPage(direction: PageDirection, longPress: Boolean) {
-        if (ReadConfig.keyPageOnLongPress || direction == PageDirection.NONE) {
+        if (readSettingsGateway.currentSettings.keyPageOnLongPress || direction == PageDirection.NONE) {
             keyPage(direction)
             keyPage(direction)
         } else {
         } else {
             keyPageDebounce(direction, longPress = longPress)
             keyPageDebounce(direction, longPress = longPress)
@@ -1946,7 +1948,7 @@ class ReadBookController(
     }
     }
 
 
     private fun upScreenTimeOut() {
     private fun upScreenTimeOut() {
-        val keepLightPrefer = ReadConfig.keepLight.toLongOrNull() ?: 0L
+        val keepLightPrefer = readSettingsGateway.currentSettings.keepLight.toLongOrNull() ?: 0L
         screenTimeOut = keepLightPrefer * 1000L
         screenTimeOut = keepLightPrefer * 1000L
         screenOffTimerStartInternal()
         screenOffTimerStartInternal()
     }
     }
@@ -2030,7 +2032,7 @@ class ReadBookController(
     }
     }
 
 
     fun setOrientation() {
     fun setOrientation() {
-        when (ReadConfig.screenOrientation) {
+        when (readSettingsGateway.currentSettings.screenOrientation) {
             "0" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
             "0" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
             "1" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
             "1" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
             "2" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
             "2" -> activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE

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

@@ -15,7 +15,7 @@ import androidx.compose.material.icons.filled.DarkMode
 import androidx.compose.material.icons.filled.History
 import androidx.compose.material.icons.filled.History
 import androidx.compose.material.icons.filled.LightMode
 import androidx.compose.material.icons.filled.LightMode
 import androidx.compose.material.icons.filled.PlayArrow
 import androidx.compose.material.icons.filled.PlayArrow
-import androidx.compose.material.icons.filled.VolumeUp
+import androidx.compose.material.icons.automirrored.filled.VolumeUp
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Alignment
@@ -79,7 +79,7 @@ private fun ReadAloudDetachedCapsule(onIntent: (ReadBookIntent) -> Unit) {
     ) {
     ) {
         MediumTonalButton(
         MediumTonalButton(
             onClick = { onIntent(ReadBookIntent.BackToSpeakingPosition) },
             onClick = { onIntent(ReadBookIntent.BackToSpeakingPosition) },
-            icon = Icons.Default.VolumeUp,
+            icon = Icons.AutoMirrored.Filled.VolumeUp,
             text = stringResource(R.string.back_to_speaking_position),
             text = stringResource(R.string.back_to_speaking_position),
             contentDescription = stringResource(R.string.back_to_speaking_position),
             contentDescription = stringResource(R.string.back_to_speaking_position),
         )
         )

+ 3 - 3
app/src/main/java/io/legado/app/ui/book/read/ReadBookMenuBarFloating.kt

@@ -29,7 +29,7 @@ import androidx.compose.material.icons.filled.Replay
 import androidx.compose.material.icons.filled.SwapHoriz
 import androidx.compose.material.icons.filled.SwapHoriz
 import androidx.compose.material.icons.filled.SwapVert
 import androidx.compose.material.icons.filled.SwapVert
 import androidx.compose.material.icons.filled.Sync
 import androidx.compose.material.icons.filled.Sync
-import androidx.compose.material.icons.filled.Toc
+import androidx.compose.material.icons.automirrored.filled.Toc
 import androidx.compose.material.icons.filled.Translate
 import androidx.compose.material.icons.filled.Translate
 import androidx.compose.material.icons.filled.Tune
 import androidx.compose.material.icons.filled.Tune
 import androidx.compose.material3.Icon
 import androidx.compose.material3.Icon
@@ -191,7 +191,7 @@ private fun OverflowDropdownMenu(
                 if (state.isLocalTxt) {
                 if (state.isLocalTxt) {
                     RoundDropdownMenuItem(
                     RoundDropdownMenuItem(
                         text = stringResource(R.string.txt_toc_rule),
                         text = stringResource(R.string.txt_toc_rule),
-                        leadingIcon = menuIcon(Icons.Default.Toc),
+                        leadingIcon = menuIcon(Icons.AutoMirrored.Filled.Toc),
                         onClick = { dismiss(); onIntent(ReadBookIntent.MenuTocRegex) },
                         onClick = { dismiss(); onIntent(ReadBookIntent.MenuTocRegex) },
                     )
                     )
                 }
                 }
@@ -241,7 +241,7 @@ private fun OverflowDropdownMenu(
         )
         )
         RoundDropdownMenuItem(
         RoundDropdownMenuItem(
             text = stringResource(R.string.re_segment),
             text = stringResource(R.string.re_segment),
-            leadingIcon = menuIcon(Icons.Default.Toc),
+            leadingIcon = menuIcon(Icons.AutoMirrored.Filled.Toc),
             isSelected = state.reSegment,
             isSelected = state.reSegment,
             onClick = { onIntent(ReadBookIntent.MenuReSegment) },
             onClick = { onIntent(ReadBookIntent.MenuReSegment) },
         )
         )

+ 3 - 3
app/src/main/java/io/legado/app/ui/book/read/ReadBookMenuBarTitle.kt

@@ -39,7 +39,7 @@ import androidx.compose.material.icons.filled.MoreVert
 import androidx.compose.material.icons.filled.Payment
 import androidx.compose.material.icons.filled.Payment
 import androidx.compose.material.icons.filled.Refresh
 import androidx.compose.material.icons.filled.Refresh
 import androidx.compose.material.icons.filled.SwapHoriz
 import androidx.compose.material.icons.filled.SwapHoriz
-import androidx.compose.material.icons.filled.Toc
+import androidx.compose.material.icons.automirrored.filled.Toc
 import androidx.compose.material.icons.filled.Translate
 import androidx.compose.material.icons.filled.Translate
 import androidx.compose.material3.Icon
 import androidx.compose.material3.Icon
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
@@ -840,7 +840,7 @@ private fun MenuTitleBarMergedGlassButton(
                 // TXT directory rule
                 // TXT directory rule
                 if (state.isLocalTxt) {
                 if (state.isLocalTxt) {
                     MergedGlassIconButton(
                     MergedGlassIconButton(
-                        icon = Icons.Default.Toc,
+                        icon = Icons.AutoMirrored.Filled.Toc,
                         tint = tint,
                         tint = tint,
                         contentDescription = stringResource(R.string.txt_toc_rule),
                         contentDescription = stringResource(R.string.txt_toc_rule),
                         onClick = { onIntent(ReadBookIntent.MenuTocRegex) },
                         onClick = { onIntent(ReadBookIntent.MenuTocRegex) },
@@ -1005,7 +1005,7 @@ private fun TxtTocRuleActionButton(
 ) {
 ) {
     MenuTitleGlassButton(
     MenuTitleGlassButton(
         onClick = { onIntent(ReadBookIntent.MenuTocRegex) },
         onClick = { onIntent(ReadBookIntent.MenuTocRegex) },
-        icon = Icons.Default.Toc,
+        icon = Icons.AutoMirrored.Filled.Toc,
         contentDescription = stringResource(R.string.txt_toc_rule),
         contentDescription = stringResource(R.string.txt_toc_rule),
         state = state,
         state = state,
         colors = colors,
         colors = colors,

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

@@ -6,7 +6,7 @@ import androidx.compose.material3.DropdownMenuItem
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExposedDropdownMenuBox
 import androidx.compose.material3.ExposedDropdownMenuBox
 import androidx.compose.material3.ExposedDropdownMenuDefaults
 import androidx.compose.material3.ExposedDropdownMenuDefaults
-import androidx.compose.material3.MenuAnchorType
+import androidx.compose.material3.ExposedDropdownMenuAnchorType
 import androidx.compose.material3.OutlinedTextField
 import androidx.compose.material3.OutlinedTextField
 import androidx.compose.material3.Text
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
 import androidx.compose.material3.TextButton
@@ -48,7 +48,7 @@ fun CharsetConfigSheet(
                             value = charset,
                             value = charset,
                             onValueChange = { charset = it },
                             onValueChange = { charset = it },
                             modifier = Modifier
                             modifier = Modifier
-                                .menuAnchor(MenuAnchorType.PrimaryEditable)
+                                .menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable)
                                 .fillMaxWidth(),
                                 .fillMaxWidth(),
                             readOnly = false,
                             readOnly = false,
                             label = { Text(stringResource(R.string.set_charset)) },
                             label = { Text(stringResource(R.string.set_charset)) },

+ 3 - 3
app/src/main/java/io/legado/app/ui/book/read/sheet/ReaderMoreActionsSheet.kt

@@ -32,7 +32,7 @@ import androidx.compose.material.icons.filled.Settings
 import androidx.compose.material.icons.filled.SwapHoriz
 import androidx.compose.material.icons.filled.SwapHoriz
 import androidx.compose.material.icons.filled.SwapVert
 import androidx.compose.material.icons.filled.SwapVert
 import androidx.compose.material.icons.filled.Sync
 import androidx.compose.material.icons.filled.Sync
-import androidx.compose.material.icons.filled.Toc
+import androidx.compose.material.icons.automirrored.filled.Toc
 import androidx.compose.material.icons.filled.Translate
 import androidx.compose.material.icons.filled.Translate
 import androidx.compose.material.icons.filled.Tune
 import androidx.compose.material.icons.filled.Tune
 import androidx.compose.material3.HorizontalDivider
 import androidx.compose.material3.HorizontalDivider
@@ -301,7 +301,7 @@ private fun moreActionSpecs(
         applicable = !state.isLocalBook,
         applicable = !state.isLocalBook,
         onClick = { onIntent(ReadBookIntent.ShowSheet(ReadBookSheet.Download)) }),
         onClick = { onIntent(ReadBookIntent.ShowSheet(ReadBookSheet.Download)) }),
     MoreActionSpec(
     MoreActionSpec(
-        "toc_rule", stringResource(R.string.txt_toc_rule), Icons.Default.Toc,
+        "toc_rule", stringResource(R.string.txt_toc_rule), Icons.AutoMirrored.Filled.Toc,
         applicable = state.isLocalTxt, onClick = { dispatch(ReadBookIntent.MenuTocRegex) }),
         applicable = state.isLocalTxt, onClick = { dispatch(ReadBookIntent.MenuTocRegex) }),
     MoreActionSpec(
     MoreActionSpec(
         "charset", stringResource(R.string.set_charset), Icons.Default.Translate,
         "charset", stringResource(R.string.set_charset), Icons.Default.Translate,
@@ -323,7 +323,7 @@ private fun moreActionSpecs(
         "reverse_content", stringResource(R.string.reverse_content), Icons.Default.SwapVert,
         "reverse_content", stringResource(R.string.reverse_content), Icons.Default.SwapVert,
         onClick = { dispatch(ReadBookIntent.MenuReverseContent) }),
         onClick = { dispatch(ReadBookIntent.MenuReverseContent) }),
     MoreActionSpec(
     MoreActionSpec(
-        "re_segment", stringResource(R.string.re_segment), Icons.Default.Toc,
+        "re_segment", stringResource(R.string.re_segment), Icons.AutoMirrored.Filled.Toc,
         selected = state.reSegment, onClick = { onIntent(ReadBookIntent.MenuReSegment) }),
         selected = state.reSegment, onClick = { onIntent(ReadBookIntent.MenuReSegment) }),
     MoreActionSpec(
     MoreActionSpec(
         "del_ruby", stringResource(R.string.del_ruby_tag), Icons.Default.CleanHands,
         "del_ruby", stringResource(R.string.del_ruby_tag), Icons.Default.CleanHands,

+ 14 - 14
app/src/main/java/io/legado/app/ui/book/read/sheet/TypographyTabs.kt

@@ -13,16 +13,16 @@ import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.AlignHorizontalCenter
 import androidx.compose.material.icons.filled.AlignHorizontalCenter
-import androidx.compose.material.icons.filled.AlignHorizontalLeft
-import androidx.compose.material.icons.filled.AlignHorizontalRight
+import androidx.compose.material.icons.automirrored.filled.AlignHorizontalLeft
+import androidx.compose.material.icons.automirrored.filled.AlignHorizontalRight
 import androidx.compose.material.icons.filled.AspectRatio
 import androidx.compose.material.icons.filled.AspectRatio
-import androidx.compose.material.icons.filled.CallSplit
+import androidx.compose.material.icons.automirrored.filled.CallSplit
 import androidx.compose.material.icons.filled.CleanHands
 import androidx.compose.material.icons.filled.CleanHands
 import androidx.compose.material.icons.filled.Code
 import androidx.compose.material.icons.filled.Code
 import androidx.compose.material.icons.filled.CopyAll
 import androidx.compose.material.icons.filled.CopyAll
 import androidx.compose.material.icons.filled.FormatAlignJustify
 import androidx.compose.material.icons.filled.FormatAlignJustify
 import androidx.compose.material.icons.filled.FormatColorText
 import androidx.compose.material.icons.filled.FormatColorText
-import androidx.compose.material.icons.filled.FormatIndentIncrease
+import androidx.compose.material.icons.automirrored.filled.FormatIndentIncrease
 import androidx.compose.material.icons.filled.FormatItalic
 import androidx.compose.material.icons.filled.FormatItalic
 import androidx.compose.material.icons.filled.FormatLineSpacing
 import androidx.compose.material.icons.filled.FormatLineSpacing
 import androidx.compose.material.icons.filled.FormatSize
 import androidx.compose.material.icons.filled.FormatSize
@@ -32,7 +32,7 @@ import androidx.compose.material.icons.filled.Layers
 import androidx.compose.material.icons.filled.LineWeight
 import androidx.compose.material.icons.filled.LineWeight
 import androidx.compose.material.icons.filled.Minimize
 import androidx.compose.material.icons.filled.Minimize
 import androidx.compose.material.icons.filled.Palette
 import androidx.compose.material.icons.filled.Palette
-import androidx.compose.material.icons.filled.Segment
+import androidx.compose.material.icons.automirrored.filled.Segment
 import androidx.compose.material.icons.filled.SpaceBar
 import androidx.compose.material.icons.filled.SpaceBar
 import androidx.compose.material.icons.filled.TextFields
 import androidx.compose.material.icons.filled.TextFields
 import androidx.compose.material.icons.filled.Title
 import androidx.compose.material.icons.filled.Title
@@ -238,14 +238,14 @@ private fun PaddingSliders(
         title = stringResource(R.string.padding_left),
         title = stringResource(R.string.padding_left),
         value = left,
         value = left,
         valueRange = 0f..maxValue,
         valueRange = 0f..maxValue,
-        imageVector = Icons.Default.AlignHorizontalLeft,
+        imageVector = Icons.AutoMirrored.Filled.AlignHorizontalLeft,
         onValueChange = onLeftChange,
         onValueChange = onLeftChange,
     )
     )
     TinySliderSettingItem(
     TinySliderSettingItem(
         title = stringResource(R.string.padding_right),
         title = stringResource(R.string.padding_right),
         value = right,
         value = right,
         valueRange = 0f..maxValue,
         valueRange = 0f..maxValue,
-        imageVector = Icons.Default.AlignHorizontalRight,
+        imageVector = Icons.AutoMirrored.Filled.AlignHorizontalRight,
         onValueChange = onRightChange,
         onValueChange = onRightChange,
     )
     )
 }
 }
@@ -356,7 +356,7 @@ internal fun TypographyBodyTab(
             value = indentCount.toFloat(),
             value = indentCount.toFloat(),
             valueRange = 0f..4f,
             valueRange = 0f..4f,
             steps = 3,
             steps = 3,
-            imageVector = Icons.Default.FormatIndentIncrease,
+            imageVector = Icons.AutoMirrored.Filled.FormatIndentIncrease,
             onValueChange = { value ->
             onValueChange = { value ->
                 indentCount = value.toInt()
                 indentCount = value.toInt()
                 onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ParagraphIndent(" ".repeat(indentCount))))
                 onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ParagraphIndent(" ".repeat(indentCount))))
@@ -391,7 +391,7 @@ internal fun TypographyBodyTab(
             value = paragraphSpacing,
             value = paragraphSpacing,
             valueRange = 0f..20f,
             valueRange = 0f..20f,
             steps = 19,
             steps = 19,
-            imageVector = Icons.Default.Segment,
+            imageVector = Icons.AutoMirrored.Filled.Segment,
             valueFormat = { (it / 10f).toString() },
             valueFormat = { (it / 10f).toString() },
             onValueChange = { value ->
             onValueChange = { value ->
                 paragraphSpacing = value
                 paragraphSpacing = value
@@ -520,7 +520,7 @@ internal fun TypographyTitleTab(
                 stringResource(R.string.split_title_by_regex),
                 stringResource(R.string.split_title_by_regex),
             ),
             ),
             entryValues = arrayOf("0", "1", "2", "3"),
             entryValues = arrayOf("0", "1", "2", "3"),
-            imageVector = Icons.Default.CallSplit,
+            imageVector = Icons.AutoMirrored.Filled.CallSplit,
             onValueChange = {
             onValueChange = {
                 titleSegType = it.toInt()
                 titleSegType = it.toInt()
                 onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.TitleSegType(titleSegType)))
                 onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.TitleSegType(titleSegType)))
@@ -692,7 +692,7 @@ internal fun TypographyHeaderTab(
             value = headerLeft,
             value = headerLeft,
             tipNames = tipNames,
             tipNames = tipNames,
             tipValues = tipValues,
             tipValues = tipValues,
-            imageVector = Icons.Default.AlignHorizontalLeft,
+            imageVector = Icons.AutoMirrored.Filled.AlignHorizontalLeft,
             onValueChange = { onTipChange(CustomTipTarget.HEADER_LEFT, it) },
             onValueChange = { onTipChange(CustomTipTarget.HEADER_LEFT, it) },
         )
         )
         TipPositionDropdown(
         TipPositionDropdown(
@@ -708,7 +708,7 @@ internal fun TypographyHeaderTab(
             value = headerRight,
             value = headerRight,
             tipNames = tipNames,
             tipNames = tipNames,
             tipValues = tipValues,
             tipValues = tipValues,
-            imageVector = Icons.Default.AlignHorizontalRight,
+            imageVector = Icons.AutoMirrored.Filled.AlignHorizontalRight,
             onValueChange = { onTipChange(CustomTipTarget.HEADER_RIGHT, it) },
             onValueChange = { onTipChange(CustomTipTarget.HEADER_RIGHT, it) },
         )
         )
 
 
@@ -808,7 +808,7 @@ internal fun TypographyFooterTab(
             value = footerLeft,
             value = footerLeft,
             tipNames = tipNames,
             tipNames = tipNames,
             tipValues = tipValues,
             tipValues = tipValues,
-            imageVector = Icons.Default.AlignHorizontalLeft,
+            imageVector = Icons.AutoMirrored.Filled.AlignHorizontalLeft,
             onValueChange = { onTipChange(CustomTipTarget.FOOTER_LEFT, it) },
             onValueChange = { onTipChange(CustomTipTarget.FOOTER_LEFT, it) },
         )
         )
         TipPositionDropdown(
         TipPositionDropdown(
@@ -824,7 +824,7 @@ internal fun TypographyFooterTab(
             value = footerRight,
             value = footerRight,
             tipNames = tipNames,
             tipNames = tipNames,
             tipValues = tipValues,
             tipValues = tipValues,
-            imageVector = Icons.Default.AlignHorizontalRight,
+            imageVector = Icons.AutoMirrored.Filled.AlignHorizontalRight,
             onValueChange = { onTipChange(CustomTipTarget.FOOTER_RIGHT, it) },
             onValueChange = { onTipChange(CustomTipTarget.FOOTER_RIGHT, it) },
         )
         )
 
 

+ 2 - 3
app/src/main/java/io/legado/app/ui/book/readaloud/player/ReadAloudPlayerCoordinator.kt

@@ -14,7 +14,6 @@ import io.legado.app.model.ReadAloudSessionStore
 import io.legado.app.model.ReadBook
 import io.legado.app.model.ReadBook
 import io.legado.app.service.BaseReadAloudService
 import io.legado.app.service.BaseReadAloudService
 import io.legado.app.ui.book.read.ReadConfigUpdateBus
 import io.legado.app.ui.book.read.ReadConfigUpdateBus
-import io.legado.app.ui.config.readConfig.ReadConfig
 import kotlinx.collections.immutable.ImmutableList
 import kotlinx.collections.immutable.ImmutableList
 import kotlinx.collections.immutable.persistentListOf
 import kotlinx.collections.immutable.persistentListOf
 import kotlinx.collections.immutable.toImmutableList
 import kotlinx.collections.immutable.toImmutableList
@@ -92,7 +91,7 @@ class ReadAloudPlayerCoordinator(
             engineName = playback.engineName,
             engineName = playback.engineName,
             speakerName = playback.characterName.ifBlank { playback.roleType.storageValue },
             speakerName = playback.characterName.ifBlank { playback.roleType.storageValue },
             isPaused = session.status != ReadAloudSessionStatus.Playing,
             isPaused = session.status != ReadAloudSessionStatus.Playing,
-            speed = ReadConfig.ttsSpeechRate,
+            speed = readAloudSettingsGateway.currentSettings.ttsSpeechRate,
             timerMinutes = session.timerMinutes,
             timerMinutes = session.timerMinutes,
             finishCurrentChapterAfterTimer = settings.finishCurrentChapterAfterTimer,
             finishCurrentChapterAfterTimer = settings.finishCurrentChapterAfterTimer,
         )
         )
@@ -119,7 +118,7 @@ class ReadAloudPlayerCoordinator(
             engineName = playback.engineName,
             engineName = playback.engineName,
             speakerName = playback.characterName.ifBlank { playback.roleType.storageValue },
             speakerName = playback.characterName.ifBlank { playback.roleType.storageValue },
             isPaused = session.status != ReadAloudSessionStatus.Playing,
             isPaused = session.status != ReadAloudSessionStatus.Playing,
-            speed = ReadConfig.ttsSpeechRate,
+            speed = readAloudSettingsGateway.currentSettings.ttsSpeechRate,
             timerMinutes = session.timerMinutes,
             timerMinutes = session.timerMinutes,
             finishCurrentChapterAfterTimer =
             finishCurrentChapterAfterTimer =
                 readAloudSettingsGateway.currentSettings.finishCurrentChapterAfterTimer,
                 readAloudSettingsGateway.currentSettings.finishCurrentChapterAfterTimer,

+ 3 - 2
app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt

@@ -2,6 +2,7 @@ package io.legado.app.ui.book.search
 
 
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import androidx.lifecycle.viewModelScope
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.data.entities.BookSourcePart
 import io.legado.app.data.entities.BookSourcePart
 import io.legado.app.data.entities.SearchBook
 import io.legado.app.data.entities.SearchBook
 import io.legado.app.data.local.preferences.LocalPreferencesKeys
 import io.legado.app.data.local.preferences.LocalPreferencesKeys
@@ -20,7 +21,6 @@ import io.legado.app.domain.usecase.ExploreBooksUseCase
 import io.legado.app.domain.usecase.ResolveBookShelfStateUseCase
 import io.legado.app.domain.usecase.ResolveBookShelfStateUseCase
 import io.legado.app.domain.usecase.SearchBooksUseCase
 import io.legado.app.domain.usecase.SearchBooksUseCase
 import io.legado.app.domain.usecase.SearchRunEvent
 import io.legado.app.domain.usecase.SearchRunEvent
-import io.legado.app.ui.config.otherConfig.OtherConfig
 import kotlinx.collections.immutable.persistentListOf
 import kotlinx.collections.immutable.persistentListOf
 import kotlinx.collections.immutable.persistentSetOf
 import kotlinx.collections.immutable.persistentSetOf
 import kotlinx.collections.immutable.toImmutableList
 import kotlinx.collections.immutable.toImmutableList
@@ -55,6 +55,7 @@ class SearchViewModel(
     private val addToBookshelfUseCase: AddToBookshelfUseCase,
     private val addToBookshelfUseCase: AddToBookshelfUseCase,
     private val checkBookContentQualityUseCase: CheckBookContentQualityUseCase,
     private val checkBookContentQualityUseCase: CheckBookContentQualityUseCase,
     private val localPreferencesRepository: SettingsRepository,
     private val localPreferencesRepository: SettingsRepository,
+    private val downloadCacheSettingsGateway: DownloadCacheSettingsGateway,
 ) : ViewModel() {
 ) : ViewModel() {
 
 
     private val searchLayoutMode = localPreferencesRepository
     private val searchLayoutMode = localPreferencesRepository
@@ -588,7 +589,7 @@ class SearchViewModel(
                             page = page,
                             page = page,
                             scope = BookSearchScope(searchScope.toString()),
                             scope = BookSearchScope(searchScope.toString()),
                             matchMode = _uiState.value.matchMode,
                             matchMode = _uiState.value.matchMode,
-                            concurrency = OtherConfig.threadCount,
+                            concurrency = downloadCacheSettingsGateway.currentSettings.threadCount,
                             types = _uiState.value.selectedSourceTypes.takeIf { it.isNotEmpty() },
                             types = _uiState.value.selectedSourceTypes.takeIf { it.isNotEmpty() },
                         ),
                         ),
                         searchControl
                         searchControl

+ 3 - 3
app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentAdapter.kt

@@ -6,7 +6,6 @@ import androidx.core.view.isVisible
 import io.legado.app.base.adapter.ItemViewHolder
 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.ItemSearchListBinding
 import io.legado.app.databinding.ItemSearchListBinding
-import io.legado.app.help.config.AppConfig
 import io.legado.app.utils.themeColor
 import io.legado.app.utils.themeColor
 
 
 
 
@@ -16,6 +15,7 @@ class SearchContentAdapter(context: Context, val callback: Callback) :
     private val textColorInt = context.themeColor(com.google.android.material.R.attr.colorOnSurface)
     private val textColorInt = context.themeColor(com.google.android.material.R.attr.colorOnSurface)
     private val accentColorInt = context.themeColor(androidx.appcompat.R.attr.colorPrimary)
     private val accentColorInt = context.themeColor(androidx.appcompat.R.attr.colorPrimary)
     private val bgColorInt = context.themeColor(com.google.android.material.R.attr.colorSecondaryContainer)
     private val bgColorInt = context.themeColor(com.google.android.material.R.attr.colorSecondaryContainer)
+    private val isEInkMode get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.ThemeSettingsGateway>().currentSettings.appTheme == "4"
 
 
     override fun getViewBinding(parent: ViewGroup): ItemSearchListBinding {
     override fun getViewBinding(parent: ViewGroup): ItemSearchListBinding {
         return ItemSearchListBinding.inflate(inflater, parent, false)
         return ItemSearchListBinding.inflate(inflater, parent, false)
@@ -30,12 +30,12 @@ class SearchContentAdapter(context: Context, val callback: Callback) :
         binding.run {
         binding.run {
             val isDur = callback.durChapterIndex() == item.chapterIndex
             val isDur = callback.durChapterIndex() == item.chapterIndex
             if (payloads.isEmpty()) {
             if (payloads.isEmpty()) {
-                tvTitle.text = item.getTitleSpannable(textColorInt, AppConfig.isEInkMode)
+                tvTitle.text = item.getTitleSpannable(textColorInt, isEInkMode)
                 tvContent.text = item.getContentSpannable(
                 tvContent.text = item.getContentSpannable(
                     textColorInt,
                     textColorInt,
                     accentColorInt,
                     accentColorInt,
                     bgColorInt,
                     bgColorInt,
-                    AppConfig.isEInkMode,
+                    isEInkMode,
                 )
                 )
 
 
                 if (item.progressPercent > 0) {
                 if (item.progressPercent > 0) {

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

@@ -372,13 +372,6 @@ fun SearchHistoryList(
                         modifier = Modifier
                         modifier = Modifier
                             .clickable { onHistoryClick(item) }
                             .clickable { onHistoryClick(item) }
                             .animateItem(),
                             .animateItem(),
-                        headlineContent = {
-                            AppText(
-                                text = item.query,
-                                maxLines = 1,
-                                overflow = TextOverflow.Ellipsis
-                            )
-                        },
                         leadingContent = {
                         leadingContent = {
                             Icon(Icons.Default.History, contentDescription = null)
                             Icon(Icons.Default.History, contentDescription = null)
                         },
                         },
@@ -393,7 +386,13 @@ fun SearchHistoryList(
                             containerColor = LegadoTheme.colorScheme.surface,
                             containerColor = LegadoTheme.colorScheme.surface,
                             contentColor = LegadoTheme.colorScheme.onSurface
                             contentColor = LegadoTheme.colorScheme.onSurface
                         )
                         )
-                    )
+                    ) {
+                        AppText(
+                            text = item.query,
+                            maxLines = 1,
+                            overflow = TextOverflow.Ellipsis
+                        )
+                    }
                 }
                 }
                 item {
                 item {
                     Box(
                     Box(

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceScreen.kt

@@ -513,7 +513,7 @@ fun BookSourceScreen(
                 showExportSheet = true
                 showExportSheet = true
             },
             },
         ),
         ),
-        onDeleteSelected = { deleteIds = it as Set<String> },
+        onDeleteSelected = { deleteIds = @Suppress("UNCHECKED_CAST") (it as Set<String>) },
         dropDownMenuContent = { dismiss ->
         dropDownMenuContent = { dismiss ->
             RoundDropdownMenuItem(
             RoundDropdownMenuItem(
                 text = stringResource(R.string.group_manage),
                 text = stringResource(R.string.group_manage),

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

@@ -357,7 +357,7 @@ fun TocScreen(
 
 
     LaunchedEffect(state.items, book) {
     LaunchedEffect(state.items, book) {
         if (!hasAutoScrolled && state.items.isNotEmpty() && book != null) {
         if (!hasAutoScrolled && state.items.isNotEmpty() && book != null) {
-            val durIndex = book?.durChapterIndex ?: -1
+            val durIndex = book.durChapterIndex
             val targetIndex = state.items.indexOfFirst { it.id == durIndex || it.isDur }
             val targetIndex = state.items.indexOfFirst { it.id == durIndex || it.isDur }
             if (targetIndex != -1) {
             if (targetIndex != -1) {
                 delay(100) 
                 delay(100) 
@@ -482,7 +482,7 @@ fun TocScreen(
                                 RoundDropdownMenuItem(
                                 RoundDropdownMenuItem(
                                     text = stringResource(R.string.local_book_toc_rule),
                                     text = stringResource(R.string.local_book_toc_rule),
                                     onClick = {
                                     onClick = {
-                                        onEditLocalTocRule(book?.tocUrl)
+                                        onEditLocalTocRule(book.tocUrl)
                                         dismiss()
                                         dismiss()
                                     }
                                     }
                                 )
                                 )
@@ -995,7 +995,7 @@ fun BookmarkListContent(
 
 
     LaunchedEffect(bookmarks, book?.durChapterIndex) {
     LaunchedEffect(bookmarks, book?.durChapterIndex) {
         if (bookmarks.isNotEmpty() && book != null) {
         if (bookmarks.isNotEmpty() && book != null) {
-            val durIndex = book!!.durChapterIndex
+            val durIndex = book.durChapterIndex
             var scrollPos = 0
             var scrollPos = 0
             for ((index, bookmark) in bookmarks.withIndex()) {
             for ((index, bookmark) in bookmarks.withIndex()) {
                 if (bookmark.chapterIndex >= durIndex) break
                 if (bookmark.chapterIndex >= durIndex) break

+ 1 - 0
app/src/main/java/io/legado/app/ui/book/toc/TocViewModel.kt

@@ -519,6 +519,7 @@ class TocViewModel(
     val useReplace get() = tocPreferences.value.useReplace
     val useReplace get() = tocPreferences.value.useReplace
     val showWordCount get() = tocPreferences.value.showWordCount
     val showWordCount get() = tocPreferences.value.showWordCount
 
 
+    @Suppress("OVERRIDE_DEPRECATION")
     override fun filterData(data: List<TocDomainItem>, key: String): List<TocDomainItem> {
     override fun filterData(data: List<TocDomainItem>, key: String): List<TocDomainItem> {
         val collapsed = _collapsedVolumes.value
         val collapsed = _collapsedVolumes.value
         val isSearch = key.isNotBlank()
         val isSearch = key.isNotBlank()

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleViewModel.kt

@@ -85,7 +85,7 @@ class TxtTocRuleViewModel(
     override fun TxtTocRule.toUiItem() =
     override fun TxtTocRule.toUiItem() =
         TxtTocRuleItemUi(id, name, enable, this, example = example ?: "")
         TxtTocRuleItemUi(id, name, enable, this, example = example ?: "")
 
 
-    @Suppress("DEPRECATION")
+    @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
     override fun filterData(data: List<TxtTocRule>, key: String): List<TxtTocRule> {
     override fun filterData(data: List<TxtTocRule>, key: String): List<TxtTocRule> {
         val filtered = if (key.isEmpty()) data
         val filtered = if (key.isEmpty()) data
         else data.filter { it.name.contains(key, ignoreCase = true) }
         else data.filter { it.name.contains(key, ignoreCase = true) }

+ 6 - 2
app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfManageScreenConfig.kt

@@ -1,16 +1,20 @@
 package io.legado.app.ui.config.bookshelfConfig
 package io.legado.app.ui.config.bookshelfConfig
 
 
 import io.legado.app.data.dao.BookGroupDao
 import io.legado.app.data.dao.BookGroupDao
+import io.legado.app.domain.gateway.BookshelfSettingsGateway
+import org.koin.core.context.GlobalContext
 
 
 class BookshelfManageScreenConfig(
 class BookshelfManageScreenConfig(
     private val bookGroupDao: BookGroupDao
     private val bookGroupDao: BookGroupDao
 ) {
 ) {
+    private val bookshelfSettingsGateway
+        get() = GlobalContext.get().get<BookshelfSettingsGateway>()
 
 
     fun getBookSortByGroupId(groupId: Long): Int {
     fun getBookSortByGroupId(groupId: Long): Int {
-        val defaultSort = BookshelfConfig.bookshelfSort
+        val defaultSort = bookshelfSettingsGateway.currentSettings.bookshelfSort
         return bookGroupDao.getByID(groupId)?.getRealBookSort(defaultSort) ?: defaultSort
         return bookGroupDao.getByID(groupId)?.getRealBookSort(defaultSort) ?: defaultSort
     }
     }
 
 
     val bookshelfSortOrder: Int
     val bookshelfSortOrder: Int
-        get() = BookshelfConfig.bookshelfSortOrder
+        get() = bookshelfSettingsGateway.currentSettings.bookshelfSortOrder
 }
 }

+ 13 - 8
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt

@@ -24,9 +24,9 @@ import io.legado.app.domain.usecase.RefreshTocUseCase
 import io.legado.app.domain.usecase.UpdateBooksGroupUseCase
 import io.legado.app.domain.usecase.UpdateBooksGroupUseCase
 import io.legado.app.domain.gateway.BookshelfSettingsGateway
 import io.legado.app.domain.gateway.BookshelfSettingsGateway
 import io.legado.app.domain.gateway.AppShellSettingsGateway
 import io.legado.app.domain.gateway.AppShellSettingsGateway
+import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.domain.gateway.ThemeSettingsGateway
 import io.legado.app.domain.gateway.ThemeSettingsGateway
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.model.CacheBook
 import io.legado.app.model.CacheBook
 import io.legado.app.model.SourceCallBack
 import io.legado.app.model.SourceCallBack
@@ -92,6 +92,7 @@ class BookshelfViewModel(
     private val bookshelfSettingsGateway: BookshelfSettingsGateway,
     private val bookshelfSettingsGateway: BookshelfSettingsGateway,
     private val appShellSettingsGateway: AppShellSettingsGateway,
     private val appShellSettingsGateway: AppShellSettingsGateway,
     private val themeSettingsGateway: ThemeSettingsGateway,
     private val themeSettingsGateway: ThemeSettingsGateway,
+    private val downloadCacheSettingsGateway: DownloadCacheSettingsGateway,
 ) : BaseViewModel(application) {
 ) : BaseViewModel(application) {
     private var addBookJob: Coroutine<*>? = null
     private var addBookJob: Coroutine<*>? = null
 
 
@@ -144,7 +145,8 @@ class BookshelfViewModel(
     val scrollTrigger = _scrollTrigger.asSharedFlow()
     val scrollTrigger = _scrollTrigger.asSharedFlow()
 
 
     private val updateConcurrency: Int
     private val updateConcurrency: Int
-        get() = AppConfig.threadCount.coerceIn(1, AppConst.MAX_THREAD)
+        get() = downloadCacheSettingsGateway.currentSettings.threadCount
+            .coerceIn(1, AppConst.MAX_THREAD)
 
 
     @OptIn(ExperimentalCoroutinesApi::class)
     @OptIn(ExperimentalCoroutinesApi::class)
     private val updateDispatcher: CoroutineDispatcher
     private val updateDispatcher: CoroutineDispatcher
@@ -313,7 +315,7 @@ class BookshelfViewModel(
                 }
                 }
                 combine(flows) { results ->
                 combine(flows) { results ->
                     results.fold(persistentMapOf<Long, ImmutableList<BookUiItem>>()) { acc, (id, list) ->
                     results.fold(persistentMapOf<Long, ImmutableList<BookUiItem>>()) { acc, (id, list) ->
-                        acc.put(id, list)
+                        acc.putting(id, list)
                     }
                     }
                 }
                 }
             }
             }
@@ -398,8 +400,8 @@ class BookshelfViewModel(
                 var previews = persistentMapOf<Long, ImmutableList<BookUiItem>>()
                 var previews = persistentMapOf<Long, ImmutableList<BookUiItem>>()
                 var counts = persistentMapOf<Long, Int>()
                 var counts = persistentMapOf<Long, Int>()
                 results.forEach { (groupId, count, preview) ->
                 results.forEach { (groupId, count, preview) ->
-                    counts = counts.put(groupId, count)
-                    previews = previews.put(groupId, preview.toImmutableList())
+                    counts = counts.putting(groupId, count)
+                    previews = previews.putting(groupId, preview.toImmutableList())
                 }
                 }
                 GroupPreviewState(previews, counts, allBookCount)
                 GroupPreviewState(previews, counts, allBookCount)
             }
             }
@@ -1080,9 +1082,12 @@ class BookshelfViewModel(
     }
     }
 
 
     private fun addDownload(source: BookSource, book: Book) {
     private fun addDownload(source: BookSource, book: Book) {
-        if (AppConfig.preDownloadNum == 0) return
+        if (downloadCacheSettingsGateway.currentSettings.preDownloadNum == 0) return
         val endIndex =
         val endIndex =
-            min(book.totalChapterNum - 1, book.durChapterIndex + AppConfig.preDownloadNum)
+            min(
+                book.totalChapterNum - 1,
+                book.durChapterIndex + downloadCacheSettingsGateway.currentSettings.preDownloadNum
+            )
         val cacheBook = CacheBook.getOrCreate(source, book)
         val cacheBook = CacheBook.getOrCreate(source, book)
         cacheBook.addDownload(book.durChapterIndex, endIndex)
         cacheBook.addDownload(book.durChapterIndex, endIndex)
     }
     }
@@ -1096,7 +1101,7 @@ class BookshelfViewModel(
             )
             )
         }
         }
         eventListenerSource.clear()
         eventListenerSource.clear()
-        if (AppConfig.preDownloadNum == 0) return
+        if (downloadCacheSettingsGateway.currentSettings.preDownloadNum == 0) return
         cacheBookJob?.cancel()
         cacheBookJob?.cancel()
         cacheBookJob = viewModelScope.launch(updateDispatcher) {
         cacheBookJob = viewModelScope.launch(updateDispatcher) {
             launch {
             launch {

+ 8 - 9
app/src/main/java/io/legado/app/ui/main/explore/ExploreScreen.kt

@@ -398,13 +398,6 @@ fun ExploreSourceHeader(
             colors = ListItemDefaults.colors(
             colors = ListItemDefaults.colors(
                 containerColor = Color.Transparent
                 containerColor = Color.Transparent
             ),
             ),
-            headlineContent = {
-                AppText(
-                    text = item.bookSourceName,
-                    style = LegadoTheme.typography.titleMedium,
-                    color = contentColor
-                )
-            },
             trailingContent = {
             trailingContent = {
                 AnimatedContent(
                 AnimatedContent(
                     targetState = loadingKinds,
                     targetState = loadingKinds,
@@ -466,7 +459,13 @@ fun ExploreSourceHeader(
                         onClick = { onDelete(); showMenu = false }
                         onClick = { onDelete(); showMenu = false }
                     )
                     )
                 }
                 }
-            }
-        )
+            },
+        ) {
+            AppText(
+                text = item.bookSourceName,
+                style = LegadoTheme.typography.titleMedium,
+                color = contentColor
+            )
+        }
     }
     }
 }
 }

+ 1 - 1
app/src/main/java/io/legado/app/ui/main/homepage/modules/ButtonGroupModule.kt

@@ -115,7 +115,7 @@ fun ButtonGroupModule(
                                 ) {
                                 ) {
                                     if (hasIcon) {
                                     if (hasIcon) {
                                         SourceIcon(
                                         SourceIcon(
-                                            path = buttonIcon!!,
+                                            path = buttonIcon,
                                             modifier = Modifier.size(20.dp),
                                             modifier = Modifier.size(20.dp),
                                             placeholderIcon = {
                                             placeholderIcon = {
 
 

+ 7 - 3
app/src/main/java/io/legado/app/ui/replace/ReplaceRuleViewModel.kt

@@ -12,10 +12,10 @@ import io.legado.app.data.repository.ReadSettingsRepository
 import io.legado.app.data.repository.ReplaceRuleRepository
 import io.legado.app.data.repository.ReplaceRuleRepository
 import io.legado.app.data.repository.UploadRepository
 import io.legado.app.data.repository.UploadRepository
 import io.legado.app.domain.gateway.BookContentProcessGateway
 import io.legado.app.domain.gateway.BookContentProcessGateway
+import io.legado.app.domain.gateway.OtherSettingsGateway
 import io.legado.app.domain.model.TextProcessAction
 import io.legado.app.domain.model.TextProcessAction
 import io.legado.app.domain.model.TextProcessAnchor
 import io.legado.app.domain.model.TextProcessAnchor
 import io.legado.app.help.ReplaceAnalyzer
 import io.legado.app.help.ReplaceAnalyzer
-import io.legado.app.help.config.AppConfig
 import io.legado.app.model.ReadBook
 import io.legado.app.model.ReadBook
 import io.legado.app.ui.book.read.ContentProcessConfigUiState
 import io.legado.app.ui.book.read.ContentProcessConfigUiState
 import io.legado.app.ui.book.read.ContentProcessItemUi
 import io.legado.app.ui.book.read.ContentProcessItemUi
@@ -55,6 +55,7 @@ class ReplaceRuleViewModel(
     private val bookContentProcessGateway: BookContentProcessGateway,
     private val bookContentProcessGateway: BookContentProcessGateway,
     private val readSettingsRepository: ReadSettingsRepository,
     private val readSettingsRepository: ReadSettingsRepository,
     private val repository: ReplaceRuleRepository,
     private val repository: ReplaceRuleRepository,
+    private val otherSettingsGateway: OtherSettingsGateway,
 ) : BaseRuleViewModel<ReplaceRuleItemUi, ReplaceRule, Long, ReplaceRuleUiState>(
 ) : BaseRuleViewModel<ReplaceRuleItemUi, ReplaceRule, Long, ReplaceRuleUiState>(
     application,
     application,
     ReplaceRuleUiState(interaction = InteractionState(isLoading = true)),
     ReplaceRuleUiState(interaction = InteractionState(isLoading = true)),
@@ -419,7 +420,8 @@ class ReplaceRuleViewModel(
         val chapterInput = ReadBook.readerChapterInputWindow.current
         val chapterInput = ReadBook.readerChapterInputWindow.current
         if (book != null && book.bookUrl == bookUrl) {
         if (book != null && book.bookUrl == bookUrl) {
             val effectiveRules = chapterInput?.content?.effectiveReplaceRules.orEmpty().toImmutableList()
             val effectiveRules = chapterInput?.content?.effectiveReplaceRules.orEmpty().toImmutableList()
-            val replaceEnabled = book.getUseReplaceRule(AppConfig.replaceEnableDefault)
+            val replaceEnabled =
+                book.getUseReplaceRule(otherSettingsGateway.currentSettings.replaceEnableDefault)
             val chineseConvertActive = readSettingsRepository.currentSettings.chineseConverterType > 0
             val chineseConvertActive = readSettingsRepository.currentSettings.chineseConverterType > 0
             val reSegmentActive = book.getReSegment()
             val reSegmentActive = book.getReSegment()
             _bookState.update {
             _bookState.update {
@@ -436,7 +438,9 @@ class ReplaceRuleViewModel(
 
 
     private fun toggleReplaceEnable() {
     private fun toggleReplaceEnable() {
         ReadBook.book?.let { book ->
         ReadBook.book?.let { book ->
-            val enabled = !book.getUseReplaceRule(AppConfig.replaceEnableDefault)
+            val enabled = !book.getUseReplaceRule(
+                otherSettingsGateway.currentSettings.replaceEnableDefault
+            )
             book.setUseReplaceRule(enabled)
             book.setUseReplaceRule(enabled)
             ReadBook.saveRead()
             ReadBook.saveRead()
             _bookState.update { it.copy(replaceEnabled = enabled) }
             _bookState.update { it.copy(replaceEnabled = enabled) }

+ 3 - 3
app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt

@@ -17,7 +17,6 @@ import io.legado.app.domain.gateway.AppShellSettingsGateway
 import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.domain.gateway.DownloadCacheSettingsGateway
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.TTS
 import io.legado.app.help.TTS
-import io.legado.app.help.config.AppConfig
 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.webView.WebJsExtensions
 import io.legado.app.help.webView.WebJsExtensions
@@ -56,7 +55,7 @@ data class ReadRssSettings(
 class ReadRssViewModel(
 class ReadRssViewModel(
     application: Application,
     application: Application,
     appShellSettingsGateway: AppShellSettingsGateway,
     appShellSettingsGateway: AppShellSettingsGateway,
-    downloadCacheSettingsGateway: DownloadCacheSettingsGateway,
+    private val downloadCacheSettingsGateway: DownloadCacheSettingsGateway,
     private val rssRepository: RssRepository,
     private val rssRepository: RssRepository,
     private val articleRepository: RssArticleRepository,
     private val articleRepository: RssArticleRepository,
     private val favoriteRepository: RssFavoriteRepository,
     private val favoriteRepository: RssFavoriteRepository,
@@ -106,7 +105,8 @@ class ReadRssViewModel(
             rssSource = rssRepository.getByKey(args.origin)
             rssSource = rssRepository.getByKey(args.origin)
             hasPreloadJs = !rssSource?.preloadJs.isNullOrBlank()
             hasPreloadJs = !rssSource?.preloadJs.isNullOrBlank()
             headerMap = runScriptWithContext {
             headerMap = runScriptWithContext {
-                rssSource?.getHeaderMap(AppConfig.userAgent) ?: emptyMap()
+                rssSource?.getHeaderMap(downloadCacheSettingsGateway.currentSettings.userAgent)
+                    ?: emptyMap()
             }
             }
             isStartPage = args.startPage
             isStartPage = args.startPage
             if (isStartPage) {
             if (isStartPage) {

+ 2 - 2
app/src/main/java/io/legado/app/ui/rss/read/RssReadRouteScreen.kt

@@ -27,7 +27,7 @@ import androidx.compose.material.icons.filled.Refresh
 import androidx.compose.material.icons.filled.Share
 import androidx.compose.material.icons.filled.Share
 import androidx.compose.material.icons.filled.Star
 import androidx.compose.material.icons.filled.Star
 import androidx.compose.material.icons.filled.Stop
 import androidx.compose.material.icons.filled.Stop
-import androidx.compose.material.icons.filled.VolumeUp
+import androidx.compose.material.icons.automirrored.filled.VolumeUp
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.Icon
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Composable
@@ -348,7 +348,7 @@ fun RssReadRouteScreen(
                                         R.string.read_aloud
                                         R.string.read_aloud
                                     ),
                                     ),
                                     leadingIcon = {
                                     leadingIcon = {
-                                        MenuItemIcon(if (isSpeaking) Icons.Default.Stop else Icons.Default.VolumeUp)
+                                        MenuItemIcon(if (isSpeaking) Icons.Default.Stop else Icons.AutoMirrored.Filled.VolumeUp)
                                     },
                                     },
                                     onClick = {
                                     onClick = {
                                         dismiss()
                                         dismiss()

+ 2 - 2
app/src/main/java/io/legado/app/ui/widget/BatteryViewOrgin.kt

@@ -11,7 +11,6 @@ import android.text.StaticLayout
 import android.util.AttributeSet
 import android.util.AttributeSet
 import androidx.annotation.ColorInt
 import androidx.annotation.ColorInt
 import androidx.appcompat.widget.AppCompatTextView
 import androidx.appcompat.widget.AppCompatTextView
-import io.legado.app.ui.config.readConfig.ReadConfig
 import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
 import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
 import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
 import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
 import io.legado.app.utils.dpToPx
 import io.legado.app.utils.dpToPx
@@ -20,6 +19,7 @@ class BatteryViewOrgin @JvmOverloads constructor(
     context: Context,
     context: Context,
     attrs: AttributeSet? = null
     attrs: AttributeSet? = null
 ) : AppCompatTextView(context, attrs) {
 ) : AppCompatTextView(context, attrs) {
+    private val readSettingsGateway get() = org.koin.core.context.GlobalContext.get().get<io.legado.app.domain.gateway.ReadSettingsGateway>()
     private val batteryTypeface by lazy {
     private val batteryTypeface by lazy {
         Typeface.createFromAsset(context.assets, "font/number.ttf")
         Typeface.createFromAsset(context.assets, "font/number.ttf")
     }
     }
@@ -75,7 +75,7 @@ class BatteryViewOrgin @JvmOverloads constructor(
     }
     }
 
 
     override fun onDraw(canvas: Canvas) {
     override fun onDraw(canvas: Canvas) {
-        if (ReadConfig.optimizeRender) {
+        if (readSettingsGateway.currentSettings.optimizeRender) {
             canvasRecorder.recordIfNeededThenDraw(canvas, width, height) {
             canvasRecorder.recordIfNeededThenDraw(canvas, width, height) {
                 super.onDraw(this)
                 super.onDraw(this)
                 drawBattery(this)
                 drawBattery(this)

+ 3 - 3
app/src/main/java/io/legado/app/ui/widget/components/AppTextField.kt

@@ -41,7 +41,7 @@ fun AppTextField(
     readOnly: Boolean = false,
     readOnly: Boolean = false,
     backgroundColor: Color = Color.Unspecified,
     backgroundColor: Color = Color.Unspecified,
     label: String? = null,
     label: String? = null,
-    labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Attached(),
+    labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Inside(),
     placeholder: @Composable (() -> Unit)? = null,
     placeholder: @Composable (() -> Unit)? = null,
     leadingIcon: @Composable (() -> Unit)? = null,
     leadingIcon: @Composable (() -> Unit)? = null,
     trailingIcon: @Composable (() -> Unit)? = null,
     trailingIcon: @Composable (() -> Unit)? = null,
@@ -241,7 +241,7 @@ fun AppTextFieldSurface(
     enabled: Boolean = true,
     enabled: Boolean = true,
     readOnly: Boolean = false,
     readOnly: Boolean = false,
     label: String? = null,
     label: String? = null,
-    labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Attached(),
+    labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Inside(),
     placeholder: @Composable (() -> Unit)? = null,
     placeholder: @Composable (() -> Unit)? = null,
     leadingIcon: @Composable (() -> Unit)? = null,
     leadingIcon: @Composable (() -> Unit)? = null,
     trailingIcon: @Composable (() -> Unit)? = null,
     trailingIcon: @Composable (() -> Unit)? = null,
@@ -297,7 +297,7 @@ fun AppDenseTextField(
     readOnly: Boolean = false,
     readOnly: Boolean = false,
     backgroundColor: Color = Color.Unspecified,
     backgroundColor: Color = Color.Unspecified,
     label: String? = null,
     label: String? = null,
-    labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Attached(),
+    labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Inside(),
     placeholder: @Composable (() -> Unit)? = null,
     placeholder: @Composable (() -> Unit)? = null,
     leadingIcon: @Composable (() -> Unit)? = null,
     leadingIcon: @Composable (() -> Unit)? = null,
     trailingIcon: @Composable (() -> Unit)? = null,
     trailingIcon: @Composable (() -> Unit)? = null,

+ 8 - 9
app/src/main/java/io/legado/app/ui/widget/components/card/SelectionItemCard.kt

@@ -207,14 +207,6 @@ fun SelectionItemCardContent(
         } else {
         } else {
             ListItem(
             ListItem(
                 modifier = Modifier.weight(1f),
                 modifier = Modifier.weight(1f),
-                headlineContent = {
-                    AppText(
-                        text = title,
-                        style = LegadoTheme.typography.titleSmall,
-                        maxLines = 1,
-                        overflow = TextOverflow.Ellipsis
-                    )
-                },
                 supportingContent = when {
                 supportingContent = when {
                     supportingContent != null -> supportingContent
                     supportingContent != null -> supportingContent
                     !subtitle.isNullOrBlank() -> {
                     !subtitle.isNullOrBlank() -> {
@@ -230,7 +222,14 @@ fun SelectionItemCardContent(
                     else -> null
                     else -> null
                 },
                 },
                 colors = ListItemDefaults.colors(containerColor = Color.Transparent)
                 colors = ListItemDefaults.colors(containerColor = Color.Transparent)
-            )
+            ) {
+                AppText(
+                    text = title,
+                    style = LegadoTheme.typography.titleSmall,
+                    maxLines = 1,
+                    overflow = TextOverflow.Ellipsis
+                )
+            }
         }
         }
 
 
         Row(
         Row(

+ 14 - 3
app/src/main/java/io/legado/app/ui/widget/dialog/BottomWebViewDialog.kt

@@ -43,10 +43,11 @@ import io.legado.app.constant.AppLog
 import io.legado.app.data.appDb
 import io.legado.app.data.appDb
 import io.legado.app.data.entities.BaseSource
 import io.legado.app.data.entities.BaseSource
 import io.legado.app.databinding.DialogWebViewBinding
 import io.legado.app.databinding.DialogWebViewBinding
+import io.legado.app.domain.gateway.AppShellSettingsGateway
 import io.legado.app.domain.gateway.AppUiConfigurationGateway
 import io.legado.app.domain.gateway.AppUiConfigurationGateway
+import io.legado.app.domain.gateway.OtherSettingsGateway
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.WebCacheManager
 import io.legado.app.help.WebCacheManager
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.http.newCallResponse
 import io.legado.app.help.http.newCallResponse
 import io.legado.app.help.http.newCallResponseBody
 import io.legado.app.help.http.newCallResponseBody
@@ -69,10 +70,12 @@ import io.legado.app.utils.GSON
 import io.legado.app.utils.applyDayNight
 import io.legado.app.utils.applyDayNight
 import io.legado.app.utils.fromJsonObject
 import io.legado.app.utils.fromJsonObject
 import io.legado.app.utils.invisible
 import io.legado.app.utils.invisible
+import io.legado.app.utils.isNightMode
 import io.legado.app.utils.longSnackbar
 import io.legado.app.utils.longSnackbar
 import io.legado.app.utils.openUrl
 import io.legado.app.utils.openUrl
 import io.legado.app.utils.setLayout
 import io.legado.app.utils.setLayout
 import io.legado.app.utils.startActivity
 import io.legado.app.utils.startActivity
+import io.legado.app.utils.sysConfiguration
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.viewbindingdelegate.viewBinding
 import io.legado.app.utils.viewbindingdelegate.viewBinding
 import io.legado.app.utils.visible
 import io.legado.app.utils.visible
@@ -124,6 +127,14 @@ class BottomWebViewDialog() : BottomSheetDialogFragment(R.layout.dialog_web_view
     }
     }
     private val displayMetrics by lazy { resources.displayMetrics }
     private val displayMetrics by lazy { resources.displayMetrics }
     private val appUiConfigurationGateway by inject<AppUiConfigurationGateway>()
     private val appUiConfigurationGateway by inject<AppUiConfigurationGateway>()
+    private val otherSettingsGateway by inject<OtherSettingsGateway>()
+    private val appShellSettingsGateway by inject<AppShellSettingsGateway>()
+    private val isNightTheme: Boolean
+        get() = when (appShellSettingsGateway.currentSettings.themeMode) {
+            "1" -> false
+            "2" -> true
+            else -> sysConfiguration.isNightMode
+        }
     private var appliedDarkTheme: Boolean? = null
     private var appliedDarkTheme: Boolean? = null
     private val selectImageDir = registerForActivityResult(HandleFileContract()) {
     private val selectImageDir = registerForActivityResult(HandleFileContract()) {
         it.uri?.let { uri ->
         it.uri?.let { uri ->
@@ -727,7 +738,7 @@ class BottomWebViewDialog() : BottomSheetDialogFragment(R.layout.dialog_web_view
         }
         }
 
 
         override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
         override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
-            if (!AppConfig.recordLog) return false
+            if (!otherSettingsGateway.currentSettings.recordLog) return false
             val source = source ?: return false
             val source = source ?: return false
             val messageLevel = consoleMessage.messageLevel().name
             val messageLevel = consoleMessage.messageLevel().name
             val message = consoleMessage.message()
             val message = consoleMessage.message()
@@ -765,7 +776,7 @@ class BottomWebViewDialog() : BottomSheetDialogFragment(R.layout.dialog_web_view
 
 
         override fun onPageFinished(view: WebView?, url: String?) {
         override fun onPageFinished(view: WebView?, url: String?) {
             super.onPageFinished(view, url)
             super.onPageFinished(view, url)
-            applyWebViewTheme(appliedDarkTheme ?: AppConfig.isNightTheme, force = true)
+            applyWebViewTheme(appliedDarkTheme ?: isNightTheme, force = true)
         }
         }
 
 
         private fun shouldOverrideUrlLoading(url: Uri): Boolean {
         private fun shouldOverrideUrlLoading(url: Uri): Boolean {

+ 27 - 14
app/src/main/java/io/legado/app/ui/widget/image/CoverImageView.kt

@@ -20,15 +20,18 @@ import com.bumptech.glide.request.RequestListener
 import com.bumptech.glide.request.RequestOptions
 import com.bumptech.glide.request.RequestOptions
 import com.bumptech.glide.request.target.Target
 import com.bumptech.glide.request.target.Target
 import io.legado.app.constant.AppPattern
 import io.legado.app.constant.AppPattern
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.AppShellSettingsGateway
+import io.legado.app.domain.gateway.CoverSettingsGateway
 import io.legado.app.help.glide.ImageLoader
 import io.legado.app.help.glide.ImageLoader
 import io.legado.app.help.glide.OkHttpModelLoader
 import io.legado.app.help.glide.OkHttpModelLoader
 import io.legado.app.model.BookCover
 import io.legado.app.model.BookCover
-import io.legado.app.ui.config.coverConfig.CoverConfig
+import io.legado.app.utils.isNightMode
 import io.legado.app.utils.spToPx
 import io.legado.app.utils.spToPx
+import io.legado.app.utils.sysConfiguration
 import io.legado.app.utils.textHeight
 import io.legado.app.utils.textHeight
 import io.legado.app.utils.themeColor
 import io.legado.app.utils.themeColor
 import io.legado.app.utils.toStringArray
 import io.legado.app.utils.toStringArray
+import org.koin.core.context.GlobalContext
 
 
 /**
 /**
  * 封面
  * 封面
@@ -51,9 +54,19 @@ class CoverImageView @JvmOverloads constructor(
     private var author: String? = null
     private var author: String? = null
     private var nameHeight = 0f
     private var nameHeight = 0f
     private var authorHeight = 0f
     private var authorHeight = 0f
-    private val isNightTheme get() = AppConfig.isNightTheme
-    private val colorKey get() = if (isNightTheme) CoverConfig.coverTextColorN else CoverConfig.coverTextColor
-    private val shadowKey get() = if (isNightTheme) CoverConfig.coverShadowColorN else CoverConfig.coverShadowColor
+    private val coverSettingsGateway
+        get() = GlobalContext.get().get<CoverSettingsGateway>()
+    private val coverSettings get() = coverSettingsGateway.currentSettings
+    private val appShellSettingsGateway
+        get() = GlobalContext.get().get<AppShellSettingsGateway>()
+    private val isNightTheme: Boolean
+        get() = when (appShellSettingsGateway.currentSettings.themeMode) {
+            "1" -> false
+            "2" -> true
+            else -> sysConfiguration.isNightMode
+        }
+    private val colorKey get() = if (isNightTheme) coverSettings.textColorDark else coverSettings.textColor
+    private val shadowKey get() = if (isNightTheme) coverSettings.shadowColorDark else coverSettings.shadowColor
     private val namePaint by lazy {
     private val namePaint by lazy {
         val textPaint = TextPaint()
         val textPaint = TextPaint()
         textPaint.typeface = Typeface.DEFAULT_BOLD
         textPaint.typeface = Typeface.DEFAULT_BOLD
@@ -128,14 +141,14 @@ class CoverImageView @JvmOverloads constructor(
     private fun drawNameAuthor(canvas: Canvas) {
     private fun drawNameAuthor(canvas: Canvas) {
         var startX = width * 0.2f
         var startX = width * 0.2f
         var startY = viewHeight * 0.2f
         var startY = viewHeight * 0.2f
-        val showName = if (isNightTheme) CoverConfig.coverShowNameN else CoverConfig.coverShowName
+        val showName = if (isNightTheme) coverSettings.showNameDark else coverSettings.showName
         if (showName) {
         if (showName) {
             name?.toStringArray()?.let { nameChars ->
             name?.toStringArray()?.let { nameChars ->
                 val textSize = viewWidth / 8
                 val textSize = viewWidth / 8
                 namePaint.textSize = textSize
                 namePaint.textSize = textSize
                 namePaint.strokeWidth = textSize / 8
                 namePaint.strokeWidth = textSize / 8
 
 
-                if (CoverConfig.coverShowShadow) {
+                if (coverSettings.showShadow) {
                     namePaint.setShadowLayer(
                     namePaint.setShadowLayer(
                         4f,
                         4f,
                         0f,
                         0f,
@@ -147,12 +160,12 @@ class CoverImageView @JvmOverloads constructor(
                 }
                 }
 
 
                 nameChars.forEach { char ->
                 nameChars.forEach { char ->
-                    if (CoverConfig.coverShowStroke) {
+                    if (coverSettings.showStroke) {
                         namePaint.color = Color.WHITE
                         namePaint.color = Color.WHITE
                         namePaint.style = Paint.Style.STROKE
                         namePaint.style = Paint.Style.STROKE
                         canvas.drawText(char, startX, startY, namePaint)
                         canvas.drawText(char, startX, startY, namePaint)
                     }
                     }
-                    if (CoverConfig.coverDefaultColor)
+                    if (coverSettings.useDefaultColor)
                         namePaint.color = context.themeColor(com.google.android.material.R.attr.colorSecondary)
                         namePaint.color = context.themeColor(com.google.android.material.R.attr.colorSecondary)
                     else
                     else
                         namePaint.color = colorKey
                         namePaint.color = colorKey
@@ -171,7 +184,7 @@ class CoverImageView @JvmOverloads constructor(
         }
         }
 
 
         val showAuthor =
         val showAuthor =
-            if (isNightTheme) CoverConfig.coverShowAuthorN else CoverConfig.coverShowAuthor
+            if (isNightTheme) coverSettings.showAuthorDark else coverSettings.showAuthor
         if (showAuthor) {
         if (showAuthor) {
             author?.toStringArray()?.let { author ->
             author?.toStringArray()?.let { author ->
                 authorPaint.textSize = viewWidth / 10
                 authorPaint.textSize = viewWidth / 10
@@ -179,7 +192,7 @@ class CoverImageView @JvmOverloads constructor(
                 startX = width * 0.8f
                 startX = width * 0.8f
                 startY = viewHeight * 0.95f - author.size * authorPaint.textHeight
                 startY = viewHeight * 0.95f - author.size * authorPaint.textHeight
                 startY = maxOf(startY, viewHeight * 0.2f)
                 startY = maxOf(startY, viewHeight * 0.2f)
-                if (CoverConfig.coverShowShadow) {
+                if (coverSettings.showShadow) {
                     authorPaint.setShadowLayer(
                     authorPaint.setShadowLayer(
                         4f,
                         4f,
                         0f,
                         0f,
@@ -191,12 +204,12 @@ class CoverImageView @JvmOverloads constructor(
                 }
                 }
 
 
                 author.forEach {
                 author.forEach {
-                    if (CoverConfig.coverShowStroke) {
+                    if (coverSettings.showStroke) {
                         authorPaint.color = Color.WHITE
                         authorPaint.color = Color.WHITE
                         authorPaint.style = Paint.Style.STROKE
                         authorPaint.style = Paint.Style.STROKE
                         canvas.drawText(it, startX, startY, authorPaint)
                         canvas.drawText(it, startX, startY, authorPaint)
                     }
                     }
-                    if (CoverConfig.coverDefaultColor)
+                    if (coverSettings.useDefaultColor)
                         authorPaint.color = context.themeColor(com.google.android.material.R.attr.colorSecondary)
                         authorPaint.color = context.themeColor(com.google.android.material.R.attr.colorSecondary)
                     else
                     else
                         authorPaint.color = colorKey
                         authorPaint.color = colorKey
@@ -262,7 +275,7 @@ class CoverImageView @JvmOverloads constructor(
         this.author = author?.replace(AppPattern.bdRegex, "")?.trim()
         this.author = author?.replace(AppPattern.bdRegex, "")?.trim()
         defaultCover = true
         defaultCover = true
         invalidate()
         invalidate()
-        if (AppConfig.useDefaultCover) {
+        if (coverSettings.useDefaultCover) {
             ImageLoader.load(context, BookCover.defaultDrawable)
             ImageLoader.load(context, BookCover.defaultDrawable)
                 .centerCrop()
                 .centerCrop()
                 .into(this)
                 .into(this)

+ 7 - 3
app/src/main/java/io/legado/app/utils/AnimationExtensions.kt

@@ -4,12 +4,16 @@ import android.content.Context
 import android.view.animation.Animation
 import android.view.animation.Animation
 import android.view.animation.AnimationUtils
 import android.view.animation.AnimationUtils
 import androidx.annotation.AnimRes
 import androidx.annotation.AnimRes
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.ThemeSettingsGateway
+import org.koin.core.context.GlobalContext
+
+private val themeSettingsGateway
+    get() = GlobalContext.get().get<ThemeSettingsGateway>()
 
 
 fun loadAnimation(context: Context, @AnimRes id: Int): Animation {
 fun loadAnimation(context: Context, @AnimRes id: Int): Animation {
     val animation = AnimationUtils.loadAnimation(context, id)
     val animation = AnimationUtils.loadAnimation(context, id)
-    if (AppConfig.isEInkMode) {
+    if (themeSettingsGateway.currentSettings.appTheme == "4") {
         animation.duration = 0
         animation.duration = 0
     }
     }
     return animation
     return animation
-}
+}

+ 17 - 10
app/src/main/java/io/legado/app/utils/ContextExtensions.kt

@@ -41,13 +41,14 @@ import io.legado.app.data.entities.Book
 import io.legado.app.help.IntentHelp
 import io.legado.app.help.IntentHelp
 import io.legado.app.help.book.isAudio
 import io.legado.app.help.book.isAudio
 import io.legado.app.help.book.isImage
 import io.legado.app.help.book.isImage
-import io.legado.app.help.config.AppConfig
 import io.legado.app.help.config.AppConfigStore
 import io.legado.app.help.config.AppConfigStore
 import io.legado.app.help.config.SettingsWriter
 import io.legado.app.help.config.SettingsWriter
-import io.legado.app.ui.config.readMangaConfig.ReadMangaConfig
+import io.legado.app.domain.gateway.BookshelfSettingsGateway
+import io.legado.app.domain.gateway.MangaSettingsGateway
 import io.legado.app.ui.main.MainActivity
 import io.legado.app.ui.main.MainActivity
 import io.legado.app.ui.main.bookshelf.BookShelfItem
 import io.legado.app.ui.main.bookshelf.BookShelfItem
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.runBlocking
+import org.koin.core.context.GlobalContext
 import splitties.systemservices.clipboardManager
 import splitties.systemservices.clipboardManager
 import splitties.systemservices.connectivityManager
 import splitties.systemservices.connectivityManager
 import splitties.systemservices.uiModeManager
 import splitties.systemservices.uiModeManager
@@ -62,19 +63,25 @@ inline fun <reified A : Activity> Context.startActivity(configIntent: Intent.()
     startActivity(intent)
     startActivity(intent)
 }
 }
 
 
+private val mangaSettingsGateway
+    get() = GlobalContext.get().get<MangaSettingsGateway>()
+
+private val bookshelfSettingsGateway
+    get() = GlobalContext.get().get<BookshelfSettingsGateway>()
+
 fun Context.startActivityForBook(
 fun Context.startActivityForBook(
     book: Book,
     book: Book,
     configIntent: Intent.() -> Unit = {},
     configIntent: Intent.() -> Unit = {},
 ) {
 ) {
     val intent = when {
     val intent = when {
         book.isAudio -> MainActivity.createAudioPlayIntent(this, book.bookUrl)
         book.isAudio -> MainActivity.createAudioPlayIntent(this, book.bookUrl)
-        book.isImage && ReadMangaConfig.showMangaUi ->
+        book.isImage && mangaSettingsGateway.currentSettings.showMangaUi ->
             MainActivity.createReadMangaIntent(this, book.bookUrl)
             MainActivity.createReadMangaIntent(this, book.bookUrl)
 
 
         else -> MainActivity.createReadBookIntent(this, book.bookUrl)
         else -> MainActivity.createReadBookIntent(this, book.bookUrl)
     }
     }
     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-    if (book.isAudio || (book.isImage && ReadMangaConfig.showMangaUi)) {
+    if (book.isAudio || (book.isImage && mangaSettingsGateway.currentSettings.showMangaUi)) {
         intent.putExtra("bookUrl", book.bookUrl)
         intent.putExtra("bookUrl", book.bookUrl)
     }
     }
     intent.apply(configIntent)
     intent.apply(configIntent)
@@ -87,13 +94,13 @@ fun Context.startActivityForBook(
 ) {
 ) {
     val intent = when {
     val intent = when {
         book.isAudio -> MainActivity.createAudioPlayIntent(this, book.bookUrl)
         book.isAudio -> MainActivity.createAudioPlayIntent(this, book.bookUrl)
-        book.isImage && ReadMangaConfig.showMangaUi ->
+        book.isImage && mangaSettingsGateway.currentSettings.showMangaUi ->
             MainActivity.createReadMangaIntent(this, book.bookUrl)
             MainActivity.createReadMangaIntent(this, book.bookUrl)
 
 
         else -> MainActivity.createReadBookIntent(this, book.bookUrl)
         else -> MainActivity.createReadBookIntent(this, book.bookUrl)
     }
     }
     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-    if (book.isAudio || (book.isImage && ReadMangaConfig.showMangaUi)) {
+    if (book.isAudio || (book.isImage && mangaSettingsGateway.currentSettings.showMangaUi)) {
         intent.putExtra("bookUrl", book.bookUrl)
         intent.putExtra("bookUrl", book.bookUrl)
     }
     }
     intent.apply(configIntent)
     intent.apply(configIntent)
@@ -457,16 +464,16 @@ val Context.isDebuggable: Boolean
 
 
 val Context.bookshelfLayoutMode: Int
 val Context.bookshelfLayoutMode: Int
     get() = if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
     get() = if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
-        AppConfig.bookshelfLayoutModeLandscape
+        bookshelfSettingsGateway.currentSettings.bookshelfLayoutModeLandscape
     } else {
     } else {
-        AppConfig.bookshelfLayoutModePortrait
+        bookshelfSettingsGateway.currentSettings.bookshelfLayoutModePortrait
     }
     }
 
 
 val Context.bookshelfLayoutGrid: Int
 val Context.bookshelfLayoutGrid: Int
     get() = if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
     get() = if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
-        AppConfig.bookshelfLayoutGridLandscape
+        bookshelfSettingsGateway.currentSettings.bookshelfLayoutGridLandscape
     } else {
     } else {
-        AppConfig.bookshelfLayoutGridPortrait
+        bookshelfSettingsGateway.currentSettings.bookshelfLayoutGridPortrait
     }
     }
 
 
 fun Context.themeColor(attr: Int): Int {
 fun Context.themeColor(attr: Int): Int {

+ 7 - 2
app/src/main/java/io/legado/app/utils/CustomExportUtils.kt

@@ -1,17 +1,22 @@
 package io.legado.app.utils
 package io.legado.app.utils
 
 
-import io.legado.app.help.config.AppConfig
+import io.legado.app.domain.gateway.BookExportSettingsGateway
+import org.koin.core.context.GlobalContext
 
 
 // 匹配待“输入的章节”字符串
 // 匹配待“输入的章节”字符串
 private val regexEpisode = Regex("\\d+(-\\d+)?(,\\d+(-\\d+)?)*")
 private val regexEpisode = Regex("\\d+(-\\d+)?(,\\d+(-\\d+)?)*")
 
 
+private val bookExportSettingsGateway
+    get() = GlobalContext.get().get<BookExportSettingsGateway>()
+
 /**
 /**
  * 是否启用自定义导出
  * 是否启用自定义导出
  *
  *
  * @author Discut
  * @author Discut
  */
  */
 fun enableCustomExport(): Boolean {
 fun enableCustomExport(): Boolean {
-    return AppConfig.enableCustomExport && AppConfig.exportType == 1
+    val exportSettings = bookExportSettingsGateway.currentSettings
+    return exportSettings.enableCustomExport && exportSettings.exportType == 1
 }
 }
 
 
 /**
 /**

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff