From 75a3752079922be801cc0ff80b168a6700c5a0d3 Mon Sep 17 00:00:00 2001
From: HapeLee <1321903405@qq.com>
Date: Fri, 27 Mar 2026 01:22:54 +0800
Subject: [PATCH] =?UTF-8?q?[=E4=BC=98=E5=8C=96]=20=E5=AF=BC=E8=88=AA?=
=?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=8D=A2=E6=88=90Navigation3=EF=BC=8C?=
=?UTF-8?q?=E8=80=81=E6=97=A7=E8=AE=BE=E7=BD=AE=E9=A1=B5=E6=9B=BF=E6=8D=A2?=
=?UTF-8?q?=E4=B8=BA=E6=96=B0=E7=9A=84Compose=E7=95=8C=E9=9D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 25 +-
app/src/main/AndroidManifest.xml | 11 +-
.../java/io/legado/app/constant/PreferKey.kt | 1 +
.../legado/app/help/config/OldThemeConfig.kt | 11 +-
.../app/ui/config/BackupConfigFragment.kt | 414 ---------------
.../io/legado/app/ui/config/ConfigActivity.kt | 46 --
.../legado/app/ui/config/ConfigNavScreen.kt | 78 +++
.../legado/app/ui/config/ConfigViewModel.kt | 51 --
.../app/ui/config/CoverConfigFragment.kt | 168 ------
.../app/ui/config/OtherConfigFragment.kt | 401 ---------------
.../app/ui/config/ReadConfigFragment.kt | 175 -------
.../app/ui/config/TestConfigActivity.kt | 153 ------
.../app/ui/config/ThemeConfigFragment.kt | 479 ------------------
.../legado/app/ui/config/ThemeListDialog.kt | 129 -----
.../backupConfig/BackupConfigViewModel.kt | 9 +
.../config/themeConfig/ThemeConfigScreen.kt | 59 +++
.../ui/config/themeConfig/ThemeListDialog.kt | 184 +++++++
.../io/legado/app/ui/main/MainActivity.kt | 265 +++++++++-
.../java/io/legado/app/ui/main/MainScreen.kt | 4 +-
.../java/io/legado/app/ui/main/my/MyScreen.kt | 101 +---
20 files changed, 632 insertions(+), 2132 deletions(-)
delete mode 100644 app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt
delete mode 100644 app/src/main/java/io/legado/app/ui/config/ConfigActivity.kt
create mode 100644 app/src/main/java/io/legado/app/ui/config/ConfigNavScreen.kt
delete mode 100644 app/src/main/java/io/legado/app/ui/config/ConfigViewModel.kt
delete mode 100644 app/src/main/java/io/legado/app/ui/config/CoverConfigFragment.kt
delete mode 100644 app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt
delete mode 100644 app/src/main/java/io/legado/app/ui/config/ReadConfigFragment.kt
delete mode 100644 app/src/main/java/io/legado/app/ui/config/TestConfigActivity.kt
delete mode 100644 app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
delete mode 100644 app/src/main/java/io/legado/app/ui/config/ThemeListDialog.kt
create mode 100644 app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeListDialog.kt
diff --git a/README.md b/README.md
index d47aaeca5..57ef54751 100644
--- a/README.md
+++ b/README.md
@@ -38,25 +38,11 @@ Design 3 风格重构版本。
## 🛠️ 核心功能
-1. **自定义书源:** 强大的爬虫引擎,规则简单易懂,支持自定义抓取网页数据。
-2. **多格式支持:** 支持本地 TXT、EPUB 阅读,智能扫描本地文件。
-3. **高度自定义:** 切换字体、背景、行距、段距、加粗、简繁转换等。
-4. **订阅内容:** 不止是书,可以订阅任何你想看的内容。
-5. **净化替换:** 强力去除广告,替换正文内容。
-6. **翻页模式:** 覆盖、仿真、滑动、滚动等多种模式随心切换。
-7. **完全开源:** 无广告,持续迭代优化。
-
----
-
-## 🤝 社区与支持
-
-由于本项目的开发者比较忙,暂时没有独立社区。
-
-以下为原项目的社区:
-
-* **官方 Wiki:** [书源规则教程](https://mgz0227.github.io/The-tutorial-of-Legado/)
-* **交流社区:** [Telegram 频道](https://t.me/legado_channels) | [Discord](https://discord.gg/VtUfRyzRXn) | [语雀 Wiki](https://www.yuque.com/legado/wiki/community)
-* **相关文档:** [免责声明](https://gedoor.github.io/Disclaimer)
+1. **多格式支持:** 支持本地 TXT、EPUB 阅读,智能扫描本地文件。
+2. **高度自定义:** 切换字体、背景、行距、段距、加粗、简繁转换等。
+3. **净化替换:** 强力去除广告,替换正文内容。
+4. **翻页模式:** 覆盖、仿真、滑动、滚动等多种模式随心切换。
+5. **完全开源:** 无广告,持续迭代优化。
---
@@ -78,6 +64,5 @@ Design 3 风格重构版本。
## ⚠️ 免责声明
-本项目为 **Legado(阅读)** 的非官方分支,仅用于学习、交流与个人使用。
项目内容不代表原作者立场,与原项目作者不存在任何隶属或授权关系。
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3d4d585c7..00ea91137 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -211,15 +211,6 @@
android:configChanges="locale|keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize|stateHidden" />
-
-
-
-
\ No newline at end of file
+
diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt
index bfc49a24d..c0f9bcf2f 100644
--- a/app/src/main/java/io/legado/app/constant/PreferKey.kt
+++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt
@@ -52,6 +52,7 @@ object PreferKey {
const val bookshelfLayoutModePortrait = "bookshelfLayoutPortrait"
const val bookshelfLayoutModeLandscape = "bookshelf_layout_landscape"
const val bookshelfLayoutCompact = "bookshelfLayoutCompact"
+ const val bookshelfShowDivider = "bookshelfShowDivider"
const val bookshelfGridLayout = "bookshelfGridLayout"
const val bookshelfSort = "bookshelfSort"
const val bookExportFileName = "bookExportFileName"
diff --git a/app/src/main/java/io/legado/app/help/config/OldThemeConfig.kt b/app/src/main/java/io/legado/app/help/config/OldThemeConfig.kt
index 42a54eef2..ceb8e501b 100644
--- a/app/src/main/java/io/legado/app/help/config/OldThemeConfig.kt
+++ b/app/src/main/java/io/legado/app/help/config/OldThemeConfig.kt
@@ -168,19 +168,10 @@ object OldThemeConfig {
fun applyConfig(context: Context, config: Config) {
try {
val primary = config.primaryColor.toColorInt()
- val accent = config.accentColor.toColorInt()
- val background = config.backgroundColor.toColorInt()
- val bBackground = config.bottomBackground.toColorInt()
if (config.isNightTheme) {
context.putPrefInt(PreferKey.cNPrimary, primary)
- context.putPrefInt(PreferKey.cNAccent, accent)
- context.putPrefInt(PreferKey.cNBackground, background)
- context.putPrefInt(PreferKey.cNBBackground, bBackground)
} else {
context.putPrefInt(PreferKey.cPrimary, primary)
- context.putPrefInt(PreferKey.cAccent, accent)
- context.putPrefInt(PreferKey.cBackground, background)
- context.putPrefInt(PreferKey.cBBackground, bBackground)
}
AppConfig.isNightTheme = config.isNightTheme
applyDayNight(context)
@@ -332,4 +323,4 @@ object OldThemeConfig {
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt
deleted file mode 100644
index c3076e27a..000000000
--- a/app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt
+++ /dev/null
@@ -1,414 +0,0 @@
-package io.legado.app.ui.config
-
-import android.content.Context
-import android.content.SharedPreferences
-import android.os.Bundle
-import android.text.InputType
-import android.view.Menu
-import android.view.MenuInflater
-import android.view.MenuItem
-import android.view.View
-import androidx.core.view.MenuProvider
-import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.lifecycleScope
-import androidx.preference.EditTextPreference
-import androidx.preference.ListPreference
-import androidx.preference.Preference
-import androidx.preference.PreferenceManager
-import io.legado.app.R
-import io.legado.app.constant.AppLog
-import io.legado.app.constant.PreferKey
-import io.legado.app.databinding.DialogWebdavAuthBinding
-import io.legado.app.exception.NoStackTraceException
-import io.legado.app.help.AppWebDav
-import io.legado.app.help.AppWebDav.testWebDav
-import io.legado.app.help.config.AppConfig
-import io.legado.app.help.config.LocalConfig
-import io.legado.app.help.coroutine.Coroutine
-import io.legado.app.help.storage.Backup
-import io.legado.app.help.storage.BackupConfig
-import io.legado.app.help.storage.ImportOldData
-import io.legado.app.help.storage.Restore
-import io.legado.app.lib.dialogs.alert
-import io.legado.app.lib.dialogs.selector
-import io.legado.app.lib.permission.Permissions
-import io.legado.app.lib.permission.PermissionsCompat
-import io.legado.app.lib.prefs.fragment.PreferenceFragment
-import io.legado.app.ui.about.AppLogDialog
-import io.legado.app.ui.file.HandleFileContract
-import io.legado.app.ui.widget.dialog.WaitDialog
-import io.legado.app.utils.FileDoc
-import io.legado.app.utils.checkWrite
-import io.legado.app.utils.getPrefString
-import io.legado.app.utils.isContentScheme
-import io.legado.app.utils.launch
-import io.legado.app.utils.showDialogFragment
-import io.legado.app.utils.showHelp
-import io.legado.app.utils.toEditable
-import io.legado.app.utils.toastOnUi
-import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.Dispatchers.Main
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.ensureActive
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import splitties.init.appCtx
-import kotlin.coroutines.coroutineContext
-import androidx.core.content.edit
-import kotlinx.coroutines.currentCoroutineContext
-
-class BackupConfigFragment : PreferenceFragment(),
- SharedPreferences.OnSharedPreferenceChangeListener,
- MenuProvider {
-
- private val viewModel by activityViewModels()
- private val waitDialog by lazy { WaitDialog(requireContext()) }
- private var backupJob: Job? = null
- private var restoreJob: Job? = null
-
- private val selectBackupPath = registerForActivityResult(HandleFileContract()) {
- it.uri?.let { uri ->
- if (uri.isContentScheme()) {
- AppConfig.backupPath = uri.toString()
- } else {
- AppConfig.backupPath = uri.path
- }
- }
- }
- private val backupDir = registerForActivityResult(HandleFileContract()) { result ->
- result.uri?.let { uri ->
- if (uri.isContentScheme()) {
- AppConfig.backupPath = uri.toString()
- backup(uri.toString())
- } else {
- uri.path?.let { path ->
- AppConfig.backupPath = path
- backup(path)
- }
- }
- }
- }
- private val restoreDoc = registerForActivityResult(HandleFileContract()) {
- it.uri?.let { uri ->
- waitDialog.setText("恢复中…")
- waitDialog.show()
- val task = Coroutine.async {
- Restore.restore(appCtx, uri)
- }.onFinally {
- waitDialog.dismiss()
- }
- waitDialog.setOnCancelListener {
- task.cancel()
- }
- }
- }
- private val restoreOld = registerForActivityResult(HandleFileContract()) {
- it.uri?.let { uri ->
- ImportOldData.importUri(appCtx, uri)
- }
- }
-
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- addPreferencesFromResource(R.xml.pref_config_backup)
- findPreference(PreferKey.webDavDir)?.let {
- it.setOnBindEditTextListener { editText ->
- editText.text = AppConfig.webDavDir?.toEditable()
- }
- }
- findPreference(PreferKey.webDavDeviceName)?.let {
- it.setOnBindEditTextListener { editText ->
- editText.text = AppConfig.webDavDeviceName?.toEditable()
- }
- }
- upPreferenceSummary(PreferKey.webDavUrl, getPrefString(PreferKey.webDavUrl))
- upPreferenceSummary(PreferKey.webDavDir, AppConfig.webDavDir)
- upPreferenceSummary(PreferKey.webDavDeviceName, AppConfig.webDavDeviceName)
- upPreferenceSummary(PreferKey.backupPath, getPrefString(PreferKey.backupPath))
- findPreference("web_dav_restore")
- ?.onLongClick {
- restoreFromLocal()
- true
- }
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- activity?.setTitle(R.string.backup_restore)
- preferenceManager.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
- //listView.setEdgeEffectColor(primaryColor)
- activity?.addMenuProvider(this, viewLifecycleOwner)
- if (!LocalConfig.backupHelpVersionIsLast) {
- showHelp("webDavHelp")
- }
- }
-
- override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
- menuInflater.inflate(R.menu.backup_restore, menu)
- }
-
- override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
- when (menuItem.itemId) {
- R.id.menu_help -> {
- showHelp("webDavHelp")
- return true
- }
-
- R.id.menu_log -> showDialogFragment()
- }
- return false
- }
-
- override fun onDestroy() {
- super.onDestroy()
- preferenceManager.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
- }
-
- override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
- when (key) {
- PreferKey.backupPath -> upPreferenceSummary(key, getPrefString(key))
- PreferKey.webDavUrl,
- PreferKey.webDavAccount,
- PreferKey.webDavPassword,
- PreferKey.webDavDir -> listView.post {
- upPreferenceSummary(key, appCtx.getPrefString(key))
- viewModel.upWebDavConfig()
- }
-
- PreferKey.webDavDeviceName -> upPreferenceSummary(key, getPrefString(key))
- }
- }
-
- private fun upPreferenceSummary(preferenceKey: String, value: String?) {
- val preference = findPreference(preferenceKey) ?: return
- when (preferenceKey) {
- PreferKey.webDavUrl ->
- if (value.isNullOrBlank()) {
- preference.summary = getString(R.string.web_dav_url_s)
- } else {
- preference.summary = value
- }
-
- PreferKey.webDavDir -> preference.summary = when (value) {
- null -> "legado"
- else -> value
- }
-
- else -> {
- if (preference is ListPreference) {
- val index = preference.findIndexOfValue(value)
- // Set the summary to reflect the new value.
- preference.summary = if (index >= 0) preference.entries[index] else null
- } else {
- preference.summary = value
- }
- }
- }
- }
-
- override fun onPreferenceTreeClick(preference: Preference): Boolean {
- when (preference.key) {
- PreferKey.backupPath -> selectBackupPath.launch()
- PreferKey.restoreIgnore -> backupIgnore()
- "web_dav_test" -> {
- lifecycleScope.launch {
- testWebDav()
- }
- }
- "web_dav_auth" -> {
- showWebDavAuthDialog()
- }
- "web_dav_backup" -> backup()
- "web_dav_restore" -> restore()
- "import_old" -> restoreOld.launch()
- }
- return super.onPreferenceTreeClick(preference)
- }
-
- private fun showWebDavAuthDialog() {
- val binding = DialogWebdavAuthBinding.inflate(layoutInflater).apply {
- editAccount.setText(PreferenceManager.getDefaultSharedPreferences(requireContext()).getString(PreferKey.webDavAccount, ""))
- editPassword.setText(PreferenceManager.getDefaultSharedPreferences(requireContext()).getString(PreferKey.webDavPassword, ""))
- editPassword.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
- }
-
- alert(titleResource = R.string.web_dav_account) {
- customView { binding.root }
-
- okButton {
- val account = binding.editAccount.text.toString()
- val password = binding.editPassword.text.toString()
-
- PreferenceManager.getDefaultSharedPreferences(requireContext()).edit {
- putString(PreferKey.webDavAccount, account)
- .putString(PreferKey.webDavPassword, password)
- }
-
- upPreferenceSummary(PreferKey.webDavAccount, account)
- upPreferenceSummary(PreferKey.webDavPassword, password)
- lifecycleScope.launch {
- testWebDav()
- }
- }
-
- cancelButton()
- }
- }
-
- /**
- * 备份忽略设置
- */
- private fun backupIgnore() {
- val checkedItems = BooleanArray(BackupConfig.ignoreKeys.size) {
- BackupConfig.ignoreConfig[BackupConfig.ignoreKeys[it]] ?: false
- }
- alert(R.string.restore_ignore) {
- multiChoiceItems(BackupConfig.ignoreTitle, checkedItems) { _, which, isChecked ->
- BackupConfig.ignoreConfig[BackupConfig.ignoreKeys[which]] = isChecked
- }
- onDismiss {
- BackupConfig.saveIgnoreConfig()
- }
- }
- }
-
-
- fun backup() {
- val backupPath = AppConfig.backupPath
- if (backupPath.isNullOrEmpty()) {
- backupDir.launch()
- } else {
- if (backupPath.isContentScheme()) {
- lifecycleScope.launch {
- val canWrite = withContext(IO) {
- FileDoc.fromDir(backupPath).checkWrite()
- }
- if (canWrite) {
- backup(backupPath)
- } else {
- backupDir.launch()
- }
- }
- } else {
- backupUsePermission(backupPath)
- }
- }
- }
-
- private fun backup(backupPath: String) {
- waitDialog.setText("备份中…")
- waitDialog.setOnCancelListener {
- backupJob?.cancel()
- }
- waitDialog.show()
- backupJob?.cancel()
- backupJob = lifecycleScope.launch {
- try {
- Backup.backupLocked(requireContext(), backupPath)
- appCtx.toastOnUi(R.string.backup_success)
- } catch (e: Throwable) {
- ensureActive()
- AppLog.put("备份出错\n${e.localizedMessage}", e)
- appCtx.toastOnUi(
- appCtx.getString(
- R.string.backup_fail,
- e.localizedMessage
- )
- )
- } finally {
- ensureActive()
- waitDialog.dismiss()
- }
- }
- }
-
- private fun backupUsePermission(path: String) {
- PermissionsCompat.Builder()
- .addPermissions(*Permissions.Group.STORAGE)
- .rationale(R.string.tip_perm_request_storage)
- .onGranted {
- backup(path)
- }
- .request()
- }
-
- fun restore() {
- waitDialog.setText(R.string.loading)
- waitDialog.setOnCancelListener {
- restoreJob?.cancel()
- }
- waitDialog.show()
- Coroutine.async {
- restoreJob = coroutineContext[Job]
- showRestoreDialog(requireContext())
- }.onError {
- AppLog.put("恢复备份出错WebDavError\n${it.localizedMessage}", it)
- if (context == null) {
- return@onError
- }
- alert {
- setTitle(R.string.restore)
- setMessage("WebDavError\n${it.localizedMessage}\n将从本地备份恢复。")
- okButton {
- restoreFromLocal()
- }
- cancelButton()
- }
- }.onFinally {
- waitDialog.dismiss()
- }
- }
-
- private suspend fun showRestoreDialog(context: Context) {
- val names = withContext(IO) { AppWebDav.getBackupNames() }
- if (AppWebDav.isJianGuoYun && names.size > 700) {
- context.toastOnUi("由于坚果云限制列出文件数量,部分备份可能未显示,请及时清理旧备份")
- }
- if (names.isNotEmpty()) {
- currentCoroutineContext().ensureActive()
- withContext(Main) {
- context.selector(
- title = context.getString(R.string.select_restore_file),
- items = names
- ) { _, index ->
- if (index in 0 until names.size) {
- listView.post {
- restoreWebDav(names[index])
- }
- }
- }
- }
- } else {
- throw NoStackTraceException("Web dav no back up file")
- }
- }
-
- private fun restoreWebDav(name: String) {
- waitDialog.setText("恢复中…")
- waitDialog.show()
- val task = Coroutine.async {
- AppWebDav.restoreWebDav(name)
- }.onError {
- AppLog.put("WebDav恢复出错\n${it.localizedMessage}", it)
- appCtx.toastOnUi("WebDav恢复出错\n${it.localizedMessage}")
- }.onFinally {
- waitDialog.dismiss()
- }
- waitDialog.setOnCancelListener {
- task.cancel()
- }
- }
-
- private fun restoreFromLocal() {
- restoreDoc.launch {
- title = getString(R.string.select_restore_file)
- mode = HandleFileContract.FILE
- allowExtensions = arrayOf("zip")
- }
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- waitDialog.dismiss()
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/config/ConfigActivity.kt b/app/src/main/java/io/legado/app/ui/config/ConfigActivity.kt
deleted file mode 100644
index 98424c4e6..000000000
--- a/app/src/main/java/io/legado/app/ui/config/ConfigActivity.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package io.legado.app.ui.config
-
-import android.os.Bundle
-import androidx.activity.viewModels
-import androidx.fragment.app.Fragment
-import io.legado.app.R
-import io.legado.app.base.VMBaseActivity
-import io.legado.app.databinding.ActivityConfigBinding
-import io.legado.app.utils.viewbindingdelegate.viewBinding
-
-class ConfigActivity : VMBaseActivity() {
-
- override val binding by viewBinding(ActivityConfigBinding::inflate)
- override val viewModel by viewModels()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setSupportActionBar(binding.topBar)
- when (val configTag = intent.getStringExtra("configTag")) {
- ConfigTag.OTHER_CONFIG -> replaceFragment(configTag)
- ConfigTag.THEME_CONFIG -> replaceFragment(configTag)
- ConfigTag.BACKUP_CONFIG -> replaceFragment(configTag)
- ConfigTag.COVER_CONFIG -> replaceFragment(configTag)
- ConfigTag.READ_CONFIG -> replaceFragment(configTag)
- else -> finish()
- }
- }
-
- override fun setTitle(resId: Int) {
- super.setTitle(resId)
- binding.topBar.setTitle(resId)
- binding.topBar.setNavigationOnClickListener {
- onBackPressedDispatcher.onBackPressed()
- }
- }
-
- inline fun replaceFragment(configTag: String) {
- intent.putExtra("configTag", configTag)
- @Suppress("DEPRECATION")
- val configFragment = supportFragmentManager.findFragmentByTag(configTag)
- ?: T::class.java.newInstance()
- supportFragmentManager.beginTransaction()
- .replace(R.id.configFrameLayout, configFragment, configTag)
- .commit()
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/config/ConfigNavScreen.kt b/app/src/main/java/io/legado/app/ui/config/ConfigNavScreen.kt
new file mode 100644
index 000000000..89db7857f
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/config/ConfigNavScreen.kt
@@ -0,0 +1,78 @@
+package io.legado.app.ui.config
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import io.legado.app.R
+import io.legado.app.ui.widget.components.AppScaffold
+import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
+import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
+import io.legado.app.ui.widget.components.SplicedColumnGroup
+import io.legado.app.ui.widget.components.button.TopbarNavigationButton
+import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ConfigNavScreen(
+ onBackClick: () -> Unit,
+ onNavigateToOther: () -> Unit,
+ onNavigateToRead: () -> Unit,
+ onNavigateToCover: () -> Unit,
+ onNavigateToTheme: () -> Unit,
+ onNavigateToBackup: () -> Unit
+) {
+ val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
+
+ AppScaffold(
+ modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
+ topBar = {
+ GlassMediumFlexibleTopAppBar(
+ title = { Text(stringResource(R.string.setting)) },
+ scrollBehavior = scrollBehavior,
+ navigationIcon = {
+ TopbarNavigationButton(onClick = onBackClick)
+ }
+ )
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues)
+ .verticalScroll(rememberScrollState())
+ .padding(16.dp)
+ ) {
+ SplicedColumnGroup {
+ ClickableSettingItem(
+ title = stringResource(R.string.theme_setting),
+ onClick = onNavigateToTheme
+ )
+ ClickableSettingItem(
+ title = stringResource(R.string.other_setting),
+ onClick = onNavigateToOther
+ )
+ ClickableSettingItem(
+ title = stringResource(R.string.read_config),
+ onClick = onNavigateToRead
+ )
+ ClickableSettingItem(
+ title = stringResource(R.string.cover_config),
+ onClick = onNavigateToCover
+ )
+ ClickableSettingItem(
+ title = stringResource(R.string.backup_restore),
+ onClick = onNavigateToBackup
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/io/legado/app/ui/config/ConfigViewModel.kt b/app/src/main/java/io/legado/app/ui/config/ConfigViewModel.kt
deleted file mode 100644
index fa6372c2d..000000000
--- a/app/src/main/java/io/legado/app/ui/config/ConfigViewModel.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package io.legado.app.ui.config
-
-import android.app.Application
-import android.content.Context
-import io.legado.app.R
-import io.legado.app.base.BaseViewModel
-import io.legado.app.data.appDb
-import io.legado.app.help.AppWebDav
-import io.legado.app.help.book.BookHelp
-import io.legado.app.utils.FileUtils
-import io.legado.app.utils.restart
-import io.legado.app.utils.toastOnUi
-import kotlinx.coroutines.delay
-import splitties.init.appCtx
-
-class ConfigViewModel(application: Application) : BaseViewModel(application) {
-
- fun upWebDavConfig() {
- execute {
- AppWebDav.upConfig()
- }
- }
-
- fun clearCache() {
- execute {
- BookHelp.clearCache()
- FileUtils.delete(context.cacheDir.absolutePath)
- }.onSuccess {
- context.toastOnUi(R.string.clear_cache_success)
- }
- }
-
- fun clearWebViewData() {
- execute {
- FileUtils.delete(context.getDir("webview", Context.MODE_PRIVATE))
- FileUtils.delete(context.getDir("hws_webview", Context.MODE_PRIVATE), true)
- context.toastOnUi(R.string.clear_webview_data_success)
- delay(3000)
- appCtx.restart()
- }
- }
-
- fun shrinkDatabase() {
- execute {
- appDb.openHelper.writableDatabase.execSQL("VACUUM")
- }.onSuccess {
- context.toastOnUi(R.string.success)
- }
- }
-
-}
diff --git a/app/src/main/java/io/legado/app/ui/config/CoverConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/CoverConfigFragment.kt
deleted file mode 100644
index 97f2eec62..000000000
--- a/app/src/main/java/io/legado/app/ui/config/CoverConfigFragment.kt
+++ /dev/null
@@ -1,168 +0,0 @@
-package io.legado.app.ui.config
-
-import android.annotation.SuppressLint
-import android.content.SharedPreferences
-import android.net.Uri
-import android.os.Bundle
-import android.view.View
-import androidx.preference.Preference
-import io.legado.app.R
-import io.legado.app.constant.PreferKey
-import io.legado.app.lib.dialogs.selector
-import io.legado.app.lib.prefs.SwitchPreference
-import io.legado.app.lib.prefs.fragment.PreferenceFragment
-//import io.legado.app.lib.theme.primaryColor
-import io.legado.app.model.BookCover
-import io.legado.app.utils.*
-import splitties.init.appCtx
-import java.io.FileOutputStream
-
-class CoverConfigFragment : PreferenceFragment(),
- SharedPreferences.OnSharedPreferenceChangeListener {
-
- private val requestCodeCover = 111
- private val requestCodeCoverDark = 112
- private val selectImage = registerForActivityResult(SelectImageContract()) {
- it.uri?.let { uri ->
- when (it.requestCode) {
- requestCodeCover -> setCoverFromUri(PreferKey.defaultCover, uri)
- requestCodeCoverDark -> setCoverFromUri(PreferKey.defaultCoverDark, uri)
- }
- }
- }
-
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- addPreferencesFromResource(R.xml.pref_config_cover)
- upPreferenceSummary(PreferKey.defaultCover, getPrefString(PreferKey.defaultCover))
- upPreferenceSummary(PreferKey.defaultCoverDark, getPrefString(PreferKey.defaultCoverDark))
- findPreference(PreferKey.coverShowAuthor)
- ?.isEnabled = getPrefBoolean(PreferKey.coverShowName)
- findPreference(PreferKey.coverShowAuthorN)
- ?.isEnabled = getPrefBoolean(PreferKey.coverShowNameN)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- activity?.setTitle(R.string.cover_config)
- //listView.setEdgeEffectColor(primaryColor)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- preferenceManager.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
- }
-
- override fun onDestroy() {
- super.onDestroy()
- preferenceManager.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
- }
-
- override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
- sharedPreferences ?: return
- when (key) {
- PreferKey.defaultCover,
- PreferKey.defaultCoverDark -> {
- upPreferenceSummary(key, getPrefString(key))
- }
- PreferKey.coverShowName -> {
- findPreference(PreferKey.coverShowAuthor)
- ?.isEnabled = getPrefBoolean(key)
- BookCover.upDefaultCover()
- }
- PreferKey.coverShowNameN -> {
- findPreference(PreferKey.coverShowAuthorN)
- ?.isEnabled = getPrefBoolean(key)
- BookCover.upDefaultCover()
- }
- PreferKey.coverTextColor,
- PreferKey.coverTextColorN,
- PreferKey.coverShadowColor,
- PreferKey.coverShadowColorN,
- PreferKey.coverDefaultColor,
- PreferKey.coverShowShadow,
- PreferKey.coverShowStroke,
- PreferKey.coverShowAuthor,
- PreferKey.coverShowAuthorN -> {
- BookCover.upDefaultCover()
- }
- }
- }
-
- @SuppressLint("PrivateResource")
- override fun onPreferenceTreeClick(preference: Preference): Boolean {
- when (preference.key) {
- "coverRule" -> showDialogFragment(CoverRuleConfigDialog())
- PreferKey.defaultCover ->
- if (getPrefString(preference.key).isNullOrEmpty()) {
- selectImage.launch(requestCodeCover)
- } else {
- context?.selector(
- items = arrayListOf(
- getString(R.string.delete),
- getString(R.string.select_image)
- )
- ) { _, i ->
- if (i == 0) {
- removePref(preference.key)
- BookCover.upDefaultCover()
- } else {
- selectImage.launch(requestCodeCover)
- }
- }
- }
- PreferKey.defaultCoverDark ->
- if (getPrefString(preference.key).isNullOrEmpty()) {
- selectImage.launch(requestCodeCoverDark)
- } else {
- context?.selector(
- items = arrayListOf(
- getString(R.string.delete),
- getString(R.string.select_image)
- )
- ) { _, i ->
- if (i == 0) {
- removePref(preference.key)
- BookCover.upDefaultCover()
- } else {
- selectImage.launch(requestCodeCoverDark)
- }
- }
- }
- }
- return super.onPreferenceTreeClick(preference)
- }
-
- private fun upPreferenceSummary(preferenceKey: String, value: String?) {
- val preference = findPreference(preferenceKey) ?: return
- when (preferenceKey) {
- PreferKey.defaultCover,
- PreferKey.defaultCoverDark -> preference.summary = if (value.isNullOrBlank()) {
- getString(R.string.select_image)
- } else {
- value
- }
- else -> preference.summary = value
- }
- }
-
- private fun setCoverFromUri(preferenceKey: String, uri: Uri) {
- readUri(uri) { fileDoc, inputStream ->
- kotlin.runCatching {
- var file = requireContext().externalFiles
- val suffix = fileDoc.name.substringAfterLast(".")
- val fileName = uri.inputStream(requireContext()).getOrThrow().use {
- MD5Utils.md5Encode(it) + ".$suffix"
- }
- file = FileUtils.createFileIfNotExist(file, "covers", fileName)
- FileOutputStream(file).use {
- inputStream.copyTo(it)
- }
- putPrefString(preferenceKey, file.absolutePath)
- BookCover.upDefaultCover()
- }.onFailure {
- appCtx.toastOnUi(it.localizedMessage)
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt
deleted file mode 100644
index 6bfbc71d6..000000000
--- a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt
+++ /dev/null
@@ -1,401 +0,0 @@
-package io.legado.app.ui.config
-
-//import io.legado.app.lib.theme.primaryColor
-import android.annotation.SuppressLint
-import android.content.ComponentName
-import android.content.Context
-import android.content.SharedPreferences
-import android.content.pm.PackageManager
-import android.os.Build
-import android.os.Bundle
-import android.os.PowerManager
-import android.view.View
-import androidx.core.content.ContextCompat
-import androidx.core.view.postDelayed
-import androidx.fragment.app.activityViewModels
-import androidx.preference.ListPreference
-import androidx.preference.Preference
-import com.jeremyliao.liveeventbus.LiveEventBus
-import io.legado.app.R
-import io.legado.app.constant.PreferKey
-import io.legado.app.databinding.DialogEditTextBinding
-import io.legado.app.help.AppFreezeMonitor
-import io.legado.app.help.DispatchersMonitor
-import io.legado.app.help.config.AppConfig
-import io.legado.app.help.config.LocalConfig
-import io.legado.app.lib.dialogs.alert
-import io.legado.app.lib.permission.Permissions
-import io.legado.app.lib.permission.PermissionsCompat
-import io.legado.app.lib.prefs.fragment.PreferenceFragment
-import io.legado.app.model.CheckSource
-import io.legado.app.model.ImageProvider
-import io.legado.app.receiver.SharedReceiverActivity
-import io.legado.app.service.WebService
-import io.legado.app.ui.file.HandleFileContract
-import io.legado.app.ui.widget.number.NumberPickerDialog
-import io.legado.app.utils.FirebaseManager
-import io.legado.app.utils.LogUtils
-import io.legado.app.utils.getPrefBoolean
-import io.legado.app.utils.postEvent
-import io.legado.app.utils.putPrefBoolean
-import io.legado.app.utils.putPrefString
-import io.legado.app.utils.removePref
-import io.legado.app.utils.restart
-import io.legado.app.utils.showDialogFragment
-import io.legado.app.utils.startActivity
-import splitties.init.appCtx
-
-/**
- * 其它设置
- */
-class OtherConfigFragment : PreferenceFragment(),
- SharedPreferences.OnSharedPreferenceChangeListener {
-
- private val viewModel by activityViewModels()
- private val packageManager = appCtx.packageManager
- private val componentName = ComponentName(
- appCtx,
- SharedReceiverActivity::class.java.name
- )
- private val localBookTreeSelect = registerForActivityResult(HandleFileContract()) {
- it.uri?.let { treeUri ->
- AppConfig.defaultBookTreeUri = treeUri.toString()
- }
- }
-
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- putPrefBoolean(PreferKey.processText, isProcessTextEnabled())
- addPreferencesFromResource(R.xml.pref_config_other)
- upPreferenceSummary(PreferKey.userAgent, AppConfig.userAgent)
- upPreferenceSummary(PreferKey.preDownloadNum, AppConfig.preDownloadNum.toString())
- upPreferenceSummary(PreferKey.threadCount, AppConfig.threadCount.toString())
- upPreferenceSummary(PreferKey.webPort, AppConfig.webPort.toString())
- AppConfig.defaultBookTreeUri?.let {
- upPreferenceSummary(PreferKey.defaultBookTreeUri, it)
- }
- upPreferenceSummary(PreferKey.checkSource, CheckSource.summary)
- upPreferenceSummary(PreferKey.bitmapCacheSize, AppConfig.bitmapCacheSize.toString())
- upPreferenceSummary(PreferKey.imageRetainNum, AppConfig.imageRetainNum.toString())
- upPreferenceSummary(PreferKey.sourceEditMaxLine, AppConfig.sourceEditMaxLine.toString())
- updatePermissionSummary()
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- activity?.setTitle(R.string.other_setting)
- preferenceManager.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
- //listView.setEdgeEffectColor(primaryColor)
- }
-
- override fun onDestroy() {
- super.onDestroy()
- preferenceManager.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
- }
-
- override fun onResume() {
- super.onResume()
- updatePermissionSummary()
- }
-
- override fun onPreferenceTreeClick(preference: Preference): Boolean {
- when (preference.key) {
- PreferKey.userAgent -> showUserAgentDialog()
- PreferKey.defaultBookTreeUri -> localBookTreeSelect.launch {
- title = getString(R.string.select_book_folder)
- mode = HandleFileContract.DIR_SYS
- }
-
- PreferKey.preDownloadNum -> NumberPickerDialog(requireContext())
- .setTitle(getString(R.string.pre_download))
- .setMaxValue(9999)
- .setMinValue(0)
- .setValue(AppConfig.preDownloadNum)
- .show {
- AppConfig.preDownloadNum = it
- }
-
- PreferKey.threadCount -> NumberPickerDialog(requireContext())
- .setTitle(getString(R.string.threads_num_title))
- .setMaxValue(999)
- .setMinValue(1)
- .setValue(AppConfig.threadCount)
- .show {
- AppConfig.threadCount = it
- }
-
- PreferKey.webPort -> NumberPickerDialog(requireContext())
- .setTitle(getString(R.string.web_port_title))
- .setMaxValue(60000)
- .setMinValue(1024)
- .setValue(AppConfig.webPort)
- .show {
- AppConfig.webPort = it
- }
-
- PreferKey.cleanCache -> clearCache()
- PreferKey.uploadRule -> showDialogFragment()
- PreferKey.checkSource -> showDialogFragment()
- PreferKey.bitmapCacheSize -> {
- NumberPickerDialog(requireContext())
- .setTitle(getString(R.string.bitmap_cache_size))
- .setMaxValue(2047)
- .setMinValue(1)
- .setValue(AppConfig.bitmapCacheSize)
- .show {
- AppConfig.bitmapCacheSize = it
- ImageProvider.bitmapLruCache.resize(ImageProvider.cacheSize)
- }
- }
- PreferKey.imageRetainNum -> NumberPickerDialog(requireContext())
- .setTitle(getString(R.string.image_retain_number))
- .setMaxValue(999)
- .setMinValue(0)
- .setValue(AppConfig.imageRetainNum)
- .show {
- AppConfig.imageRetainNum = it
- }
-
- PreferKey.sourceEditMaxLine -> {
- NumberPickerDialog(requireContext())
- .setTitle(getString(R.string.source_edit_text_max_line))
- .setMaxValue(Int.MAX_VALUE)
- .setMinValue(10)
- .setValue(AppConfig.sourceEditMaxLine)
- .show {
- AppConfig.sourceEditMaxLine = it
- }
- }
-
- PreferKey.clearWebViewData -> clearWebViewData()
- "localPassword" -> alertLocalPassword()
- PreferKey.shrinkDatabase -> shrinkDatabase()
- "TEST" -> startActivity()
- PreferKey.notificationsPost -> checkPermission(1)
- PreferKey.ignoreBatteryPermission -> checkPermission(2)
- }
- return super.onPreferenceTreeClick(preference)
- }
-
- override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
- when (key) {
- PreferKey.preDownloadNum -> {
- upPreferenceSummary(key, AppConfig.preDownloadNum.toString())
- }
-
- PreferKey.threadCount -> {
- upPreferenceSummary(key, AppConfig.threadCount.toString())
- postEvent(PreferKey.threadCount, "")
- }
-
- PreferKey.webPort -> {
- upPreferenceSummary(key, AppConfig.webPort.toString())
- if (WebService.isRun) {
- WebService.stop(requireContext())
- WebService.start(requireContext())
- }
- }
-
- PreferKey.defaultBookTreeUri -> {
- upPreferenceSummary(key, AppConfig.defaultBookTreeUri)
- }
-
- PreferKey.recordLog -> {
- AppConfig.recordLog = appCtx.getPrefBoolean(PreferKey.recordLog)
- LogUtils.upLevel()
- LogUtils.logDeviceInfo()
- LiveEventBus.config().enableLogger(AppConfig.recordLog)
- AppFreezeMonitor.init(appCtx)
- DispatchersMonitor.init()
- }
-
- PreferKey.processText -> sharedPreferences?.let {
- setProcessTextEnable(it.getBoolean(key, true))
- }
-
- PreferKey.language -> listView.postDelayed(1000) {
- appCtx.restart()
- }
-
- PreferKey.userAgent -> listView.post {
- upPreferenceSummary(PreferKey.userAgent, AppConfig.userAgent)
- }
-
- PreferKey.checkSource -> listView.post {
- upPreferenceSummary(PreferKey.checkSource, CheckSource.summary)
- }
-
- PreferKey.bitmapCacheSize -> {
- upPreferenceSummary(key, AppConfig.bitmapCacheSize.toString())
- }
-
- PreferKey.imageRetainNum -> {
- upPreferenceSummary(key, AppConfig.imageRetainNum.toString())
- }
-
- PreferKey.sourceEditMaxLine -> {
- upPreferenceSummary(key, AppConfig.sourceEditMaxLine.toString())
- }
-
- PreferKey.firebaseEnable -> {
- sharedPreferences?.let {
- FirebaseManager.setEnabled(requireContext(), it.getBoolean(key, true))
- }
- }
-
- }
- }
- private fun updatePermissionSummary() {
- val notificationsGranted: Boolean =
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- ContextCompat.checkSelfPermission(
- requireContext(), android.Manifest.permission.POST_NOTIFICATIONS
- ) == PackageManager.PERMISSION_GRANTED
- } else {
- true
- }
- upPreferenceSummary(
- PreferKey.notificationsPost,
- if (notificationsGranted) "已获取" else "未获取"
- )
-
- val pm = requireContext().getSystemService(Context.POWER_SERVICE) as PowerManager
- val batteryGranted: Boolean =
- pm.isIgnoringBatteryOptimizations(requireContext().packageName)
-
- upPreferenceSummary(
- PreferKey.ignoreBatteryPermission,
- if (batteryGranted) "已获取" else "未获取"
- )
- }
-
-
- private fun upPreferenceSummary(preferenceKey: String, value: String?) {
- val preference = findPreference(preferenceKey) ?: return
- when (preferenceKey) {
- PreferKey.preDownloadNum -> preference.summary =
- getString(R.string.pre_download_s, value)
-
- PreferKey.threadCount -> preference.summary = getString(R.string.threads_num, value)
- PreferKey.webPort -> preference.summary = getString(R.string.web_port_summary, value)
- PreferKey.bitmapCacheSize -> preference.summary =
- getString(R.string.bitmap_cache_size_summary, value)
- PreferKey.imageRetainNum -> preference.summary =
- getString(R.string.image_retain_number_summary, value)
-
- PreferKey.sourceEditMaxLine -> preference.summary =
- getString(R.string.source_edit_max_line_summary, value)
-
- else -> if (preference is ListPreference) {
- val index = preference.findIndexOfValue(value)
- // Set the summary to reflect the new value.
- preference.summary = if (index >= 0) preference.entries[index] else null
- } else {
- preference.summary = value
- }
- }
- }
-
- private fun checkPermission(int: Int) {
- when (int) {
- 1 -> {
- PermissionsCompat.Builder()
- .addPermissions(Permissions.POST_NOTIFICATIONS)
- .rationale(R.string.notification_permission_rationale)
- .onGranted { updatePermissionSummary() }
- .request()
- }
- 2 -> {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- PermissionsCompat.Builder()
- .addPermissions(Permissions.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
- .rationale(R.string.ignore_battery_permission_rationale)
- .request()
- }
- }
- }
- }
-
- @SuppressLint("InflateParams")
- private fun showUserAgentDialog() {
- alert(getString(R.string.user_agent)) {
- val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
- editView.hint = getString(R.string.user_agent)
- editView.setText(AppConfig.userAgent)
- }
- customView { alertBinding.root }
- okButton {
- val userAgent = alertBinding.editView.text?.toString()
- if (userAgent.isNullOrBlank()) {
- removePref(PreferKey.userAgent)
- } else {
- putPrefString(PreferKey.userAgent, userAgent)
- }
- }
- cancelButton()
- }
- }
-
- private fun clearCache() {
- requireContext().alert(
- titleResource = R.string.clear_cache,
- messageResource = R.string.sure_del
- ) {
- okButton {
- viewModel.clearCache()
- }
- noButton()
- }
- }
-
- private fun shrinkDatabase() {
- alert(R.string.sure, R.string.shrink_database) {
- okButton {
- viewModel.shrinkDatabase()
- }
- noButton()
- }
- }
-
- private fun clearWebViewData() {
- alert(R.string.clear_webview_data, R.string.sure_del) {
- okButton {
- viewModel.clearWebViewData()
- }
- noButton()
- }
- }
-
- private fun isProcessTextEnabled(): Boolean {
- return packageManager.getComponentEnabledSetting(componentName) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED
- }
-
- private fun setProcessTextEnable(enable: Boolean) {
- if (enable) {
- packageManager.setComponentEnabledSetting(
- componentName,
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP
- )
- } else {
- packageManager.setComponentEnabledSetting(
- componentName,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP
- )
- }
- }
-
- private fun alertLocalPassword() {
- context?.alert(R.string.set_local_password, R.string.set_local_password_summary) {
- val editTextBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
- editView.hint = "password"
- }
- customView {
- editTextBinding.root
- }
- okButton {
- LocalConfig.password = editTextBinding.editView.text.toString()
- }
- cancelButton()
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/config/ReadConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/ReadConfigFragment.kt
deleted file mode 100644
index e8677ba4a..000000000
--- a/app/src/main/java/io/legado/app/ui/config/ReadConfigFragment.kt
+++ /dev/null
@@ -1,175 +0,0 @@
-package io.legado.app.ui.config
-
-import android.annotation.SuppressLint
-import android.content.SharedPreferences
-import android.os.Bundle
-import android.view.View
-import android.view.ViewConfiguration
-import androidx.preference.Preference
-import androidx.preference.PreferenceFragmentCompat
-import io.legado.app.R
-import io.legado.app.constant.EventBus
-import io.legado.app.constant.PreferKey
-import io.legado.app.help.config.AppConfig
-import io.legado.app.help.config.ReadBookConfig
-import io.legado.app.model.ReadBook
-import io.legado.app.ui.book.read.ReadBookActivity
-import io.legado.app.ui.book.read.config.PageKeyDialog
-import io.legado.app.ui.book.read.page.provider.ChapterProvider
-import io.legado.app.ui.widget.number.NumberPickerDialog
-import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
-import io.legado.app.utils.getPrefBoolean
-import io.legado.app.utils.postEvent
-import io.legado.app.utils.removePref
-
-class ReadConfigFragment : PreferenceFragmentCompat(),
- SharedPreferences.OnSharedPreferenceChangeListener {
-
- private val slopSquare by lazy { ViewConfiguration.get(requireContext()).scaledTouchSlop }
-
- @SuppressLint("RestrictedApi")
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- addPreferencesFromResource(R.xml.pref_config_read)
- upPreferenceSummary(PreferKey.menuAlpha, AppConfig.menuAlpha.toString())
- upPreferenceSummary(PreferKey.pageTouchSlop, slopSquare.toString())
- if (!CanvasRecorderFactory.isSupport) {
- removePref(PreferKey.optimizeRender)
- preferenceScreen.removePreferenceRecursively(PreferKey.optimizeRender)
- }
- val pref = findPreference("clickRegionalConfig")
- pref?.isEnabled = false
-
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- activity?.setTitle(R.string.read)
- }
-
- override fun onResume() {
- super.onResume()
- preferenceManager
- .sharedPreferences
- ?.registerOnSharedPreferenceChangeListener(this)
- }
-
- override fun onPause() {
- preferenceManager
- .sharedPreferences
- ?.unregisterOnSharedPreferenceChangeListener(this)
- super.onPause()
- }
-
- override fun onSharedPreferenceChanged(
- sharedPreferences: SharedPreferences?,
- key: String?
- ) {
- when (key) {
- PreferKey.readBodyToLh -> activity?.recreate()
- PreferKey.hideStatusBar -> {
- ReadBookConfig.hideStatusBar = getPrefBoolean(PreferKey.hideStatusBar)
- postEvent(EventBus.UP_CONFIG, arrayListOf(0, 2))
- }
-
- PreferKey.hideNavigationBar -> {
- ReadBookConfig.hideNavigationBar = getPrefBoolean(PreferKey.hideNavigationBar)
- postEvent(EventBus.UP_CONFIG, arrayListOf(0, 2))
- }
-
- PreferKey.keepLight -> postEvent(key, true)
- PreferKey.readSliderMode -> postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
- PreferKey.textSelectAble -> postEvent(key, getPrefBoolean(key))
- PreferKey.screenOrientation -> {
- (activity as? ReadBookActivity)?.setOrientation()
- }
-
- PreferKey.textFullJustify,
- PreferKey.textBottomJustify,
- PreferKey.useZhLayout -> {
- postEvent(EventBus.UP_CONFIG, arrayListOf(5))
- }
-
- PreferKey.showBrightnessView -> {
- postEvent(PreferKey.showBrightnessView, "")
- }
-
- PreferKey.expandTextMenu -> {
- (activity as? ReadBookActivity)?.textActionMenu?.upMenu()
- }
-
- PreferKey.doublePageHorizontal -> {
- ChapterProvider.upLayout()
- ReadBook.loadContent(false)
- }
-
- PreferKey.showReadTitleAddition,
- PreferKey.readBarStyleFollowPage -> {
- postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
- }
-
- PreferKey.progressBarBehavior -> {
- postEvent(EventBus.UP_SEEK_BAR, true)
- }
-
- PreferKey.noAnimScrollPage -> {
- ReadBook.callBack?.upPageAnim()
- }
-
- PreferKey.optimizeRender -> {
- ChapterProvider.upStyle()
- ReadBook.callBack?.upPageAnim(true)
- ReadBook.loadContent(false)
- }
-
- PreferKey.paddingDisplayCutouts -> {
- postEvent(EventBus.UP_CONFIG, arrayListOf(2))
- }
- }
- }
-
- override fun onPreferenceTreeClick(preference: Preference): Boolean {
- when (preference.key) {
- "customPageKey" -> PageKeyDialog(requireContext()).show()
- "clickRegionalConfig" -> {
- (activity as? ReadBookActivity)?.showClickRegionalConfig()
- }
-
- PreferKey.menuAlpha -> {
- NumberPickerDialog(requireContext())
- .setTitle(getString(R.string.menu_alpha))
- .setMaxValue(100)
- .setMinValue(0)
- .setValue(AppConfig.menuAlpha)
- .show {
- AppConfig.menuAlpha = it
- upPreferenceSummary(PreferKey.menuAlpha, it.toString())
- postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
- }
- }
-
- PreferKey.pageTouchSlop -> {
- NumberPickerDialog(requireContext())
- .setTitle(getString(R.string.page_touch_slop_dialog_title))
- .setMaxValue(9999)
- .setMinValue(0)
- .setValue(AppConfig.pageTouchSlop)
- .show {
- AppConfig.pageTouchSlop = it
- postEvent(EventBus.UP_CONFIG, arrayListOf(4))
- }
- }
- }
- return super.onPreferenceTreeClick(preference)
- }
-
- @Suppress("SameParameterValue")
- private fun upPreferenceSummary(preferenceKey: String, value: String?) {
- val preference = findPreference(preferenceKey) ?: return
- when (preferenceKey) {
- PreferKey.menuAlpha -> preference.summary =
- getString(R.string.menu_alpha_sum, AppConfig.menuAlpha)
- PreferKey.pageTouchSlop -> preference.summary =
- getString(R.string.page_touch_slop_summary, value)
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/config/TestConfigActivity.kt b/app/src/main/java/io/legado/app/ui/config/TestConfigActivity.kt
deleted file mode 100644
index 8d1642680..000000000
--- a/app/src/main/java/io/legado/app/ui/config/TestConfigActivity.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-package io.legado.app.ui.config
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.input.nestedscroll.nestedScroll
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
-import androidx.navigation.compose.rememberNavController
-import io.legado.app.R
-import io.legado.app.base.BaseComposeActivity
-import io.legado.app.ui.config.backupConfig.BackupConfigScreen
-import io.legado.app.ui.config.coverConfig.CoverConfigScreen
-import io.legado.app.ui.config.otherConfig.OtherConfigScreen
-import io.legado.app.ui.config.readConfig.ReadConfigScreen
-import io.legado.app.ui.config.themeConfig.ThemeConfigScreen
-import io.legado.app.ui.widget.components.AppScaffold
-import io.legado.app.ui.widget.components.GlassMediumFlexibleTopAppBar
-import io.legado.app.ui.widget.components.GlassTopAppBarDefaults
-import io.legado.app.ui.widget.components.SplicedColumnGroup
-import io.legado.app.ui.widget.components.button.TopbarNavigationButton
-import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
-import kotlinx.serialization.Serializable
-
-class TestConfigActivity : BaseComposeActivity() {
-
- @Serializable
- object ConfigNavRoute
-
- @Serializable
- object OtherConfigRoute
-
- @Serializable
- object ReadConfigRoute
-
- @Serializable
- object CoverConfigRoute
-
- @Serializable
- object ThemeConfigRoute
-
- @Serializable
- object BackupConfigRoute
-
- @Composable
- override fun Content() {
- val navController = rememberNavController()
-
- NavHost(
- navController = navController,
- startDestination = ConfigNavRoute
- ) {
- composable {
- ConfigNavScreen(
- onBackClick = { finish() },
- onNavigateToOther = { navController.navigate(OtherConfigRoute) },
- onNavigateToRead = { navController.navigate(ReadConfigRoute) },
- onNavigateToCover = { navController.navigate(CoverConfigRoute) },
- onNavigateToTheme = { navController.navigate(ThemeConfigRoute) },
- onNavigateToBackup = { navController.navigate(BackupConfigRoute) }
- )
- }
-
- composable {
- OtherConfigScreen(onBackClick = { navController.popBackStack() })
- }
-
- composable {
- ReadConfigScreen(onBackClick = { navController.popBackStack() })
- }
-
- composable {
- CoverConfigScreen(onBackClick = { navController.popBackStack() })
- }
-
- composable {
- ThemeConfigScreen(
- onBackClick = { navController.popBackStack() }
- )
- }
-
- composable {
- BackupConfigScreen(onBackClick = { navController.popBackStack() })
- }
- }
- }
-
- @OptIn(ExperimentalMaterial3Api::class)
- @Composable
- fun ConfigNavScreen(
- onBackClick: () -> Unit,
- onNavigateToOther: () -> Unit,
- onNavigateToRead: () -> Unit,
- onNavigateToCover: () -> Unit,
- onNavigateToTheme: () -> Unit,
- onNavigateToBackup: () -> Unit
- ) {
- val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
-
- AppScaffold(
- modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
- topBar = {
- GlassMediumFlexibleTopAppBar(
- title = { Text("设置") },
- scrollBehavior = scrollBehavior,
- navigationIcon = {
- TopbarNavigationButton(onClick = onBackClick)
- }
- )
- }
- ) { paddingValues ->
- Column(
- modifier = Modifier
- .fillMaxSize()
- .padding(paddingValues)
- .verticalScroll(rememberScrollState())
- .padding(16.dp)
- ) {
- SplicedColumnGroup {
- ClickableSettingItem(
- title = stringResource(R.string.theme_setting),
- onClick = onNavigateToTheme
- )
- ClickableSettingItem(
- title = stringResource(R.string.other_setting),
- onClick = onNavigateToOther
- )
- ClickableSettingItem(
- title = stringResource(R.string.read_config),
- onClick = onNavigateToRead
- )
- ClickableSettingItem(
- title = stringResource(R.string.cover_config),
- onClick = onNavigateToCover
- )
- ClickableSettingItem(
- title = stringResource(R.string.backup_restore),
- onClick = onNavigateToBackup
- )
- }
- }
- }
- }
-
-}
diff --git a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
deleted file mode 100644
index 93b164981..000000000
--- a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
+++ /dev/null
@@ -1,479 +0,0 @@
-package io.legado.app.ui.config
-
-//import io.legado.app.lib.theme.primaryColor
-import android.annotation.SuppressLint
-import android.app.Application
-import android.content.SharedPreferences
-import android.net.Uri
-import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
-import android.view.View
-import android.widget.SeekBar
-import androidx.preference.Preference
-import androidx.preference.PreferenceFragmentCompat
-import com.google.android.material.color.DynamicColors
-import com.google.android.material.color.DynamicColorsOptions
-import io.legado.app.R
-import io.legado.app.base.AppContextWrapper
-import io.legado.app.constant.EventBus
-import io.legado.app.constant.PreferKey
-import io.legado.app.databinding.DialogEditTextBinding
-import io.legado.app.databinding.DialogImageBlurringBinding
-import io.legado.app.help.LauncherIconHelp
-import io.legado.app.help.config.AppConfig
-import io.legado.app.help.config.OldThemeConfig
-import io.legado.app.lib.dialogs.alert
-import io.legado.app.lib.dialogs.selector
-import io.legado.app.lib.prefs.ColorPreference
-import io.legado.app.lib.prefs.ImagePreviewPreference
-import io.legado.app.lib.prefs.NameListPreference
-import io.legado.app.lib.prefs.ThemeCardPreference
-import io.legado.app.lib.prefs.ThemeModePreference
-import io.legado.app.lib.theme.ThemeStore
-import io.legado.app.lib.theme.primaryColor
-import io.legado.app.ui.config.themeConfig.ThemeConfig
-import io.legado.app.ui.widget.number.NumberPickerDialog
-import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
-import io.legado.app.utils.ColorUtils
-import io.legado.app.utils.FileUtils
-import io.legado.app.utils.MD5Utils
-import io.legado.app.utils.SelectImageContract
-import io.legado.app.utils.externalFiles
-import io.legado.app.utils.getPrefInt
-import io.legado.app.utils.getPrefString
-import io.legado.app.utils.inputStream
-import io.legado.app.utils.postEvent
-import io.legado.app.utils.putPrefInt
-import io.legado.app.utils.putPrefString
-import io.legado.app.utils.readUri
-import io.legado.app.utils.removePref
-import io.legado.app.utils.restart
-import io.legado.app.utils.startActivity
-import io.legado.app.utils.toastOnUi
-import splitties.init.appCtx
-import java.io.FileOutputStream
-
-
-@Suppress("SameParameterValue")
-class ThemeConfigFragment : PreferenceFragmentCompat(),
- SharedPreferences.OnSharedPreferenceChangeListener
- //MenuProvider
- {
- private val requestCodeBgLight = 121
- private val requestCodeBgDark = 122
-
- private val requestCodeColorImage = 123
-
- private val selectImage = registerForActivityResult(SelectImageContract()) {
- it.uri?.let { uri ->
- when (it.requestCode) {
- requestCodeBgLight -> setBgFromUri(uri, PreferKey.bgImage) {
- upTheme(false)
- }
-
- requestCodeBgDark -> setBgFromUri(uri, PreferKey.bgImageN) {
- upTheme(true)
- }
-
- requestCodeColorImage -> setBgFromUri(uri, PreferKey.colorImage) {
-
- }
- }
- }
- }
-
- override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
- addPreferencesFromResource(R.xml.pref_config_theme)
-
- upPreferenceSummary(PreferKey.fontScale)
- upPreferenceSummary(PreferKey.containerOpacity)
-
- findPreference(PreferKey.cBackground)?.let {
- it.onSaveColor = { color ->
- if (!ColorUtils.isColorLight(color)) {
- toastOnUi(R.string.day_background_too_dark)
- true
- } else {
- false
- }
- }
- }
-
- findPreference(PreferKey.cNBackground)?.let {
- it.onSaveColor = { color ->
- if (ColorUtils.isColorLight(color)) {
- toastOnUi(R.string.night_background_too_light)
- true
- } else {
- false
- }
- }
- }
-
- findPreference(PreferKey.themeMode)?.let {
- it.setOnPreferenceChangeListener { _, _ ->
- true
- }
- }
-
- val themePref = findPreference(PreferKey.themePref)
- val colorImage = findPreference(PreferKey.colorImage)
- val colorPrimary = findPreference("colorPrimary")
- val customMode = findPreference("customMode")
- val currentTheme = getPrefString("app_theme")
-
- colorPrimary?.isVisible = currentTheme == "12"
- colorImage?.isVisible = currentTheme == "12"
- customMode?.isVisible = currentTheme == "12"
-
- themePref?.let {
- it.setOnPreferenceChangeListener { _, _ ->
- true
- }
- }
-
- themePref?.setOnPreferenceChangeListener { _, newValue ->
- colorPrimary?.isVisible = newValue == "12"
- colorImage?.isVisible = newValue == "12"
- customMode?.isVisible = newValue == "12"
- true
- }
-
- // TODO:删掉种子色了喵
- val hasColorImage = !getPrefString(PreferKey.colorImage).isNullOrBlank()
- colorPrimary?.isEnabled = !hasColorImage
- if (hasColorImage)
- {
- upPreferenceSummary("colorPrimary", getString(R.string.seed_photo_alart))
- upPreferenceSummary(PreferKey.colorImage, getString(R.string.click_to_delete))
- }
- upPreferenceSummary(PreferKey.bgImage)
- upPreferenceSummary(PreferKey.bgImageN)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- activity?.setTitle(R.string.theme_setting)
- //listView.setEdgeEffectColor(primaryColor)
- //activity?.addMenuProvider(this, viewLifecycleOwner)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- preferenceManager.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
- }
-
- override fun onDestroy() {
- super.onDestroy()
- preferenceManager.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
- }
-
- override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
- sharedPreferences ?: return
- when (key) {
- PreferKey.launcherIcon -> LauncherIconHelp.changeIcon(getPrefString(key))
- //PreferKey.transparentStatusBar -> recreateActivities()
- //PreferKey.immNavigationBar -> recreateActivities()
-
- PreferKey.themePref -> {
- //recreateActivities()
- }
-
-
- PreferKey.paletteStyle, PreferKey.pureBlack, PreferKey.swipeAnimation -> {
- Handler(Looper.getMainLooper()).postDelayed({
- recreateActivities()
- }, 100)
- }
-
- PreferKey.enableBlur -> {
- ThemeConfig.enableBlur = PreferKey.enableBlur.toBoolean()
- }
-
- PreferKey.enableProgressiveBlur -> {
- ThemeConfig.enableProgressiveBlur = PreferKey.enableProgressiveBlur.toBoolean()
- }
-
- PreferKey.customMode -> handleRestartRequired()
-
- PreferKey.isPredictiveBackEnabled -> {
- toastOnUi("重启以应用")
- }
-
- PreferKey.cPrimary -> {
- val color = getPrefInt(key, ThemeStore.primaryColor(requireContext()))
- ThemeStore.editTheme(requireContext())
- .primaryColor(color)
- .apply()
- DynamicColors.applyToActivitiesIfAvailable(requireContext().applicationContext as Application, DynamicColorsOptions.Builder()
- .setContentBasedSource(requireContext().primaryColor)
- .build())
- Handler(Looper.getMainLooper()).postDelayed({
- recreateActivities()
- }, 100)
- }
-
- PreferKey.cBackground,
- PreferKey.cBBackground -> {
- upTheme(false)
- }
-
- //PreferKey.cNPrimary,
-
- PreferKey.cNBackground,
- PreferKey.cNBBackground -> {
- upTheme(true)
- }
-
- PreferKey.themeMode -> {
- //recreateActivities()
- }
-
- PreferKey.bgImage,
- PreferKey.bgImageN -> {
- upPreferenceSummary(key, getString(R.string.click_to_delete))
- }
-
- PreferKey.colorImage -> handleRestartRequired()
-
- PreferKey.showDiscovery, PreferKey.showRss,
- PreferKey.showStatusBar, PreferKey.showBottomView, PreferKey.tabletInterface,
- PreferKey.labelVisibilityMode -> postEvent(EventBus.NOTIFY_MAIN, true)
- }
-
- }
-
- @SuppressLint("PrivateResource")
- override fun onPreferenceTreeClick(preference: Preference): Boolean {
- when (val key = preference.key) {
-
- PreferKey.fontScale -> NumberPickerDialog(requireContext())
- .setTitle(getString(R.string.font_scale))
- .setMaxValue(16)
- .setMinValue(8)
- .setValue(10)
- .setCustomButton((R.string.btn_default_s)) {
- putPrefInt(PreferKey.fontScale, 0)
- Handler(Looper.getMainLooper()).postDelayed({
- recreateActivities()
- }, 100)
- }
- .show {
- putPrefInt(PreferKey.fontScale, it)
- Handler(Looper.getMainLooper()).postDelayed({
- recreateActivities()
- }, 100)
- }
-
- PreferKey.containerOpacity -> NumberPickerDialog(requireContext())
- .setTitle(getString(R.string.container_opacity))
- .setMaxValue(100)
- .setMinValue(0)
- .setValue(ThemeConfig.containerOpacity)
- .setCustomButton((R.string.btn_default_s)) {
- ThemeConfig.containerOpacity = 100
- Handler(Looper.getMainLooper()).postDelayed({
- upPreferenceSummary(PreferKey.containerOpacity)
- }, 100)
- }
- .show {
- ThemeConfig.containerOpacity = it
- Handler(Looper.getMainLooper()).postDelayed({
- upPreferenceSummary(PreferKey.containerOpacity)
- }, 100)
- }
-
- PreferKey.bgImage -> selectBgAction(false)
- PreferKey.bgImageN -> selectBgAction(true)
- "colorImage" -> selectBgAction(null)
-
- "themeList" -> ThemeListDialog().show(childFragmentManager, "themeList")
- "saveDayTheme",
- "saveNightTheme" -> alertSaveTheme(key)
-
- "coverConfig" -> startActivity {
- putExtra("configTag", ConfigTag.COVER_CONFIG)
- }
- }
- return super.onPreferenceTreeClick(preference)
- }
-
- @SuppressLint("InflateParams")
- private fun alertSaveTheme(key: String) {
- alert(R.string.theme_name) {
- val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
- editView.hint = "name"
- }
- customView { alertBinding.root }
- okButton {
- alertBinding.editView.text?.toString()?.let { themeName ->
- when (key) {
- "saveDayTheme" -> {
- OldThemeConfig.saveDayTheme(requireContext(), themeName)
- }
-
- "saveNightTheme" -> {
- OldThemeConfig.saveNightTheme(requireContext(), themeName)
- }
- }
- }
- }
- cancelButton()
- }
- }
-
- private fun selectBgAction(isNight: Boolean?) {
- val bgKey = when (isNight) {
- true -> PreferKey.bgImageN
- false -> PreferKey.bgImage
- null -> PreferKey.colorImage
- }
- val blurringKey = when (isNight) {
- true -> PreferKey.bgImageNBlurring
- false -> PreferKey.bgImageBlurring
- else -> null
- }
-
- val actions = mutableListOf()
-
- if (isNight != null) {
- actions.add(getString(R.string.background_image_blurring))
- }
-
- actions.add(getString(R.string.select_image))
-
- if (!getPrefString(bgKey).isNullOrEmpty()) {
- actions.add(getString(R.string.delete))
- }
-
- context?.selector(items = actions) { _, i ->
- when {
- isNight != null && i == 0 -> {
- alertImageBlurring(blurringKey!!) {
- upTheme(isNight)
- }
- }
-
- (isNight == null && i == 0) || (isNight != null && i == 1) -> {
- when (isNight) {
- true -> selectImage.launch(requestCodeBgDark)
- false -> selectImage.launch(requestCodeBgLight)
- null -> selectImage.launch(requestCodeColorImage)
- }
- }
-
- (isNight == null && i == 1) || (isNight != null && i == 2) -> {
- removePref(bgKey)
- if (isNight != null) {
- upTheme(isNight)
- }
- }
- }
- }
- }
-
- private fun handleRestartRequired() {
- alert(getString(R.string.restart_required_message)) {
- okButton {
- Handler(Looper.getMainLooper()).postDelayed({
- requireContext().restart()
- }, 100)
- }
- cancelButton {
- toastOnUi(R.string.restart_later_message)
- }
- }
- }
-
- private fun alertImageBlurring(preferKey: String, success: () -> Unit) {
- alert(R.string.background_image_blurring) {
- val alertBinding = DialogImageBlurringBinding.inflate(layoutInflater).apply {
- getPrefInt(preferKey, 0).let {
- seekBar.progress = it
- textViewValue.text = it.toString()
- }
- seekBar.setOnSeekBarChangeListener(object : SeekBarChangeListener {
- override fun onProgressChanged(
- seekBar: SeekBar,
- progress: Int,
- fromUser: Boolean
- ) {
- textViewValue.text = progress.toString()
- }
- })
- }
- customView { alertBinding.root }
- okButton {
- alertBinding.seekBar.progress.let {
- putPrefInt(preferKey, it)
- success.invoke()
- }
- }
- cancelButton()
- }
- }
-
- private fun upTheme(isNightTheme: Boolean) {
- if (AppConfig.isNightTheme == isNightTheme) {
- listView.post {
- //ThemeConfig.applyTheme(requireContext())
- recreateActivities()
- }
- }
- }
-
- private fun recreateActivities() {
- postEvent(EventBus.RECREATE, "")
- }
-
- private fun upPreferenceSummary(preferenceKey: String, value: String? = null) {
- val preference = findPreference(preferenceKey) ?: return
- when (preferenceKey) {
- //PreferKey.barElevation -> preference.summary =
- // getString(R.string.bar_elevation_s, value)
- PreferKey.containerOpacity -> {
- preference.summary =
- getString(R.string.container_opacity_summary, AppConfig.containerOpacity)
- }
-
- PreferKey.fontScale -> {
- val fontScale = AppContextWrapper.getFontScale(requireContext())
- preference.summary = getString(R.string.font_scale_summary, fontScale)
- }
-
- PreferKey.bgImage,
- PreferKey.bgImageN -> {
- val hasImage = !getPrefString(preferenceKey).isNullOrBlank()
- preference.summary = if (hasImage) {
- getString(R.string.click_to_delete)
- } else {
- getString(R.string.select_image)
- }
- }
-
- else -> preference.summary = value
- }
- }
-
- private fun setBgFromUri(uri: Uri, preferenceKey: String, success: () -> Unit) {
- readUri(uri) { fileDoc, inputStream ->
- kotlin.runCatching {
- var file = requireContext().externalFiles
- val suffix = fileDoc.name.substringAfterLast(".")
- val fileName = uri.inputStream(requireContext()).getOrThrow().use {
- MD5Utils.md5Encode(it) + ".$suffix"
- }
- file = FileUtils.createFileIfNotExist(file, preferenceKey, fileName)
- FileOutputStream(file).use {
- inputStream.copyTo(it)
- }
- putPrefString(preferenceKey, file.absolutePath)
- success()
- }.onFailure {
- appCtx.toastOnUi(it.localizedMessage)
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/config/ThemeListDialog.kt b/app/src/main/java/io/legado/app/ui/config/ThemeListDialog.kt
deleted file mode 100644
index 804eab7ac..000000000
--- a/app/src/main/java/io/legado/app/ui/config/ThemeListDialog.kt
+++ /dev/null
@@ -1,129 +0,0 @@
-package io.legado.app.ui.config
-
-import android.content.Context
-import android.os.Bundle
-import android.view.MenuItem
-import android.view.View
-import android.view.ViewGroup
-import androidx.appcompat.widget.Toolbar
-import androidx.core.graphics.toColorInt
-import androidx.recyclerview.widget.LinearLayoutManager
-import io.legado.app.R
-import io.legado.app.base.BaseBottomSheetDialogFragment
-import io.legado.app.base.adapter.ItemViewHolder
-import io.legado.app.base.adapter.RecyclerAdapter
-import io.legado.app.databinding.DialogRecyclerViewBinding
-import io.legado.app.databinding.ItemThemeConfigBinding
-import io.legado.app.help.config.OldThemeConfig
-import io.legado.app.lib.dialogs.alert
-import io.legado.app.lib.theme.primaryColor
-//import io.legado.app.lib.theme.primaryColor
-import io.legado.app.utils.*
-import io.legado.app.utils.viewbindingdelegate.viewBinding
-
-class ThemeListDialog : BaseBottomSheetDialogFragment(R.layout.dialog_recycler_view),
- Toolbar.OnMenuItemClickListener {
-
- private val binding by viewBinding(DialogRecyclerViewBinding::bind)
- private val adapter by lazy { Adapter(requireContext()) }
-
- override fun onStart() {
- super.onStart()
- //setLayout(0.9f, 0.9f)
- }
-
- override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
- //binding.toolBar.setBackgroundColor(primaryColor)
- binding.toolBar.setTitle(R.string.theme_list)
- initView()
- initMenu()
- initData()
- }
-
- private fun initView() = binding.run {
- recyclerView.layoutManager = LinearLayoutManager(requireContext())
- recyclerView.adapter = adapter
- }
-
- private fun initMenu() = binding.run {
- toolBar.setOnMenuItemClickListener(this@ThemeListDialog)
- toolBar.inflateMenu(R.menu.theme_list)
- toolBar.menu.applyTint(requireContext())
- }
-
- fun initData() {
- adapter.setItems(OldThemeConfig.configList)
- }
-
- override fun onMenuItemClick(item: MenuItem?): Boolean {
- when (item?.itemId) {
- R.id.menu_import -> {
- requireContext().getClipText()?.let {
- if (OldThemeConfig.addConfig(it)) {
- initData()
- } else {
- toastOnUi("格式不对,添加失败")
- }
- }
- }
- }
- return true
- }
-
- fun delete(index: Int) {
- alert(R.string.delete, R.string.sure_del) {
- yesButton {
- OldThemeConfig.delConfig(index)
- initData()
- }
- noButton()
- }
- }
-
- fun share(index: Int) {
- val json = GSON.toJson(OldThemeConfig.configList[index])
- requireContext().share(json, "主题分享")
- }
-
- inner class Adapter(context: Context) :
- RecyclerAdapter(context) {
-
- override fun getViewBinding(parent: ViewGroup): ItemThemeConfigBinding {
- return ItemThemeConfigBinding.inflate(inflater, parent, false)
- }
-
- override fun convert(
- holder: ItemViewHolder,
- binding: ItemThemeConfigBinding,
- item: OldThemeConfig.Config,
- payloads: MutableList
- ) {
- binding.apply {
- tvName.text = item.themeName
- if (item.primaryColor.toColorInt() == context.primaryColor)
- cdRoot.setCardBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSecondaryContainer))
- else
- cdRoot.setCardBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSurfaceContainer))
- }
- }
-
-
- override fun registerListener(holder: ItemViewHolder, binding: ItemThemeConfigBinding) {
- binding.apply {
- root.setOnClickListener {
- OldThemeConfig.applyConfig(
- context,
- OldThemeConfig.configList[holder.layoutPosition]
- )
- }
- ivShare.setOnClickListener {
- share(holder.layoutPosition)
- }
- ivDelete.setOnClickListener {
- delete(holder.layoutPosition)
- }
- }
- }
-
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigViewModel.kt b/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigViewModel.kt
index 9a260ed41..1fcf351e7 100644
--- a/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigViewModel.kt
@@ -11,6 +11,12 @@ import splitties.init.appCtx
class BackupConfigViewModel : ViewModel() {
+ private suspend fun syncWebDavConfig() {
+ withContext(Dispatchers.IO) {
+ AppWebDav.upConfig()
+ }
+ }
+
fun setWebDavAccount(account: String, password: String) {
BackupConfig.webDavAccount = account
BackupConfig.webDavPassword = password
@@ -25,6 +31,7 @@ class BackupConfigViewModel : ViewModel() {
}
suspend fun testWebDav(): Boolean {
+ syncWebDavConfig()
return withContext(Dispatchers.IO) {
try {
AppWebDav.testWebDav()
@@ -51,6 +58,7 @@ class BackupConfigViewModel : ViewModel() {
}
suspend fun getBackupNames(): List {
+ syncWebDavConfig()
return withContext(Dispatchers.IO) {
AppWebDav.getBackupNames()
}
@@ -59,6 +67,7 @@ class BackupConfigViewModel : ViewModel() {
fun restoreWebDav(name: String, onSuccess: () -> Unit, onError: (String) -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
try {
+ AppWebDav.upConfig()
AppWebDav.restoreWebDav(name)
withContext(Dispatchers.Main) {
onSuccess()
diff --git a/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt b/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt
index 8554a77d0..584cf9ce8 100644
--- a/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt
@@ -42,6 +42,7 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.ToggleButton
@@ -108,6 +109,9 @@ fun ThemeConfigScreen(
var showRestartDialog by remember { mutableStateOf(false) }
var showColorPicker by remember { mutableStateOf(false) }
var showLauncherIconPicker by remember { mutableStateOf(false) }
+ var showThemeListDialog by remember { mutableStateOf(false) }
+ var saveThemeKey by remember { mutableStateOf(null) }
+ var themeName by remember { mutableStateOf("") }
val fontScaleValue = remember { mutableFloatStateOf(ThemeConfig.fontScale.toFloat()) }
val primaryColorValue = remember { mutableIntStateOf(ThemeConfig.cPrimary) }
@@ -339,6 +343,22 @@ fun ThemeConfigScreen(
)
}
+ SplicedColumnGroup(title = stringResource(R.string.share)) {
+ ClickableSettingItem(
+ title = stringResource(R.string.theme_list),
+ description = stringResource(R.string.theme_list_summary),
+ onClick = { showThemeListDialog = true }
+ )
+ ClickableSettingItem(
+ title = stringResource(R.string.save_theme_config),
+ description = stringResource(R.string.save_day_theme_summary),
+ onClick = {
+ saveThemeKey = "saveDayTheme"
+ themeName = ""
+ }
+ )
+ }
+
SplicedColumnGroup(title = stringResource(R.string.compose_related)) {
SwitchSettingItem(
title = stringResource(R.string.use_flexible_top_bar),
@@ -483,6 +503,45 @@ fun ThemeConfigScreen(
}
)
}
+
+ if (showThemeListDialog) {
+ ThemeListDialog(
+ onDismissRequest = { showThemeListDialog = false }
+ )
+ }
+
+ saveThemeKey?.let { key ->
+ AlertDialog(
+ onDismissRequest = { saveThemeKey = null },
+ title = { Text(stringResource(R.string.theme_name)) },
+ text = {
+ OutlinedTextField(
+ value = themeName,
+ onValueChange = { themeName = it },
+ label = { Text("name") },
+ singleLine = true
+ )
+ },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ when (key) {
+ "saveDayTheme" -> OldThemeConfig.saveDayTheme(context, themeName)
+ "saveNightTheme" -> OldThemeConfig.saveNightTheme(context, themeName)
+ }
+ saveThemeKey = null
+ }
+ ) {
+ Text(stringResource(android.R.string.ok))
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = { saveThemeKey = null }) {
+ Text(stringResource(android.R.string.cancel))
+ }
+ }
+ )
+ }
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
diff --git a/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeListDialog.kt b/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeListDialog.kt
new file mode 100644
index 000000000..da96e5864
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeListDialog.kt
@@ -0,0 +1,184 @@
+package io.legado.app.ui.config.themeConfig
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.material.icons.filled.FileDownload
+import androidx.compose.material.icons.filled.Share
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.core.graphics.toColorInt
+import io.legado.app.R
+import io.legado.app.help.config.OldThemeConfig
+import io.legado.app.lib.theme.primaryColor
+import io.legado.app.ui.widget.components.card.GlassCard
+import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.utils.GSON
+import io.legado.app.utils.getClipText
+import io.legado.app.utils.share
+import io.legado.app.utils.toastOnUi
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ThemeListDialog(
+ onDismissRequest: () -> Unit
+) {
+ val context = LocalContext.current
+ var listVersion by remember { mutableIntStateOf(0) }
+ var deleteIndex by remember { mutableStateOf(null) }
+ val themeList = remember(listVersion) { OldThemeConfig.configList.toList() }
+
+ GlassModalBottomSheet(
+ onDismissRequest = onDismissRequest
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(bottom = 24.dp)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = 16.dp, end = 8.dp, top = 8.dp, bottom = 8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = stringResource(R.string.theme_list),
+ style = MaterialTheme.typography.titleMedium,
+ modifier = Modifier.weight(1f)
+ )
+ IconButton(
+ onClick = {
+ val clipText = context.getClipText()
+ if (clipText != null && OldThemeConfig.addConfig(clipText)) {
+ listVersion++
+ } else {
+ context.toastOnUi("Import failed")
+ }
+ }
+ ) {
+ Icon(
+ imageVector = Icons.Default.FileDownload,
+ contentDescription = stringResource(R.string.import_theme)
+ )
+ }
+ }
+
+ LazyColumn(
+ modifier = Modifier.fillMaxWidth(),
+ verticalArrangement = Arrangement.spacedBy(12.dp)
+ ) {
+ itemsIndexed(themeList, key = { _, item -> item.themeName }) { index, item ->
+ GlassCard(
+ onClick = {
+ OldThemeConfig.applyConfig(context, item)
+ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ shape = RoundedCornerShape(20.dp),
+ colors = CardDefaults.cardColors(
+ containerColor = if (item.primaryColor.toColorInt() == context.primaryColor) {
+ MaterialTheme.colorScheme.secondaryContainer
+ } else {
+ MaterialTheme.colorScheme.surfaceContainer
+ }
+ )
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = 16.dp, end = 8.dp, top = 12.dp, bottom = 12.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Box(
+ modifier = Modifier
+ .padding(end = 12.dp)
+ .background(
+ color = Color(
+ item.primaryColor.toColorInt()
+ ),
+ shape = MaterialTheme.shapes.large
+ )
+ .padding(8.dp)
+ )
+ Text(
+ text = item.themeName,
+ style = MaterialTheme.typography.bodyLarge,
+ modifier = Modifier.weight(1f)
+ )
+ IconButton(
+ onClick = {
+ val json = GSON.toJson(item)
+ context.share(json, "主题分享")
+ }
+ ) {
+ Icon(
+ imageVector = Icons.Default.Share,
+ contentDescription = stringResource(R.string.share)
+ )
+ }
+ IconButton(onClick = { deleteIndex = index }) {
+ Icon(
+ imageVector = Icons.Default.Delete,
+ contentDescription = stringResource(R.string.delete)
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ deleteIndex?.let { index ->
+ AlertDialog(
+ onDismissRequest = { deleteIndex = null },
+ title = { Text(stringResource(R.string.delete)) },
+ text = { Text(stringResource(R.string.sure_del)) },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ OldThemeConfig.delConfig(index)
+ listVersion++
+ deleteIndex = null
+ }
+ ) {
+ Text(stringResource(android.R.string.ok))
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = { deleteIndex = null }) {
+ Text(stringResource(android.R.string.cancel))
+ }
+ }
+ )
+ }
+}
diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
index b1ef9a1d9..e1669dbed 100644
--- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
@@ -1,11 +1,27 @@
package io.legado.app.ui.main
+import android.content.Context
+import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
import android.text.format.DateUtils
+import androidx.compose.animation.AnimatedContentTransitionScope
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.LinearOutSlowInEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.scaleOut
+import androidx.compose.animation.togetherWith
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.lifecycleScope
+import androidx.navigation3.runtime.NavKey
+import androidx.navigation3.runtime.entryProvider
+import androidx.navigation3.runtime.rememberNavBackStack
+import androidx.navigation3.ui.NavDisplay
import io.legado.app.BuildConfig
import io.legado.app.R
import io.legado.app.base.BaseComposeActivity
@@ -23,15 +39,24 @@ import io.legado.app.service.WebService
import io.legado.app.ui.about.CrashLogsDialog
import io.legado.app.ui.about.UpdateDialog
import io.legado.app.ui.book.read.ReadBookActivity
+import io.legado.app.ui.config.ConfigNavScreen
+import io.legado.app.ui.config.ConfigTag
+import io.legado.app.ui.config.backupConfig.BackupConfigScreen
+import io.legado.app.ui.config.coverConfig.CoverConfigScreen
import io.legado.app.ui.config.mainConfig.MainConfig
+import io.legado.app.ui.config.otherConfig.OtherConfigScreen
+import io.legado.app.ui.config.readConfig.ReadConfigScreen
+import io.legado.app.ui.config.themeConfig.ThemeConfigScreen
import io.legado.app.ui.welcome.WelcomeActivity
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.showDialogFragment
import io.legado.app.utils.startActivity
import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import kotlinx.serialization.Serializable
import org.koin.androidx.viewmodel.ext.android.viewModel
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@@ -41,7 +66,60 @@ import kotlin.coroutines.suspendCoroutine
*/
open class MainActivity : BaseComposeActivity() {
+ companion object {
+ const val EXTRA_START_ROUTE = "startRoute"
+ private const val ROUTE_MAIN = "main"
+ private const val ROUTE_SETTINGS = "settings"
+ private const val ROUTE_SETTINGS_OTHER = "settings/other"
+ private const val ROUTE_SETTINGS_READ = "settings/read"
+ private const val ROUTE_SETTINGS_COVER = "settings/cover"
+ private const val ROUTE_SETTINGS_THEME = "settings/theme"
+ private const val ROUTE_SETTINGS_BACKUP = "settings/backup"
+
+ fun createIntent(context: Context, configTag: String? = null): Intent {
+ return Intent(context, MainActivity::class.java).apply {
+ putExtra(EXTRA_START_ROUTE, routeForConfigTag(configTag))
+ }
+ }
+
+ private fun routeForConfigTag(configTag: String?): String {
+ return when (configTag) {
+ ConfigTag.OTHER_CONFIG -> ROUTE_SETTINGS_OTHER
+ ConfigTag.READ_CONFIG -> ROUTE_SETTINGS_READ
+ ConfigTag.COVER_CONFIG -> ROUTE_SETTINGS_COVER
+ ConfigTag.THEME_CONFIG -> ROUTE_SETTINGS_THEME
+ ConfigTag.BACKUP_CONFIG -> ROUTE_SETTINGS_BACKUP
+ else -> ROUTE_SETTINGS
+ }
+ }
+ }
+
private val viewModel by viewModel()
+ private val routeEvents = MutableSharedFlow(extraBufferCapacity = 1)
+
+ @Serializable
+ private sealed interface MainRoute : NavKey
+
+ @Serializable
+ private data object MainRouteHome : MainRoute
+
+ @Serializable
+ private data object MainRouteSettings : MainRoute
+
+ @Serializable
+ private data object MainRouteSettingsOther : MainRoute
+
+ @Serializable
+ private data object MainRouteSettingsRead : MainRoute
+
+ @Serializable
+ private data object MainRouteSettingsCover : MainRoute
+
+ @Serializable
+ private data object MainRouteSettingsTheme : MainRoute
+
+ @Serializable
+ private data object MainRouteSettingsBackup : MainRoute
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
@@ -70,6 +148,12 @@ open class MainActivity : BaseComposeActivity() {
}
}
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+ setIntent(intent)
+ routeEvents.tryEmit(resolveStartRoute(intent))
+ }
+
@Composable
override fun Content() {
val orientation = resources.configuration.orientation
@@ -84,7 +168,169 @@ open class MainActivity : BaseComposeActivity() {
else -> false
}
- MainScreen(useRail = useRail)
+ val backStack = rememberNavBackStack(resolveStartRoute(intent))
+
+ LaunchedEffect(backStack) {
+ routeEvents.collect { route ->
+ navigateToRoute(backStack, route)
+ }
+ }
+
+ NavDisplay(
+ backStack = backStack,
+ transitionSpec = {
+ (slideIntoContainer(
+ towards = AnimatedContentTransitionScope.SlideDirection.Start,
+ animationSpec = tween(
+ durationMillis = 260,
+ easing = FastOutSlowInEasing
+ ),
+ initialOffset = { fullWidth -> fullWidth / 10 }
+ ) + fadeIn(
+ animationSpec = tween(
+ durationMillis = 220,
+ easing = LinearOutSlowInEasing
+ )
+ )) togetherWith ExitTransition.None
+ },
+ popTransitionSpec = {
+ (slideIntoContainer(
+ towards = AnimatedContentTransitionScope.SlideDirection.Start,
+ animationSpec = tween(
+ durationMillis = 240,
+ easing = FastOutSlowInEasing
+ ),
+ initialOffset = { fullWidth -> -fullWidth / 8 }
+ ) + fadeIn(
+ animationSpec = tween(
+ durationMillis = 200,
+ easing = LinearOutSlowInEasing
+ )
+ )) togetherWith
+ (scaleOut(
+ targetScale = 0.92f,
+ animationSpec = tween(
+ durationMillis = 220,
+ easing = FastOutSlowInEasing
+ )
+ ) + fadeOut(
+ animationSpec = tween(durationMillis = 180)
+ ))
+ },
+ predictivePopTransitionSpec = { _ ->
+ (slideIntoContainer(
+ towards = AnimatedContentTransitionScope.SlideDirection.Start,
+ animationSpec = tween(
+ durationMillis = 240,
+ easing = FastOutSlowInEasing
+ ),
+ initialOffset = { fullWidth -> -fullWidth / 8 }
+ ) + fadeIn(
+ animationSpec = tween(
+ durationMillis = 200,
+ easing = LinearOutSlowInEasing
+ )
+ )) togetherWith
+ (scaleOut(
+ targetScale = 0.92f,
+ animationSpec = tween(
+ durationMillis = 220,
+ easing = FastOutSlowInEasing
+ )
+ ) + fadeOut(
+ animationSpec = tween(durationMillis = 180)
+ ))
+ },
+ onBack = {
+ if (backStack.size > 1) {
+ backStack.removeLastOrNull()
+ } else {
+ finish()
+ }
+ },
+ entryProvider = entryProvider {
+ entry {
+ MainScreen(
+ useRail = useRail,
+ onOpenSettings = {
+ navigateToRoute(backStack, MainRouteSettings)
+ }
+ )
+ }
+
+ entry {
+ ConfigNavScreen(
+ onBackClick = { navigateBack(backStack) },
+ onNavigateToOther = { backStack.add(MainRouteSettingsOther) },
+ onNavigateToRead = { backStack.add(MainRouteSettingsRead) },
+ onNavigateToCover = { backStack.add(MainRouteSettingsCover) },
+ onNavigateToTheme = { backStack.add(MainRouteSettingsTheme) },
+ onNavigateToBackup = { backStack.add(MainRouteSettingsBackup) }
+ )
+ }
+
+ entry {
+ OtherConfigScreen(onBackClick = { navigateBack(backStack) })
+ }
+
+ entry {
+ ReadConfigScreen(onBackClick = { navigateBack(backStack) })
+ }
+
+ entry {
+ CoverConfigScreen(onBackClick = { navigateBack(backStack) })
+ }
+
+ entry {
+ ThemeConfigScreen(onBackClick = { navigateBack(backStack) })
+ }
+
+ entry {
+ BackupConfigScreen(onBackClick = { navigateBack(backStack) })
+ }
+ }
+ )
+ }
+
+ private fun navigateToRoute(backStack: MutableList, route: MainRoute) {
+ val currentRoute = backStack.lastOrNull()
+ if (currentRoute == route) return
+
+ when (route) {
+ MainRouteHome -> {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ }
+
+ MainRouteSettings -> {
+ if (currentRoute == MainRouteHome) {
+ backStack.add(MainRouteSettings)
+ } else {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(MainRouteSettings)
+ }
+ }
+
+ MainRouteSettingsOther,
+ MainRouteSettingsRead,
+ MainRouteSettingsCover,
+ MainRouteSettingsTheme,
+ MainRouteSettingsBackup -> {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(MainRouteSettings)
+ backStack.add(route)
+ }
+ }
+ }
+
+ private fun navigateBack(backStack: MutableList) {
+ if (backStack.size > 1) {
+ backStack.removeLastOrNull()
+ } else {
+ finish()
+ }
}
private fun checkStartupRoute(): Boolean {
@@ -197,6 +443,23 @@ open class MainActivity : BaseComposeActivity() {
}
}
+ private fun resolveStartRoute(intent: Intent?): MainRoute {
+ return resolveStartRoute(intent?.getStringExtra(EXTRA_START_ROUTE))
+ }
+
+ private fun resolveStartRoute(route: String?): MainRoute {
+ return when (route) {
+ "main" -> MainRouteHome
+ "settings" -> MainRouteSettings
+ "settings/other" -> MainRouteSettingsOther
+ "settings/read" -> MainRouteSettingsRead
+ "settings/cover" -> MainRouteSettingsCover
+ "settings/theme" -> MainRouteSettingsTheme
+ "settings/backup" -> MainRouteSettingsBackup
+ else -> MainRouteHome
+ }
+ }
+
}
class LauncherW : MainActivity()
diff --git a/app/src/main/java/io/legado/app/ui/main/MainScreen.kt b/app/src/main/java/io/legado/app/ui/main/MainScreen.kt
index 80359bc81..d92054be6 100644
--- a/app/src/main/java/io/legado/app/ui/main/MainScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/main/MainScreen.kt
@@ -78,7 +78,8 @@ import org.koin.androidx.compose.koinViewModel
@Composable
fun MainScreen(
viewModel: MainViewModel = koinViewModel(),
- useRail: Boolean
+ useRail: Boolean,
+ onOpenSettings: () -> Unit
) {
val context = LocalContext.current
val uiState by viewModel.state.collectAsStateWithLifecycle()
@@ -362,6 +363,7 @@ fun MainScreen(
MainDestination.Rss -> RssScreen()
MainDestination.My -> MyScreen(
viewModel = koinViewModel(),
+ onOpenSettings = onOpenSettings,
onNavigate = { event ->
viewModel.onPrefClickEvent(context, event)
}
diff --git a/app/src/main/java/io/legado/app/ui/main/my/MyScreen.kt b/app/src/main/java/io/legado/app/ui/main/my/MyScreen.kt
index ce91c522b..d23e1a50b 100644
--- a/app/src/main/java/io/legado/app/ui/main/my/MyScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/main/my/MyScreen.kt
@@ -15,13 +15,11 @@ import androidx.compose.material.icons.automirrored.filled.ExitToApp
import androidx.compose.material.icons.automirrored.filled.HelpOutline
import androidx.compose.material.icons.automirrored.filled.LibraryBooks
import androidx.compose.material.icons.automirrored.filled.Rule
-import androidx.compose.material.icons.filled.Backup
import androidx.compose.material.icons.filled.Bookmark
import androidx.compose.material.icons.filled.FindReplace
import androidx.compose.material.icons.filled.Folder
import androidx.compose.material.icons.filled.History
import androidx.compose.material.icons.filled.Info
-import androidx.compose.material.icons.filled.Palette
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Source
import androidx.compose.material.icons.filled.Web
@@ -45,8 +43,6 @@ import io.legado.app.ui.book.bookmark.AllBookmarkActivity
import io.legado.app.ui.book.readRecord.ReadRecordActivity
import io.legado.app.ui.book.source.manage.BookSourceActivity
import io.legado.app.ui.book.toc.rule.TxtTocRuleActivity
-import io.legado.app.ui.config.ConfigActivity
-import io.legado.app.ui.config.ConfigTag
import io.legado.app.ui.dict.rule.DictRuleActivity
import io.legado.app.ui.file.FileManageActivity
import io.legado.app.ui.replace.ReplaceRuleActivity
@@ -61,6 +57,7 @@ import io.legado.app.ui.widget.components.settingItem.SettingItem
@Composable
fun MyScreen(
viewModel: MyViewModel,
+ onOpenSettings: () -> Unit,
onNavigate: (PrefClickEvent) -> Unit
) {
@@ -91,27 +88,20 @@ fun MyScreen(
) { padding ->
Column(
modifier = Modifier
- .padding(
- top = padding.calculateTopPadding()
- )
.verticalScroll(rememberScrollState()),
//TODO:暂时这样解决,改天加模糊
) {
+ Spacer(
+ modifier = Modifier
+ .padding(
+ top = padding.calculateTopPadding()
+ )
+ )
SplicedColumnGroup(
modifier = Modifier.padding(horizontal = 16.dp),
title = ""
) {
- SettingItem(
- title = stringResource(R.string.book_source_manage),
- description = stringResource(R.string.book_source_manage_desc),
- imageVector = Icons.Default.Source,
- onClick = {
- onNavigate(
- PrefClickEvent.StartActivity(BookSourceActivity::class.java)
- )
- }
- )
SettingItem(
title = stringResource(R.string.web_service),
description = if (uiState.isWebServiceRun)
@@ -149,66 +139,21 @@ fun MyScreen(
)
}
- SplicedColumnGroup(
- modifier = Modifier.padding(horizontal = 16.dp),
- title = stringResource(R.string.setting)) {
- SettingItem(
- title = stringResource(R.string.backup_restore),
- description = stringResource(R.string.web_dav_set_import_old),
- imageVector = Icons.Default.Backup,
- onClick = {
- onNavigate(
- PrefClickEvent.StartActivity(
- ConfigActivity::class.java,
- ConfigTag.BACKUP_CONFIG
- )
- )
- }
- )
- SettingItem(
- title = stringResource(R.string.theme_setting),
- description = stringResource(R.string.theme_setting_s),
- imageVector = Icons.Default.Palette,
- onClick = {
- onNavigate(
- PrefClickEvent.StartActivity(
- ConfigActivity::class.java,
- ConfigTag.THEME_CONFIG
- )
- )
- }
- )
- SettingItem(
- title = stringResource(R.string.read),
- description = stringResource(R.string.global_read_setting),
- imageVector = Icons.AutoMirrored.Filled.LibraryBooks,
- onClick = {
- onNavigate(
- PrefClickEvent.StartActivity(
- ConfigActivity::class.java,
- ConfigTag.READ_CONFIG
- )
- )
- }
- )
- SettingItem(
- title = stringResource(R.string.other_setting),
- description = stringResource(R.string.other_setting_s),
- imageVector = Icons.Default.Settings,
- onClick = {
- onNavigate(
- PrefClickEvent.StartActivity(
- ConfigActivity::class.java,
- ConfigTag.OTHER_CONFIG
- )
- )
- }
- )
- }
+
SplicedColumnGroup(
modifier = Modifier.padding(horizontal = 16.dp),
title = "规则"
) {
+ SettingItem(
+ title = stringResource(R.string.book_source_manage),
+ description = stringResource(R.string.book_source_manage_desc),
+ imageVector = Icons.Default.Source,
+ onClick = {
+ onNavigate(
+ PrefClickEvent.StartActivity(BookSourceActivity::class.java)
+ )
+ }
+ )
SettingItem(
title = stringResource(R.string.replace_purify),
imageVector = Icons.Default.FindReplace,
@@ -229,7 +174,7 @@ fun MyScreen(
)
SettingItem(
title = stringResource(R.string.dict_rule),
- imageVector = Icons.AutoMirrored.Filled.Rule,
+ imageVector = Icons.AutoMirrored.Filled.LibraryBooks,
onClick = {
onNavigate(
PrefClickEvent.StartActivity(DictRuleActivity::class.java)
@@ -237,9 +182,17 @@ fun MyScreen(
}
)
}
+
SplicedColumnGroup(
modifier = Modifier.padding(horizontal = 16.dp),
title = stringResource(R.string.other)) {
+ SettingItem(
+ title = stringResource(R.string.setting),
+ imageVector = Icons.Default.Settings,
+ onClick = {
+ onOpenSettings()
+ }
+ )
SettingItem(
title = stringResource(R.string.bookmark),
imageVector = Icons.Default.Bookmark,