diff --git a/app/src/main/java/io/legado/app/domain/model/BookSearchScope.kt b/app/src/main/java/io/legado/app/domain/model/BookSearchScope.kt index 8e4667357..7db629f7e 100644 --- a/app/src/main/java/io/legado/app/domain/model/BookSearchScope.kt +++ b/app/src/main/java/io/legado/app/domain/model/BookSearchScope.kt @@ -1,8 +1,15 @@ package io.legado.app.domain.model import io.legado.app.utils.splitNotBlank -import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive data class BookSearchScope(val raw: String) { @@ -39,19 +46,11 @@ data class BookSearchScope(val raw: String) { get() = groups.isEmpty() && sources.isEmpty() } - @Serializable data class ScopeSourceItem( val name: String, val url: String, ) - @Serializable - private data class SerializedSearchScope( - val type: String = "", - val groups: List = emptyList(), - val sources: List = emptyList(), - ) - companion object { fun encodeGroups(groups: List): String { @@ -59,9 +58,12 @@ data class BookSearchScope(val raw: String) { return if (selected.isEmpty()) { "" } else { - scopeJson.encodeToString( - SerializedSearchScope(type = TYPE_GROUP, groups = selected) - ) + buildJsonObject { + put("type", JsonPrimitive(TYPE_GROUP)) + put("groups", buildJsonArray { + selected.forEach { add(JsonPrimitive(it)) } + }) + }.toString() } } @@ -70,9 +72,17 @@ data class BookSearchScope(val raw: String) { return if (selected.isEmpty()) { "" } else { - scopeJson.encodeToString( - SerializedSearchScope(type = TYPE_SOURCE, sources = selected) - ) + buildJsonObject { + put("type", JsonPrimitive(TYPE_SOURCE)) + put("sources", buildJsonArray { + selected.forEach { source -> + add(buildJsonObject { + put("name", JsonPrimitive(source.name)) + put("url", JsonPrimitive(source.url)) + }) + } + }) + }.toString() } } @@ -91,15 +101,22 @@ data class BookSearchScope(val raw: String) { if (!json.startsWith("{") || !json.endsWith("}")) return null return runCatching { - scopeJson.decodeFromString(json) + scopeJson.parseToJsonElement(json).jsonObject }.getOrNull()?.let { scope -> - when (scope.type) { + when (scope["type"]?.jsonPrimitive?.contentOrNull) { TYPE_SOURCE -> ParsedSearchScope( - sources = scope.sources.filter { it.url.isNotBlank() } + sources = scope["sources"] + ?.safeJsonArray() + ?.mapNotNull { it.toSourceItemOrNull() } + .orEmpty() ) TYPE_GROUP -> ParsedSearchScope( - groups = scope.groups.filter { it.isNotBlank() } + groups = scope["groups"] + ?.safeJsonArray() + ?.mapNotNull { it.toStringOrNull() } + ?.filter { it.isNotBlank() } + .orEmpty() ) else -> null @@ -130,12 +147,32 @@ data class BookSearchScope(val raw: String) { } } + private fun JsonElement.safeJsonArray() = runCatching { + jsonArray + }.getOrNull() + + private fun JsonElement.toStringOrNull() = runCatching { + jsonPrimitive.contentOrNull + }.getOrNull() + + private fun JsonElement.toSourceItemOrNull(): ScopeSourceItem? { + return runCatching { + val item = jsonObject + val url = item["url"]?.jsonPrimitive?.contentOrNull + if (url.isNullOrBlank()) { + null + } else { + ScopeSourceItem( + name = item["name"]?.jsonPrimitive?.contentOrNull.orEmpty(), + url = url + ) + } + }.getOrNull() + } + private const val TYPE_GROUP = "group" private const val TYPE_SOURCE = "source" - private val scopeJson = Json { - encodeDefaults = false - ignoreUnknownKeys = true - } + private val scopeJson = Json } } diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceConfig.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceConfig.kt index f703c711c..6e265f989 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceConfig.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceConfig.kt @@ -1,7 +1,9 @@ package io.legado.app.ui.book.changesource +import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.snapshots.Snapshot +import androidx.datastore.preferences.core.Preferences import io.legado.app.data.local.preferences.LocalPreferencesKeys import io.legado.app.data.local.preferences.LocalPreferencesRepository import io.legado.app.domain.usecase.ChangeSourceMigrationOptions @@ -9,9 +11,7 @@ import io.legado.app.ui.config.prefDelegate import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import splitties.init.appCtx object ChangeSourceConfig { @@ -113,40 +113,25 @@ object ChangeSourceConfig { ) init { - runBlocking(Dispatchers.IO) { - Snapshot.withMutableSnapshot { - _searchScope.value = - repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_SEARCH_SCOPE, "").first() - _checkAuthor.value = - repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_CHECK_AUTHOR, false).first() - _loadInfo.value = - repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_INFO, false).first() - _loadToc.value = - repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_TOC, false).first() - _loadWordCount.value = - repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_WORD_COUNT, false).first() - } - } + observe(LocalPreferencesKeys.CHANGE_SOURCE_SEARCH_SCOPE, "", _searchScope) + observe(LocalPreferencesKeys.CHANGE_SOURCE_CHECK_AUTHOR, false, _checkAuthor) + observe(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_INFO, false, _loadInfo) + observe(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_TOC, false, _loadToc) + observe(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_WORD_COUNT, false, _loadWordCount) + } + private fun observe( + key: Preferences.Key, + defaultValue: T, + state: MutableState, + ) { scope.launch { - repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_SEARCH_SCOPE, "") - .collect { Snapshot.withMutableSnapshot { _searchScope.value = it } } - } - scope.launch { - repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_CHECK_AUTHOR, false) - .collect { Snapshot.withMutableSnapshot { _checkAuthor.value = it } } - } - scope.launch { - repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_INFO, false) - .collect { Snapshot.withMutableSnapshot { _loadInfo.value = it } } - } - scope.launch { - repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_TOC, false) - .collect { Snapshot.withMutableSnapshot { _loadToc.value = it } } - } - scope.launch { - repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_WORD_COUNT, false) - .collect { Snapshot.withMutableSnapshot { _loadWordCount.value = it } } + repo.getPreference(key, defaultValue) + .collect { value -> + Snapshot.withMutableSnapshot { + state.value = value + } + } } }