|
|
@@ -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<String, Any>()
|
|
|
- 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<String, Any?>, aes: BackupAES) {
|
|
|
+ val finalMap = mutableMapOf<String, Any>()
|
|
|
+ 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<String, Any?> {
|
|
|
+ val map = mutableMapOf<String, Any?>()
|
|
|
+ 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 <reified T> fileToListT(path: String, fileName: String): List<T>? {
|
|
|
try {
|
|
|
val file = File(path, fileName)
|