Change backup proto & improve code of backup/restore category related preferences (mihonapp/mihon#1726)

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
(cherry picked from commit e1724d1aa0e3340e1404cfd80bd264831d86a879)
This commit is contained in:
Cuong-Tran 2025-02-25 05:04:39 +07:00
parent e062b97f5f
commit 707d9854ec
No known key found for this signature in database
GPG key ID: 733AA7624B9315C2
8 changed files with 91 additions and 173 deletions

View file

@ -25,6 +25,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
- Fix Bangumi and MAL tracking 401 errors due to Mihon sending expired credentials ([@MajorTanya](https://github.com/MajorTanya)) ([#1681](https://github.com/mihonapp/mihon/pull/1681), [#1682](https://github.com/mihonapp/mihon/pull/1682))
- Fix certain Infinix devices being unable to use any "Open link in browser" actions, including tracker setup ([@MajorTanya](https://github.com/MajorTanya)) ([#1684](https://github.com/mihonapp/mihon/pull/1684))
- Fix App's preferences referencing deleted categories ([@cuong-tran](https://github.com/cuong-tran)) ([#1734](https://github.com/mihonapp/mihon/pull/1734))
- Fix backup/restore of category related preferences ([@cuong-tran](https://github.com/cuong-tran)) ([#1726](https://github.com/mihonapp/mihon/pull/1726))
### Other
- Add zoned "Current time" to debug info and include year & timezone in logcat output ([@MajorTanya](https://github.com/MajorTanya)) ([#1672](https://github.com/mihonapp/mihon/pull/1672))

View file

@ -13,14 +13,6 @@ 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
@ -29,48 +21,9 @@ class PreferenceBackupCreator(
private val sourceManager: SourceManager = Injekt.get(),
private val preferenceStore: PreferenceStore = Injekt.get(),
) {
// KMK -->
private val getCategories by lazy { Injekt.get<GetCategories>() }
private val libraryPreferences by lazy { Injekt.get<LibraryPreferences>() }
// <--
/* KMK --> */ suspend /* KMK <-- */ fun createApp(includePrivatePreferences: Boolean): List<BackupPreference> {
// KMK -->
val allCategories = getCategories.await()
// KMK <--
fun createApp(includePrivatePreferences: Boolean): List<BackupPreference> {
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)
}

View file

@ -8,6 +8,7 @@ import tachiyomi.domain.category.model.Category
class BackupCategory(
@ProtoNumber(1) var name: String,
@ProtoNumber(2) var order: Long = 0,
@ProtoNumber(3) var id: Long = 0,
// @ProtoNumber(3) val updateInterval: Int = 0, 1.x value not used in 0.x
@ProtoNumber(100) var flags: Long = 0,
// KMK -->
@ -30,6 +31,7 @@ class BackupCategory(
val backupCategoryMapper = { category: Category ->
BackupCategory(
id = category.id,
name = category.name,
order = category.order,
flags = category.flags,

View file

@ -117,12 +117,7 @@ class BackupRestorer(
}
// SY <--
if (options.appSettings) {
restoreAppPreferences(
backup.backupPreferences,
// KMK ->
if (options.categories) backup.backupCategories else emptyList(),
// KMK <--
)
restoreAppPreferences(backup.backupPreferences, backup.backupCategories.takeIf { options.categories })
}
if (options.sourceSettings) {
restoreSourcePreferences(backup.backupSourcePreferences)
@ -197,16 +192,12 @@ class BackupRestorer(
private fun CoroutineScope.restoreAppPreferences(
preferences: List<BackupPreference>,
// KMK -->
backupCategories: List<BackupCategory>,
// KMK <--
categories: List<BackupCategory>?,
) = launch {
ensureActive()
preferenceRestorer.restoreApp(
preferences,
// KMK -->
backupCategories,
// KMK <--
categories,
)
restoreProgress += 1

View file

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.data.backup.restore.restorers
import android.content.Context
import eu.kanade.domain.source.service.SourcePreferences.Companion.PINNED_SOURCES_PREF_KEY
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
@ -19,38 +19,27 @@ import exh.EXHMigrations
import exh.log.xLogE
import tachiyomi.core.common.preference.AndroidPreferenceStore
import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.preference.plusAssign
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.category.model.Category
import tachiyomi.domain.download.service.DownloadPreferences
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
class PreferenceRestorer(
private val context: Context,
private val getCategories: GetCategories = Injekt.get(),
private val preferenceStore: PreferenceStore = Injekt.get(),
) {
// KMK -->
private val getCategories by lazy { Injekt.get<GetCategories>() }
private val libraryPreferences by lazy { Injekt.get<LibraryPreferences>() }
// <--
/* KMK --> */ suspend /* KMK <-- */ fun restoreApp(
suspend fun restoreApp(
preferences: List<BackupPreference>,
// KMK -->
backupCategories: List<BackupCategory>,
// KMK <--
backupCategories: List<BackupCategory>?,
) {
restorePreferences(
preferences,
preferenceStore,
// KMK -->
backupCategories,
// KMK <--
)
LibraryUpdateJob.setupTask(context)
@ -60,52 +49,35 @@ class PreferenceRestorer(
// KMK <--
}
/* KMK --> */ suspend /* KMK <-- */ fun restoreSource(preferences: List<BackupSourcePreferences>) {
suspend fun restoreSource(preferences: List<BackupSourcePreferences>) {
preferences.forEach {
val sourcePrefs = AndroidPreferenceStore(context, sourcePreferences(it.sourceKey))
restorePreferences(it.prefs, sourcePrefs)
}
}
private /* KMK --> */ suspend /* KMK <-- */ fun restorePreferences(
private suspend fun restorePreferences(
toRestore: List<BackupPreference>,
preferenceStore: PreferenceStore,
// KMK -->
backupCategories: List<BackupCategory> = emptyList(),
// KMK <--
backupCategories: List<BackupCategory>? = null,
) {
// KMK -->
val allCategories = getCategories.await()
val allCategories = if (backupCategories != null) getCategories.await() else emptyList()
val categoriesByName = allCategories.associateBy { it.name }
val backupCategoriesByOrder = backupCategories.associateBy { it.order.toString() }
// KMK <--
val backupCategoriesById = backupCategories?.associateBy { it.id.toString() }.orEmpty()
val prefs = preferenceStore.getAll()
toRestore.forEach { (key, value) ->
// KMK -->
try {
// KMK <--
when (value) {
is IntPreferenceValue -> {
if (prefs[key] is Int?) {
// KMK -->
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)
val newValue = if (key == LibraryPreferences.DEFAULT_CATEGORY_PREF_KEY) {
backupCategoriesById[value.value.toString()]
?.let { categoriesByName[it.name]?.id?.toInt() }
} else {
value.value
}
newValue?.let { preferenceStore.getInt(key).set(it) }
}
}
is LongPreferenceValue -> {
@ -130,37 +102,53 @@ class PreferenceRestorer(
}
is StringSetPreferenceValue -> {
if (prefs[key] is Set<*>?) {
// KMK -->
when (key) {
PINNED_SOURCES_PREF_KEY -> {
EXHMigrations.migratePinnedSources(value.value)
val restored = restoreCategoriesPreference(
key,
value.value,
preferenceStore,
backupCategoriesById,
categoriesByName,
)
if (!restored) {
// KMK -->
when (key) {
SourcePreferences.PINNED_SOURCES_PREF_KEY ->
EXHMigrations.migratePinnedSources(value.value)
else ->
// KMK <--
preferenceStore.getStringSet(key).set(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) {
// KMK -->
xLogE("Failed to restore preference <$key>", e)
// KMK <--
}
}
}
private fun restoreCategoriesPreference(
key: String,
value: Set<String>,
preferenceStore: PreferenceStore,
backupCategoriesById: Map<String, BackupCategory>,
categoriesByName: Map<String, Category>,
): Boolean {
val categoryPreferences = LibraryPreferences.categoryPreferenceKeys + DownloadPreferences.categoryPreferenceKeys
if (key !in categoryPreferences) return false
val ids = value.mapNotNull {
backupCategoriesById[it]?.name?.let { name ->
categoriesByName[name]?.id?.toString()
}
}
if (ids.isNotEmpty()) {
preferenceStore.getStringSet(key) += ids
}
return true
}
}

View file

@ -57,6 +57,10 @@ operator fun <T> Preference<Set<T>>.plusAssign(item: T) {
set(get() + item)
}
operator fun <T> Preference<Set<T>>.plusAssign(items: Iterable<T>) {
set(get() + items)
}
operator fun <T> Preference<Set<T>>.minusAssign(item: T) {
set(get() - item)
}

View file

@ -26,38 +26,29 @@ class DownloadPreferences(
fun removeBookmarkedChapters() = preferenceStore.getBoolean("pref_remove_bookmarked", false)
fun removeExcludeCategories() = preferenceStore.getStringSet(
// KMK -->
REMOVE_EXCLUDE_CATEGORIES_PREF_KEY,
// KMK <--
emptySet(),
)
fun removeExcludeCategories() = preferenceStore.getStringSet(REMOVE_EXCLUDE_CATEGORIES_PREF_KEY, emptySet())
fun downloadNewChapters() = preferenceStore.getBoolean("download_new", false)
fun downloadNewChapterCategories() = preferenceStore.getStringSet(
// KMK -->
DOWNLOAD_NEW_CATEGORIES_PREF_KEY,
// KMK <--
emptySet(),
)
fun downloadNewChapterCategories() = preferenceStore.getStringSet(DOWNLOAD_NEW_CATEGORIES_PREF_KEY, emptySet())
fun downloadNewChapterCategoriesExclude() = preferenceStore.getStringSet(
// KMK -->
DOWNLOAD_NEW_CATEGORIES_EXCLUDE_PREF_KEY,
// KMK <--
emptySet(),
)
fun downloadNewChapterCategoriesExclude() =
preferenceStore.getStringSet(DOWNLOAD_NEW_CATEGORIES_EXCLUDE_PREF_KEY, emptySet())
fun downloadNewUnreadChaptersOnly() = preferenceStore.getBoolean("download_new_unread_chapters_only", false)
// KMK -->
fun downloadCacheRenewInterval() = preferenceStore.getInt("download_cache_renew_interval", 1)
// KMK <--
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"
private const val REMOVE_EXCLUDE_CATEGORIES_PREF_KEY = "remove_exclude_categories"
private const val DOWNLOAD_NEW_CATEGORIES_PREF_KEY = "download_new_categories"
private const val DOWNLOAD_NEW_CATEGORIES_EXCLUDE_PREF_KEY = "download_new_categories_exclude"
val categoryPreferenceKeys = setOf(
REMOVE_EXCLUDE_CATEGORIES_PREF_KEY,
DOWNLOAD_NEW_CATEGORIES_PREF_KEY,
DOWNLOAD_NEW_CATEGORIES_EXCLUDE_PREF_KEY,
)
}
// KMK <--
}

View file

@ -144,12 +144,7 @@ class LibraryPreferences(
// region Category
fun defaultCategory() = preferenceStore.getInt(
// KMK -->
DEFAULT_CATEGORY_PREF_KEY,
// KMK <--
-1,
)
fun defaultCategory() = preferenceStore.getInt(DEFAULT_CATEGORY_PREF_KEY, -1)
fun lastUsedCategory() = preferenceStore.getInt(Preference.appStateKey("last_used_category"), 0)
@ -163,19 +158,9 @@ class LibraryPreferences(
fun showHiddenCategories() = preferenceStore.getBoolean("hide_hidden_categories", false)
// KMK <--
fun updateCategories() = preferenceStore.getStringSet(
// KMK -->
LIBRARY_UPDATE_CATEGORIES_PREF_KEY,
// KMK <--
emptySet(),
)
fun updateCategories() = preferenceStore.getStringSet(LIBRARY_UPDATE_CATEGORIES_PREF_KEY, emptySet())
fun updateCategoriesExclude() = preferenceStore.getStringSet(
// KMK -->
LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY,
// KMK <--
emptySet(),
)
fun updateCategoriesExclude() = preferenceStore.getStringSet(LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY, emptySet())
// endregion
@ -268,10 +253,13 @@ class LibraryPreferences(
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 <--
private const val LIBRARY_UPDATE_CATEGORIES_PREF_KEY = "library_update_categories"
private const val LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY = "library_update_categories_exclude"
val categoryPreferenceKeys = setOf(
DEFAULT_CATEGORY_PREF_KEY,
LIBRARY_UPDATE_CATEGORIES_PREF_KEY,
LIBRARY_UPDATE_CATEGORIES_EXCLUDE_PREF_KEY,
)
}
}