diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt index 00985ff23..f2028e16a 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt @@ -147,6 +147,7 @@ object SettingsBackupScreen : SearchableSettings { BackupConst.BACKUP_CHAPTER to R.string.chapters, BackupConst.BACKUP_TRACK to R.string.track, BackupConst.BACKUP_HISTORY to R.string.history, + BackupConst.BACKUP_APP_PREFS to R.string.app_settings, // SY --> BackupConst.BACKUP_CUSTOM_INFO to R.string.custom_entry_info, BackupConst.BACKUP_READ_MANGA to R.string.all_read_entries, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt index 9c402fde4..77432ddcf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt @@ -4,18 +4,26 @@ package eu.kanade.tachiyomi.data.backup internal object BackupConst { const val BACKUP_CATEGORY = 0x1 const val BACKUP_CATEGORY_MASK = 0x1 + const val BACKUP_CHAPTER = 0x2 const val BACKUP_CHAPTER_MASK = 0x2 + const val BACKUP_HISTORY = 0x4 const val BACKUP_HISTORY_MASK = 0x4 + const val BACKUP_TRACK = 0x8 const val BACKUP_TRACK_MASK = 0x8 + const val BACKUP_APP_PREFS = 0x10 + const val BACKUP_APP_PREFS_MASK = 0x10 + // SY --> - const val BACKUP_CUSTOM_INFO = 0x10 - const val BACKUP_CUSTOM_INFO_MASK = 0x10 - const val BACKUP_READ_MANGA = 0x20 - const val BACKUP_READ_MANGA_MASK = 0x20 - const val BACKUP_ALL = 0x3F + const val BACKUP_CUSTOM_INFO = 0x20 + const val BACKUP_CUSTOM_INFO_MASK = 0x20 + + const val BACKUP_READ_MANGA = 0x40 + const val BACKUP_READ_MANGA_MASK = 0x40 + + const val BACKUP_ALL = 0x7F // SY <-- } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt index 829c110f8..a95a8f02e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt @@ -6,6 +6,8 @@ import android.net.Uri import com.hippo.unifile.UniFile import eu.kanade.domain.chapter.model.copyFrom import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_APP_PREFS +import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_APP_PREFS_MASK import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY_MASK import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER @@ -24,9 +26,16 @@ import eu.kanade.tachiyomi.data.backup.models.BackupFlatMetadata import eu.kanade.tachiyomi.data.backup.models.BackupHistory import eu.kanade.tachiyomi.data.backup.models.BackupManga import eu.kanade.tachiyomi.data.backup.models.BackupMergedMangaReference +import eu.kanade.tachiyomi.data.backup.models.BackupPreference import eu.kanade.tachiyomi.data.backup.models.BackupSavedSearch import eu.kanade.tachiyomi.data.backup.models.BackupSerializer import eu.kanade.tachiyomi.data.backup.models.BackupSource +import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue +import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue +import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue +import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue +import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue +import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue import eu.kanade.tachiyomi.data.backup.models.backupCategoryMapper import eu.kanade.tachiyomi.data.backup.models.backupChapterMapper import eu.kanade.tachiyomi.data.backup.models.backupMergedMangaReferenceMapper @@ -44,6 +53,7 @@ import logcat.LogPriority import okio.buffer import okio.gzip import okio.sink +import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.util.system.logcat import tachiyomi.data.DatabaseHandler import tachiyomi.data.Manga_sync @@ -84,6 +94,7 @@ class BackupManager( private val getCategories: GetCategories = Injekt.get() private val getFavorites: GetFavorites = Injekt.get() private val getHistory: GetHistory = Injekt.get() + private val preferenceStore: PreferenceStore = Injekt.get() // SY --> private val getMergedManga: GetMergedManga = Injekt.get() @@ -117,6 +128,7 @@ class BackupManager( backupCategories(flags), emptyList(), prepExtensionInfoForSync(databaseManga), + backupAppPreferences(flags), // SY --> backupSavedSearches(), // SY <-- @@ -172,7 +184,7 @@ class BackupManager( } } - fun prepExtensionInfoForSync(mangas: List): List { + private fun prepExtensionInfoForSync(mangas: List): List { return mangas .asSequence() .map(Manga::source) @@ -187,7 +199,7 @@ class BackupManager( * * @return list of [BackupCategory] to be backed up */ - suspend fun backupCategories(options: Int): List { + private suspend fun backupCategories(options: Int): List { // Check if user wants category information in backup return if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) { getCategories.await() @@ -198,7 +210,7 @@ class BackupManager( } } - suspend fun backupMangas(mangas: List, flags: Int): List { + private suspend fun backupMangas(mangas: List, flags: Int): List { return mangas.map { backupManga(it, flags) } @@ -286,6 +298,25 @@ class BackupManager( return mangaObject } + @Suppress("UNCHECKED_CAST") + private fun backupAppPreferences(flags: Int): List { + if (flags and BACKUP_APP_PREFS_MASK != BACKUP_APP_PREFS) return emptyList() + + return preferenceStore.getAll().mapNotNull { (key, value) -> + when (value) { + is Int -> BackupPreference(key, IntPreferenceValue(value)) + is Long -> BackupPreference(key, LongPreferenceValue(value)) + is Float -> BackupPreference(key, FloatPreferenceValue(value)) + is String -> BackupPreference(key, StringPreferenceValue(value)) + is Boolean -> BackupPreference(key, BooleanPreferenceValue(value)) + is Set<*> -> (value as? Set)?.let { + BackupPreference(key, StringSetPreferenceValue(it)) + } + else -> null + } + } + } + internal suspend fun restoreExistingManga(manga: Manga, dbManga: Mangas): Manga { var updatedManga = manga.copy(id = dbManga._id) updatedManga = updatedManga.copyFrom(dbManga) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt index aef0c3c0d..85b7a1c64 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt @@ -9,16 +9,23 @@ import eu.kanade.tachiyomi.data.backup.models.BackupFlatMetadata import eu.kanade.tachiyomi.data.backup.models.BackupHistory import eu.kanade.tachiyomi.data.backup.models.BackupManga import eu.kanade.tachiyomi.data.backup.models.BackupMergedMangaReference +import eu.kanade.tachiyomi.data.backup.models.BackupPreference import eu.kanade.tachiyomi.data.backup.models.BackupSavedSearch import eu.kanade.tachiyomi.data.backup.models.BackupSource +import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue +import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue +import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue +import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue +import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue +import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue import eu.kanade.tachiyomi.util.BackupUtil import eu.kanade.tachiyomi.util.system.createFileInCacheDir import exh.EXHMigrations import exh.source.MERGED_SOURCE_ID import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.isActive +import tachiyomi.core.preference.PreferenceStore import tachiyomi.domain.chapter.model.Chapter -import tachiyomi.domain.chapter.repository.ChapterRepository import tachiyomi.domain.manga.interactor.FetchInterval import tachiyomi.domain.manga.model.CustomMangaInfo import tachiyomi.domain.manga.model.Manga @@ -36,8 +43,8 @@ class BackupRestorer( private val notifier: BackupNotifier, ) { private val updateManga: UpdateManga = Injekt.get() - private val chapterRepository: ChapterRepository = Injekt.get() private val fetchInterval: FetchInterval = Injekt.get() + private val preferenceStore: PreferenceStore = Injekt.get() private var now = ZonedDateTime.now() private var currentFetchWindow = fetchInterval.getWindow(now) @@ -118,6 +125,8 @@ class BackupRestorer( currentFetchWindow = fetchInterval.getWindow(now) return coroutineScope { + restoreAppPreferences(backup.backupPreferences) + // Restore individual manga, sort by merged source so that merged source manga go last and merged references get the proper ids backup.backupManga /* SY --> */.sortedBy { it.source == MERGED_SOURCE_ID } /* SY <-- */.forEach { if (!isActive) { @@ -128,6 +137,7 @@ class BackupRestorer( } // TODO: optionally trigger online library + tracker update + true } } @@ -257,6 +267,45 @@ class BackupRestorer( // SY <-- } + private fun restoreAppPreferences(preferences: List) { + val prefs = preferenceStore.getAll() + + preferences.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(value.value) + } + } + } + } + } + /** * Called to update dialog in [BackupConst] * diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt index 13eed1563..fedac9c39 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/Backup.kt @@ -11,9 +11,9 @@ import java.util.Locale data class Backup( @ProtoNumber(1) val backupManga: List, @ProtoNumber(2) var backupCategories: List = emptyList(), - // Bump by 100 to specify this is a 0.x value @ProtoNumber(100) var backupBrokenSources: List = emptyList(), @ProtoNumber(101) var backupSources: List = emptyList(), + @ProtoNumber(104) var backupPreferences: List = emptyList(), // SY specific values @ProtoNumber(600) var backupSavedSearches: List = emptyList(), ) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt index 9c50f9307..2d412bb10 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupCategory.kt @@ -9,7 +9,6 @@ class BackupCategory( @ProtoNumber(1) var name: String, @ProtoNumber(2) var order: Long = 0, // @ProtoNumber(3) val updateInterval: Int = 0, 1.x value not used in 0.x - // Bump by 100 to specify this is a 0.x value @ProtoNumber(100) var flags: Long = 0, // SY specific values /*@ProtoNumber(600) var mangaOrder: List = emptyList(),*/ diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupPreference.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupPreference.kt new file mode 100644 index 000000000..791c9706f --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupPreference.kt @@ -0,0 +1,31 @@ +package eu.kanade.tachiyomi.data.backup.models + +import kotlinx.serialization.Serializable +import kotlinx.serialization.protobuf.ProtoNumber + +@Serializable +data class BackupPreference( + @ProtoNumber(1) val key: String, + @ProtoNumber(2) val value: PreferenceValue, +) + +@Serializable +sealed class PreferenceValue + +@Serializable +data class IntPreferenceValue(val value: Int) : PreferenceValue() + +@Serializable +data class LongPreferenceValue(val value: Long) : PreferenceValue() + +@Serializable +data class FloatPreferenceValue(val value: Float) : PreferenceValue() + +@Serializable +data class StringPreferenceValue(val value: String) : PreferenceValue() + +@Serializable +data class BooleanPreferenceValue(val value: Boolean) : PreferenceValue() + +@Serializable +data class StringSetPreferenceValue(val value: Set) : PreferenceValue() diff --git a/core/src/main/java/tachiyomi/core/preference/AndroidPreference.kt b/core/src/main/java/tachiyomi/core/preference/AndroidPreference.kt index a0f2f335c..21f5b1d30 100644 --- a/core/src/main/java/tachiyomi/core/preference/AndroidPreference.kt +++ b/core/src/main/java/tachiyomi/core/preference/AndroidPreference.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn +import tachiyomi.core.util.system.logcat sealed class AndroidPreference( private val preferences: SharedPreferences, @@ -29,7 +30,13 @@ sealed class AndroidPreference( } override fun get(): T { - return read(preferences, key, defaultValue) + return try { + read(preferences, key, defaultValue) + } catch (e: ClassCastException) { + logcat { "Invalid value for $key; deleting" } + delete() + defaultValue + } } override fun set(value: T) { diff --git a/core/src/main/java/tachiyomi/core/preference/AndroidPreferenceStore.kt b/core/src/main/java/tachiyomi/core/preference/AndroidPreferenceStore.kt index c59652da5..67e4db7fc 100644 --- a/core/src/main/java/tachiyomi/core/preference/AndroidPreferenceStore.kt +++ b/core/src/main/java/tachiyomi/core/preference/AndroidPreferenceStore.kt @@ -60,6 +60,10 @@ class AndroidPreferenceStore( deserializer = deserializer, ) } + + override fun getAll(): Map { + return sharedPreferences.all ?: emptyMap() + } } private val SharedPreferences.keyFlow diff --git a/core/src/main/java/tachiyomi/core/preference/PreferenceStore.kt b/core/src/main/java/tachiyomi/core/preference/PreferenceStore.kt index f8cc9f890..5b0e9da9b 100644 --- a/core/src/main/java/tachiyomi/core/preference/PreferenceStore.kt +++ b/core/src/main/java/tachiyomi/core/preference/PreferenceStore.kt @@ -20,6 +20,8 @@ interface PreferenceStore { serializer: (T) -> String, deserializer: (String) -> T, ): Preference + + fun getAll(): Map } inline fun > PreferenceStore.getEnum( diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 92e05c245..91e295056 100755 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -493,6 +493,7 @@ Backup is already in progress What do you want to backup? + App settings Creating backup Backup failed Storage permissions not granted