diff --git a/app/src/main/java/io/legado/app/help/storage/Backup.kt b/app/src/main/java/io/legado/app/help/storage/Backup.kt
index fed46fe2a..f310e050c 100644
--- a/app/src/main/java/io/legado/app/help/storage/Backup.kt
+++ b/app/src/main/java/io/legado/app/help/storage/Backup.kt
@@ -25,7 +25,6 @@ import io.legado.app.utils.createFolderIfNotExist
import io.legado.app.utils.defaultSharedPreferences
import io.legado.app.utils.externalFiles
import io.legado.app.utils.getFile
-import io.legado.app.utils.getSharedPreferences
import io.legado.app.utils.isContentScheme
import io.legado.app.utils.normalizeFileName
import io.legado.app.utils.openOutputStream
@@ -178,29 +177,29 @@ object Backup {
.writeText(GSON.toJson(it))
}
currentCoroutineContext().ensureActive()
- appCtx.getSharedPreferences(backupPath, "config")?.let { sp ->
- sp.edit(commit = true) {
- appCtx.defaultSharedPreferences.all.forEach { (key, value) ->
- if (BackupConfig.keyIsNotIgnore(key)) {
- when (key) {
- PreferKey.webDavPassword -> {
- putString(key, aes.runCatching {
- encryptBase64(value.toString())
- }.getOrDefault(value.toString()))
- }
+ val configMap = appCtx.defaultSharedPreferences.all
+ val xmlBuilder = StringBuilder()
+ xmlBuilder.append("\n")
+ xmlBuilder.append("")
+ FileUtils.createFileIfNotExist(backupPath + File.separator + "config.xml")
+ .writeText(xmlBuilder.toString())
+
currentCoroutineContext().ensureActive()
val zipFileName = getNowZipFileName()
val paths = arrayListOf(*backupFileNames)
diff --git a/app/src/main/java/io/legado/app/help/storage/BackupConfig.kt b/app/src/main/java/io/legado/app/help/storage/BackupConfig.kt
index f0b691372..a471bf195 100644
--- a/app/src/main/java/io/legado/app/help/storage/BackupConfig.kt
+++ b/app/src/main/java/io/legado/app/help/storage/BackupConfig.kt
@@ -88,7 +88,35 @@ object BackupConfig {
PreferKey.bgImage,
PreferKey.bgImageBlurring,
PreferKey.bgImageN,
- PreferKey.bgImageNBlurring
+ PreferKey.bgImageNBlurring,
+ PreferKey.themeColor,
+ PreferKey.secondaryThemeColor,
+ PreferKey.paletteStyle,
+ PreferKey.materialVersion,
+ PreferKey.composeEngine,
+ PreferKey.customContrast,
+ PreferKey.customMode,
+ PreferKey.useMiuixMonet,
+ PreferKey.containerOpacity,
+ PreferKey.topBarOpacity,
+ PreferKey.bottomBarOpacity,
+ PreferKey.enableBlur,
+ PreferKey.enableProgressiveBlur,
+ PreferKey.topBarBlurRadius,
+ PreferKey.bottomBarBlurRadius,
+ PreferKey.topBarBlurAlpha,
+ PreferKey.bottomBarBlurAlpha,
+ PreferKey.bottomBarLensRadius,
+ PreferKey.useFlexibleTopAppBar,
+ PreferKey.cBackground,
+ PreferKey.cBBackground,
+ PreferKey.cNBackground,
+ PreferKey.cNBBackground,
+ PreferKey.enableDeepPersonalization,
+ PreferKey.primaryTextColor,
+ PreferKey.secondaryTextColor,
+ PreferKey.themeBackgroundColor,
+ PreferKey.labelContainerColor
)
private val coverPrefKeys = arrayOf(
@@ -100,9 +128,10 @@ object BackupConfig {
PreferKey.coverShowAuthorN
)
- fun keyIsNotIgnore(key: String): Boolean {
+ fun keyIsNotIgnore(key: String, isBackup: Boolean = false): Boolean {
+ if (ignorePrefKeys.contains(key)) return false
+ if (isBackup) return true
return when {
- ignorePrefKeys.contains(key) -> false
ignoreReadConfig && readPrefKeys.contains(key) -> false
ignoreThemeConfig && themePrefKeys.contains(key) -> false
ignoreCoverConfig && coverPrefKeys.contains(key) -> false
@@ -116,17 +145,17 @@ object BackupConfig {
val ignoreReadConfig: Boolean
get() = ignoreConfig[readConfigKey] == true
- private val ignoreThemeMode: Boolean
+ val ignoreThemeMode: Boolean
get() = ignoreConfig[PreferKey.themeMode] == true
- private val ignoreThemeConfig: Boolean
+ val ignoreThemeConfig: Boolean
get() = ignoreConfig[themeConfigKey] == true
- private val ignoreCoverConfig: Boolean
+ val ignoreCoverConfig: Boolean
get() = ignoreConfig[coverConfigKey] == true
- private val ignoreBookshelfLayout: Boolean
+ val ignoreBookshelfLayout: Boolean
get() = ignoreConfig[PreferKey.bookshelfLayout] == true
- private val ignoreShowRss: Boolean
+ val ignoreShowRss: Boolean
get() = ignoreConfig[PreferKey.showRss] == true
- private val ignoreThreadCount: Boolean
+ val ignoreThreadCount: Boolean
get() = ignoreConfig[PreferKey.threadCount] == true
val ignoreLocalBook: Boolean
get() = ignoreConfig[localBookKey] == true
diff --git a/app/src/main/java/io/legado/app/help/storage/Restore.kt b/app/src/main/java/io/legado/app/help/storage/Restore.kt
index dab27caae..c8f794253 100644
--- a/app/src/main/java/io/legado/app/help/storage/Restore.kt
+++ b/app/src/main/java/io/legado/app/help/storage/Restore.kt
@@ -48,7 +48,6 @@ import io.legado.app.utils.fromJsonArray
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.getPrefInt
import io.legado.app.utils.getPrefString
-import io.legado.app.utils.getSharedPreferences
import io.legado.app.utils.isContentScheme
import io.legado.app.utils.isJsonArray
import io.legado.app.utils.openInputStream
@@ -58,6 +57,8 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParserFactory
import splitties.init.appCtx
import java.io.File
import java.io.FileInputStream
@@ -273,17 +274,19 @@ object Restore : KoinComponent {
AppLog.put("恢复直链上传出错\n${it.localizedMessage}", it)
}
//恢复主题配置
- File(path, OldThemeConfig.configFileName).takeIf {
- it.exists()
- }?.runCatching {
- FileUtils.delete(OldThemeConfig.configFilePath)
- copyTo(File(OldThemeConfig.configFilePath))
- OldThemeConfig.upConfig()
- }?.onFailure {
- AppLog.put("恢复主题出错\n${it.localizedMessage}", it)
+ if (!BackupConfig.ignoreThemeConfig) {
+ File(path, OldThemeConfig.configFileName).takeIf {
+ it.exists()
+ }?.runCatching {
+ FileUtils.delete(OldThemeConfig.configFilePath)
+ copyTo(File(OldThemeConfig.configFilePath))
+ OldThemeConfig.upConfig()
+ }?.onFailure {
+ AppLog.put("恢复主题出错\n${it.localizedMessage}", it)
+ }
}
File(path, BookCover.configFileName).takeIf {
- it.exists()
+ it.exists() && !BackupConfig.ignoreCoverConfig
}?.runCatching {
val json = readText()
BookCover.saveCoverRule(json)
@@ -311,43 +314,19 @@ object Restore : KoinComponent {
AppLog.put("恢复阅读界面出错\n${it.localizedMessage}", it)
}
}
- //AppWebDav.downBgs()
- appCtx.getSharedPreferences(path, "config")?.all?.let { map ->
- val finalMap = mutableMapOf()
- appCtx.defaultSharedPreferences.edit {
- map.forEach { (key, value) ->
- if (BackupConfig.keyIsNotIgnore(key)) {
- when (key) {
- PreferKey.webDavPassword -> {
- val password = kotlin.runCatching {
- aes.decryptStr(value.toString())
- }.getOrNull() ?: let {
- if (appCtx.getPrefString(PreferKey.webDavPassword).isNullOrBlank()) {
- value.toString()
- } else null
- }
- password?.let {
- putString(key, it)
- finalMap[key] = it
- }
- }
-
- else -> {
- when (value) {
- is Int -> { putInt(key, value); finalMap[key] = value }
- is Boolean -> { putBoolean(key, value); finalMap[key] = value }
- is Long -> { putLong(key, value); finalMap[key] = value }
- is Float -> { putFloat(key, value); finalMap[key] = value }
- is String -> { putString(key, value); finalMap[key] = value }
- }
- }
- }
- }
+ // 恢复配置文件 (手动解析 XML,替代反射逻辑)
+ val configFile = File(path, "config.xml")
+ if (configFile.exists()) {
+ try {
+ val map = readXmlToMap(configFile)
+ if (map.isNotEmpty()) {
+ applyConfigMap(map, aes)
}
+ } catch (e: Exception) {
+ AppLog.put("恢复配置 XML 出错\n${e.localizedMessage}", e)
}
- // 同步恢复到 DataStore
- settingsRepository.batchPutFromMap(finalMap)
}
+
ReadBookConfig.apply {
comicStyleSelect = appCtx.getPrefInt(PreferKey.comicStyleSelect)
readStyleSelect = appCtx.getPrefInt(PreferKey.readStyleSelect)
@@ -366,6 +345,81 @@ object Restore : KoinComponent {
}
}
+ private suspend fun applyConfigMap(map: Map, aes: BackupAES) {
+ val finalMap = mutableMapOf()
+ appCtx.defaultSharedPreferences.edit {
+ map.forEach { (key, value) ->
+ if (BackupConfig.keyIsNotIgnore(key)) {
+ when (key) {
+ PreferKey.webDavPassword -> {
+ val password = kotlin.runCatching {
+ aes.decryptStr(value.toString())
+ }.getOrNull() ?: let {
+ if (appCtx.getPrefString(PreferKey.webDavPassword).isNullOrBlank()) {
+ value.toString()
+ } else null
+ }
+ password?.let {
+ putString(key, it)
+ finalMap[key] = it
+ }
+ }
+
+ else -> {
+ if (value != null) {
+ when (value) {
+ is Int -> { putInt(key, value); finalMap[key] = value }
+ is Boolean -> { putBoolean(key, value); finalMap[key] = value }
+ is Long -> { putLong(key, value); finalMap[key] = value }
+ is Double -> { // JSON 数字会被解析为 Double
+ val floatValue = value.toFloat()
+ putFloat(key, floatValue)
+ finalMap[key] = floatValue
+ }
+ is Float -> { putFloat(key, value); finalMap[key] = value }
+ is String -> { putString(key, value); finalMap[key] = value }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // 同步恢复到 DataStore
+ settingsRepository.batchPutFromMap(finalMap)
+ }
+
+ private fun readXmlToMap(file: File): Map {
+ val map = mutableMapOf()
+ try {
+ val factory = XmlPullParserFactory.newInstance()
+ val parser = factory.newPullParser()
+ FileInputStream(file).use { fis ->
+ parser.setInput(fis, "UTF-8")
+ var eventType = parser.eventType
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ val tagName = parser.name
+ val name = parser.getAttributeValue(null, "name")
+ if (name != null) {
+ when (tagName) {
+ "string" -> map[name] = parser.nextText()
+ "int" -> map[name] = parser.getAttributeValue(null, "value").toInt()
+ "long" -> map[name] = parser.getAttributeValue(null, "value").toLong()
+ "float" -> map[name] = parser.getAttributeValue(null, "value").toFloat()
+ "boolean" -> map[name] = parser.getAttributeValue(null, "value").toBoolean()
+ }
+ }
+ }
+ eventType = parser.next()
+ }
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return map
+ }
+
private inline fun fileToListT(path: String, fileName: String): List? {
try {
val file = File(path, fileName)
diff --git a/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt b/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt
index f6cd373b4..9f12146c2 100644
--- a/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt
@@ -374,7 +374,7 @@ fun BackupConfigScreen(
AppTextField(
value = tempPassword,
onValueChange = { tempPassword = it },
- backgroundColor = MiuixTheme.colorScheme.surface,
+ backgroundColor = LegadoTheme.colorScheme.surface,
label = stringResource(R.string.web_dav_pw),
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),