From ff8d48bf4ce80a13e314818fc133360e3df18863 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Tue, 11 Feb 2025 15:39:28 +0700 Subject: [PATCH] Fix (PreferenceRestorer): correctly backup/restore preferences related to category --- .../source/service/SourcePreferences.kt | 13 +- .../data/backup/create/BackupCreator.kt | 2 +- .../creators/PreferenceBackupCreator.kt | 49 +++++- .../data/backup/restore/BackupRestorer.kt | 21 ++- .../restore/restorers/PreferenceRestorer.kt | 155 +++++++++++++----- .../download/service/DownloadPreferences.kt | 18 +- .../library/service/LibraryPreferences.kt | 24 ++- 7 files changed, 227 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt index f281e580a..f7cce69a9 100644 --- a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt @@ -25,7 +25,12 @@ class SourcePreferences( fun incognitoExtensions() = preferenceStore.getStringSet("incognito_extensions", emptySet()) - fun pinnedSources() = preferenceStore.getStringSet(pinnedSourcesPrefKey, emptySet()) + fun pinnedSources() = preferenceStore.getStringSet( + // KMK --> + PINNED_SOURCES_PREF_KEY, + // KMK <-- + emptySet(), + ) fun lastUsedSource() = preferenceStore.getLong( Preference.appStateKey("last_catalogue_source"), @@ -107,7 +112,9 @@ class SourcePreferences( // KMK --> fun relatedMangas() = preferenceStore.getBoolean("related_mangas", true) + + companion object { + const val PINNED_SOURCES_PREF_KEY = "pinned_catalogues" + } // KMK <-- } - -const val pinnedSourcesPrefKey = "pinned_catalogues" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt index ff35c601e..731f7bad4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt @@ -159,7 +159,7 @@ class BackupCreator( return sourcesBackupCreator(mangas) } - fun backupAppPreferences(options: BackupOptions): List { + /* KMK --> */ suspend /* KMK <-- */ fun backupAppPreferences(options: BackupOptions): List { if (!options.appSettings) return emptyList() return preferenceBackupCreator.createApp(includePrivatePreferences = options.privateSettings) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt index 5b72ce384..3a380ce49 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt @@ -13,6 +13,14 @@ import eu.kanade.tachiyomi.source.preferenceKey import eu.kanade.tachiyomi.source.sourcePreferences import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.domain.category.interactor.GetCategories +import tachiyomi.domain.download.service.DownloadPreferences.Companion.DOWNLOAD_NEW_CATEGORIES_EXCLUDE_PREF_KEY +import tachiyomi.domain.download.service.DownloadPreferences.Companion.DOWNLOAD_NEW_CATEGORIES_PREF_KEY +import tachiyomi.domain.download.service.DownloadPreferences.Companion.REMOVE_EXCLUDE_CATEGORIES_PREF_KEY +import tachiyomi.domain.library.service.LibraryPreferences +import tachiyomi.domain.library.service.LibraryPreferences.Companion.DEFAULT_CATEGORY_PREF_KEY +import tachiyomi.domain.library.service.LibraryPreferences.Companion.LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY +import tachiyomi.domain.library.service.LibraryPreferences.Companion.LIBRARY_UPDATE_CATEGORIES_PREF_KEY import tachiyomi.domain.source.service.SourceManager import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -21,9 +29,48 @@ class PreferenceBackupCreator( private val sourceManager: SourceManager = Injekt.get(), private val preferenceStore: PreferenceStore = Injekt.get(), ) { + // KMK --> + private val getCategories by lazy { Injekt.get() } + private val libraryPreferences by lazy { Injekt.get() } + // <-- + + /* KMK --> */ suspend /* KMK <-- */ fun createApp(includePrivatePreferences: Boolean): List { + // KMK --> + val allCategories = getCategories.await() + // KMK <-- - fun createApp(includePrivatePreferences: Boolean): List { return preferenceStore.getAll().toBackupPreferences() + // KMK --> + .map { backupPreference -> + when (backupPreference.key) { + // Convert CategoryId to CategoryOrder + DEFAULT_CATEGORY_PREF_KEY -> { + val id = (backupPreference.value as IntPreferenceValue).value.toLong() + val value = allCategories.find { it.id == id }?.order?.toInt() + ?: libraryPreferences.defaultCategory().defaultValue() + BackupPreference( + backupPreference.key, + IntPreferenceValue(value), + ) + } + // Convert CategoryId to CategoryOrder + LIBRARY_UPDATE_CATEGORIES_PREF_KEY, LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY, + DOWNLOAD_NEW_CATEGORIES_PREF_KEY, DOWNLOAD_NEW_CATEGORIES_EXCLUDE_PREF_KEY, + REMOVE_EXCLUDE_CATEGORIES_PREF_KEY, + -> { + val categoryIds = (backupPreference.value as StringSetPreferenceValue).value + val categories = allCategories.associateBy({ it.id.toString() }, { it.order.toString() }) + BackupPreference( + backupPreference.key, + StringSetPreferenceValue( + categoryIds.mapNotNull { categories[it] }.toSet(), + ), + ) + } + else -> backupPreference + } + } + // KMK <-- .withPrivatePreferences(includePrivatePreferences) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt index 4c0914b49..676432d2b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt @@ -117,7 +117,12 @@ class BackupRestorer( } // SY <-- if (options.appSettings) { - restoreAppPreferences(backup.backupPreferences) + restoreAppPreferences( + backup.backupPreferences, + // KMK -> + if (options.categories) backup.backupCategories else emptyList(), + // KMK <-- + ) } if (options.sourceSettings) { restoreSourcePreferences(backup.backupSourcePreferences) @@ -190,9 +195,19 @@ class BackupRestorer( } } - private fun CoroutineScope.restoreAppPreferences(preferences: List) = launch { + private fun CoroutineScope.restoreAppPreferences( + preferences: List, + // KMK --> + backupCategories: List, + // KMK <-- + ) = launch { ensureActive() - preferenceRestorer.restoreApp(preferences) + preferenceRestorer.restoreApp( + preferences, + // KMK --> + backupCategories, + // KMK <-- + ) restoreProgress += 1 notifier.showRestoreProgress( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt index c150c0c77..1bd157ae7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt @@ -1,8 +1,9 @@ package eu.kanade.tachiyomi.data.backup.restore.restorers import android.content.Context -import eu.kanade.domain.source.service.pinnedSourcesPrefKey +import eu.kanade.domain.source.service.SourcePreferences.Companion.PINNED_SOURCES_PREF_KEY import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob +import eu.kanade.tachiyomi.data.backup.models.BackupCategory import eu.kanade.tachiyomi.data.backup.models.BackupPreference import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue @@ -15,8 +16,17 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.data.updater.AppUpdateJob import eu.kanade.tachiyomi.source.sourcePreferences import exh.EXHMigrations +import exh.log.xLogE import tachiyomi.core.common.preference.AndroidPreferenceStore import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.domain.category.interactor.GetCategories +import tachiyomi.domain.download.service.DownloadPreferences.Companion.DOWNLOAD_NEW_CATEGORIES_EXCLUDE_PREF_KEY +import tachiyomi.domain.download.service.DownloadPreferences.Companion.DOWNLOAD_NEW_CATEGORIES_PREF_KEY +import tachiyomi.domain.download.service.DownloadPreferences.Companion.REMOVE_EXCLUDE_CATEGORIES_PREF_KEY +import tachiyomi.domain.library.service.LibraryPreferences +import tachiyomi.domain.library.service.LibraryPreferences.Companion.DEFAULT_CATEGORY_PREF_KEY +import tachiyomi.domain.library.service.LibraryPreferences.Companion.LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY +import tachiyomi.domain.library.service.LibraryPreferences.Companion.LIBRARY_UPDATE_CATEGORIES_PREF_KEY import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -24,9 +34,24 @@ class PreferenceRestorer( private val context: Context, private val preferenceStore: PreferenceStore = Injekt.get(), ) { + // KMK --> + private val getCategories by lazy { Injekt.get() } + private val libraryPreferences by lazy { Injekt.get() } + // <-- - fun restoreApp(preferences: List) { - restorePreferences(preferences, preferenceStore) + /* KMK --> */ suspend /* KMK <-- */ fun restoreApp( + preferences: List, + // KMK --> + backupCategories: List, + // KMK <-- + ) { + restorePreferences( + preferences, + preferenceStore, + // KMK --> + backupCategories, + // KMK <-- + ) LibraryUpdateJob.setupTask(context) BackupCreateJob.setupTask(context) @@ -35,58 +60,106 @@ class PreferenceRestorer( // KMK <-- } - fun restoreSource(preferences: List) { + /* KMK --> */ suspend /* KMK <-- */ fun restoreSource(preferences: List) { preferences.forEach { val sourcePrefs = AndroidPreferenceStore(context, sourcePreferences(it.sourceKey)) restorePreferences(it.prefs, sourcePrefs) } } - private fun restorePreferences( + private /* KMK --> */ suspend /* KMK <-- */ fun restorePreferences( toRestore: List, preferenceStore: PreferenceStore, + // KMK --> + backupCategories: List = emptyList(), + // KMK <-- ) { + // KMK --> + val allCategories = getCategories.await() + val categoriesByName = allCategories.associateBy { it.name } + val backupCategoriesByOrder = backupCategories.associateBy { it.order.toString() } + // KMK <-- val prefs = preferenceStore.getAll() toRestore.forEach { (key, value) -> - when (value) { - is IntPreferenceValue -> { - if (prefs[key] is Int?) { - preferenceStore.getInt(key).set(value.value) - } - } - is LongPreferenceValue -> { - if (prefs[key] is Long?) { - preferenceStore.getLong(key).set(value.value) - } - } - is FloatPreferenceValue -> { - if (prefs[key] is Float?) { - preferenceStore.getFloat(key).set(value.value) - } - } - is StringPreferenceValue -> { - if (prefs[key] is String?) { - preferenceStore.getString(key).set(value.value) - } - } - is BooleanPreferenceValue -> { - if (prefs[key] is Boolean?) { - preferenceStore.getBoolean(key).set(value.value) - } - } - is StringSetPreferenceValue -> { - if (prefs[key] is Set<*>?) { - preferenceStore.getStringSet(key).set( + // KMK --> + try { + // KMK <-- + when (value) { + is IntPreferenceValue -> { + if (prefs[key] is Int?) { // KMK --> - if (key == pinnedSourcesPrefKey) { - EXHMigrations.migratePinnedSources(value.value) - } else { - // KMK <-- - value.value - }, - ) + when (key) { + // Convert CategoryOrder to CategoryId + DEFAULT_CATEGORY_PREF_KEY -> { + if (backupCategories.isNotEmpty()) { + val order = value.value.toLong() + val newValue = backupCategories.find { it.order == order } + ?.let { + categoriesByName[it.name]?.id?.toInt() + } + ?: libraryPreferences.defaultCategory().defaultValue() + + preferenceStore.getInt(key).set(newValue) + } + } + else -> + // KMK <-- + preferenceStore.getInt(key).set(value.value) + } + } + } + is LongPreferenceValue -> { + if (prefs[key] is Long?) { + preferenceStore.getLong(key).set(value.value) + } + } + is FloatPreferenceValue -> { + if (prefs[key] is Float?) { + preferenceStore.getFloat(key).set(value.value) + } + } + is StringPreferenceValue -> { + if (prefs[key] is String?) { + preferenceStore.getString(key).set(value.value) + } + } + is BooleanPreferenceValue -> { + if (prefs[key] is Boolean?) { + preferenceStore.getBoolean(key).set(value.value) + } + } + is StringSetPreferenceValue -> { + if (prefs[key] is Set<*>?) { + // KMK --> + when (key) { + PINNED_SOURCES_PREF_KEY -> { + EXHMigrations.migratePinnedSources(value.value) + } + // Convert CategoryOrder to CategoryId + LIBRARY_UPDATE_CATEGORIES_PREF_KEY, LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY, + DOWNLOAD_NEW_CATEGORIES_PREF_KEY, DOWNLOAD_NEW_CATEGORIES_EXCLUDE_PREF_KEY, + REMOVE_EXCLUDE_CATEGORIES_PREF_KEY, + -> { + val newValue = value.value.mapNotNull { order -> + backupCategoriesByOrder[order]?.let { backupCategory -> + categoriesByName[backupCategory.name]?.id?.toString() + } + }.toSet() + if (newValue.isNotEmpty()) { + preferenceStore.getStringSet(key).set(newValue) + } + } + else -> + // KMK <-- + preferenceStore.getStringSet(key).set(value.value) + } + } } } + // KMK --> + } catch (e: Exception) { + xLogE("Failed to restore preference <$key>", e) + // KMK <-- } } } diff --git a/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt b/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt index 304f458cb..2261bc5c2 100644 --- a/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt @@ -27,19 +27,25 @@ class DownloadPreferences( fun removeBookmarkedChapters() = preferenceStore.getBoolean("pref_remove_bookmarked", false) fun removeExcludeCategories() = preferenceStore.getStringSet( - "remove_exclude_categories", + // KMK --> + REMOVE_EXCLUDE_CATEGORIES_PREF_KEY, + // KMK <-- emptySet(), ) fun downloadNewChapters() = preferenceStore.getBoolean("download_new", false) fun downloadNewChapterCategories() = preferenceStore.getStringSet( - "download_new_categories", + // KMK --> + DOWNLOAD_NEW_CATEGORIES_PREF_KEY, + // KMK <-- emptySet(), ) fun downloadNewChapterCategoriesExclude() = preferenceStore.getStringSet( - "download_new_categories_exclude", + // KMK --> + DOWNLOAD_NEW_CATEGORIES_EXCLUDE_PREF_KEY, + // KMK <-- emptySet(), ) @@ -47,5 +53,11 @@ class DownloadPreferences( // KMK --> fun downloadCacheRenewInterval() = preferenceStore.getInt("download_cache_renew_interval", 1) + + companion object { + const val REMOVE_EXCLUDE_CATEGORIES_PREF_KEY = "remove_exclude_categories" + const val DOWNLOAD_NEW_CATEGORIES_PREF_KEY = "download_new_categories" + const val DOWNLOAD_NEW_CATEGORIES_EXCLUDE_PREF_KEY = "download_new_categories_exclude" + } // KMK <-- } diff --git a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt index 29a209921..6e02b1cc8 100644 --- a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt @@ -144,7 +144,12 @@ class LibraryPreferences( // region Category - fun defaultCategory() = preferenceStore.getInt("default_category", -1) + fun defaultCategory() = preferenceStore.getInt( + // KMK --> + DEFAULT_CATEGORY_PREF_KEY, + // KMK <-- + -1, + ) fun lastUsedCategory() = preferenceStore.getInt(Preference.appStateKey("last_used_category"), 0) @@ -158,10 +163,17 @@ class LibraryPreferences( fun showHiddenCategories() = preferenceStore.getBoolean("hide_hidden_categories", false) // KMK <-- - fun updateCategories() = preferenceStore.getStringSet("library_update_categories", emptySet()) + fun updateCategories() = preferenceStore.getStringSet( + // KMK --> + LIBRARY_UPDATE_CATEGORIES_PREF_KEY, + // KMK <-- + emptySet(), + ) fun updateCategoriesExclude() = preferenceStore.getStringSet( - "library_update_categories_exclude", + // KMK --> + LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY, + // KMK <-- emptySet(), ) @@ -255,5 +267,11 @@ class LibraryPreferences( const val MANGA_HAS_UNREAD = "manga_fully_read" const val MANGA_NON_READ = "manga_started" const val MANGA_OUTSIDE_RELEASE_PERIOD = "manga_outside_release_period" + + // KMK --> + const val DEFAULT_CATEGORY_PREF_KEY = "default_category" + const val LIBRARY_UPDATE_CATEGORIES_PREF_KEY = "library_update_categories" + const val LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY = "library_update_categories_exclude" + // KMK <-- } }