Add source preferences to backups

Closes #1857

Co-authored-by: jmir1 <jmir1@users.noreply.github.com>
This commit is contained in:
arkon 2023-10-08 16:02:03 -04:00
parent 730f3a6e52
commit 0f42b9f154
9 changed files with 63 additions and 16 deletions

View file

@ -148,6 +148,7 @@ object SettingsBackupScreen : SearchableSettings {
BackupConst.BACKUP_TRACK to R.string.track, BackupConst.BACKUP_TRACK to R.string.track,
BackupConst.BACKUP_HISTORY to R.string.history, BackupConst.BACKUP_HISTORY to R.string.history,
BackupConst.BACKUP_APP_PREFS to R.string.app_settings, BackupConst.BACKUP_APP_PREFS to R.string.app_settings,
BackupConst.BACKUP_SOURCE_PREFS to R.string.source_settings,
) )
} }
val flags = remember { choices.keys.toMutableStateList() } val flags = remember { choices.keys.toMutableStateList() }

View file

@ -17,5 +17,8 @@ internal object BackupConst {
const val BACKUP_APP_PREFS = 0x10 const val BACKUP_APP_PREFS = 0x10
const val BACKUP_APP_PREFS_MASK = 0x10 const val BACKUP_APP_PREFS_MASK = 0x10
const val BACKUP_ALL = 0x1F const val BACKUP_SOURCE_PREFS = 0x20
const val BACKUP_SOURCE_PREFS_MASK = 0x20
const val BACKUP_ALL = 0x3F
} }

View file

@ -12,12 +12,15 @@ 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_CATEGORY_MASK
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER_MASK import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER_MASK
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_SOURCE_PREFS
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_SOURCE_PREFS_MASK
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY_MASK import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY_MASK
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK
import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK_MASK import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK_MASK
import eu.kanade.tachiyomi.data.backup.models.Backup import eu.kanade.tachiyomi.data.backup.models.Backup
import eu.kanade.tachiyomi.data.backup.models.BackupCategory import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
import eu.kanade.tachiyomi.data.backup.models.BackupHistory import eu.kanade.tachiyomi.data.backup.models.BackupHistory
import eu.kanade.tachiyomi.data.backup.models.BackupManga import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.backup.models.BackupPreference import eu.kanade.tachiyomi.data.backup.models.BackupPreference
@ -32,7 +35,10 @@ import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
import eu.kanade.tachiyomi.data.backup.models.backupCategoryMapper import eu.kanade.tachiyomi.data.backup.models.backupCategoryMapper
import eu.kanade.tachiyomi.data.backup.models.backupChapterMapper import eu.kanade.tachiyomi.data.backup.models.backupChapterMapper
import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper import eu.kanade.tachiyomi.data.backup.models.backupTrackMapper
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.copyFrom import eu.kanade.tachiyomi.source.model.copyFrom
import eu.kanade.tachiyomi.source.preferenceKey
import eu.kanade.tachiyomi.source.sourcePreferences
import eu.kanade.tachiyomi.util.system.hasPermission import eu.kanade.tachiyomi.util.system.hasPermission
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import logcat.LogPriority import logcat.LogPriority
@ -94,6 +100,7 @@ class BackupManager(
emptyList(), emptyList(),
prepExtensionInfoForSync(databaseManga), prepExtensionInfoForSync(databaseManga),
backupAppPreferences(flags), backupAppPreferences(flags),
backupSourcePreferences(flags),
) )
var file: UniFile? = null var file: UniFile? = null
@ -232,12 +239,28 @@ class BackupManager(
return mangaObject return mangaObject
} }
@Suppress("UNCHECKED_CAST")
private fun backupAppPreferences(flags: Int): List<BackupPreference> { private fun backupAppPreferences(flags: Int): List<BackupPreference> {
if (flags and BACKUP_APP_PREFS_MASK != BACKUP_APP_PREFS) return emptyList() if (flags and BACKUP_APP_PREFS_MASK != BACKUP_APP_PREFS) return emptyList()
return preferenceStore.getAll() return preferenceStore.getAll().toBackupPreferences()
.filterKeys { !Preference.isPrivate(it) } }
private fun backupSourcePreferences(flags: Int): List<BackupSourcePreferences> {
if (flags and BACKUP_SOURCE_PREFS_MASK != BACKUP_SOURCE_PREFS) return emptyList()
return sourceManager.getOnlineSources()
.filterIsInstance<ConfigurableSource>()
.map {
BackupSourcePreferences(
it.preferenceKey(),
it.sourcePreferences().all.toBackupPreferences()
)
}
}
@Suppress("UNCHECKED_CAST")
private fun Map<String, *>.toBackupPreferences(): List<BackupPreference> {
return this.filterKeys { !Preference.isPrivate(it) }
.mapNotNull { (key, value) -> .mapNotNull { (key, value) ->
when (value) { when (value) {
is Int -> BackupPreference(key, IntPreferenceValue(value)) is Int -> BackupPreference(key, IntPreferenceValue(value))

View file

@ -9,16 +9,19 @@ import eu.kanade.tachiyomi.data.backup.models.BackupHistory
import eu.kanade.tachiyomi.data.backup.models.BackupManga import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.backup.models.BackupPreference import eu.kanade.tachiyomi.data.backup.models.BackupPreference
import eu.kanade.tachiyomi.data.backup.models.BackupSource import eu.kanade.tachiyomi.data.backup.models.BackupSource
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue import eu.kanade.tachiyomi.data.backup.models.BooleanPreferenceValue
import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue import eu.kanade.tachiyomi.data.backup.models.FloatPreferenceValue
import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue import eu.kanade.tachiyomi.data.backup.models.IntPreferenceValue
import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue import eu.kanade.tachiyomi.data.backup.models.LongPreferenceValue
import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
import eu.kanade.tachiyomi.source.sourcePreferences
import eu.kanade.tachiyomi.util.BackupUtil import eu.kanade.tachiyomi.util.BackupUtil
import eu.kanade.tachiyomi.util.system.createFileInCacheDir import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import tachiyomi.core.preference.AndroidPreferenceStore
import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.PreferenceStore
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.interactor.FetchInterval import tachiyomi.domain.manga.interactor.FetchInterval
@ -114,6 +117,7 @@ class BackupRestorer(
return coroutineScope { return coroutineScope {
restoreAppPreferences(backup.backupPreferences) restoreAppPreferences(backup.backupPreferences)
restoreSourcePreferences(backup.backupSourcePreferences)
// Restore individual manga // Restore individual manga
backup.backupManga.forEach { backup.backupManga.forEach {
@ -211,9 +215,22 @@ class BackupRestorer(
} }
private fun restoreAppPreferences(preferences: List<BackupPreference>) { private fun restoreAppPreferences(preferences: List<BackupPreference>) {
val prefs = preferenceStore.getAll() restorePreferences(preferences, preferenceStore)
}
preferences.forEach { (key, value) -> private fun restoreSourcePreferences(preferences: List<BackupSourcePreferences>) {
preferences.forEach {
val sourcePrefs = AndroidPreferenceStore(context, sourcePreferences(it.sourceKey))
restorePreferences(it.prefs, sourcePrefs)
}
}
private fun restorePreferences(
toRestore: List<BackupPreference>,
preferenceStore: PreferenceStore,
) {
val prefs = preferenceStore.getAll()
toRestore.forEach { (key, value) ->
when (value) { when (value) {
is IntPreferenceValue -> { is IntPreferenceValue -> {
if (prefs[key] is Int?) { if (prefs[key] is Int?) {
@ -249,13 +266,6 @@ class BackupRestorer(
} }
} }
/**
* Called to update dialog in [BackupConst]
*
* @param progress restore progress
* @param amount total restoreAmount of manga
* @param title title of restored manga
*/
private fun showRestoreProgress(progress: Int, amount: Int, title: String, contentTitle: String) { private fun showRestoreProgress(progress: Int, amount: Int, title: String, contentTitle: String) {
notifier.showRestoreProgress(title, contentTitle, progress, amount) notifier.showRestoreProgress(title, contentTitle, progress, amount)
} }

View file

@ -14,6 +14,7 @@ data class Backup(
@ProtoNumber(100) var backupBrokenSources: List<BrokenBackupSource> = emptyList(), @ProtoNumber(100) var backupBrokenSources: List<BrokenBackupSource> = emptyList(),
@ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(), @ProtoNumber(101) var backupSources: List<BackupSource> = emptyList(),
@ProtoNumber(104) var backupPreferences: List<BackupPreference> = emptyList(), @ProtoNumber(104) var backupPreferences: List<BackupPreference> = emptyList(),
@ProtoNumber(105) var backupSourcePreferences: List<BackupSourcePreferences> = emptyList(),
) { ) {
companion object { companion object {

View file

@ -9,6 +9,12 @@ data class BackupPreference(
@ProtoNumber(2) val value: PreferenceValue, @ProtoNumber(2) val value: PreferenceValue,
) )
@Serializable
data class BackupSourcePreferences(
@ProtoNumber(1) val sourceKey: String,
@ProtoNumber(2) val prefs: List<BackupPreference>,
)
@Serializable @Serializable
sealed class PreferenceValue sealed class PreferenceValue

View file

@ -15,10 +15,9 @@ import tachiyomi.core.preference.AndroidPreference.StringSetPrimitive
class AndroidPreferenceStore( class AndroidPreferenceStore(
context: Context, context: Context,
private val sharedPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context),
) : PreferenceStore { ) : PreferenceStore {
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
private val keyFlow = sharedPreferences.keyFlow private val keyFlow = sharedPreferences.keyFlow
override fun getString(key: String, defaultValue: String): Preference<String> { override fun getString(key: String, defaultValue: String): Preference<String> {

View file

@ -494,6 +494,7 @@
<string name="backup_in_progress">Backup is already in progress</string> <string name="backup_in_progress">Backup is already in progress</string>
<string name="backup_choice">What do you want to backup?</string> <string name="backup_choice">What do you want to backup?</string>
<string name="app_settings">App settings</string> <string name="app_settings">App settings</string>
<string name="source_settings">Source settings</string>
<string name="creating_backup">Creating backup</string> <string name="creating_backup">Creating backup</string>
<string name="creating_backup_error">Backup failed</string> <string name="creating_backup_error">Backup failed</string>
<string name="missing_storage_permission">Storage permissions not granted</string> <string name="missing_storage_permission">Storage permissions not granted</string>

View file

@ -19,8 +19,11 @@ interface ConfigurableSource : Source {
fun setupPreferenceScreen(screen: PreferenceScreen) fun setupPreferenceScreen(screen: PreferenceScreen)
} }
private fun ConfigurableSource.preferenceKey(): String = "source_$id" fun ConfigurableSource.preferenceKey(): String = "source_$id"
// TODO: use getSourcePreferences once all extensions are on ext-lib 1.5 // TODO: use getSourcePreferences once all extensions are on ext-lib 1.5
fun ConfigurableSource.sourcePreferences(): SharedPreferences = fun ConfigurableSource.sourcePreferences(): SharedPreferences =
Injekt.get<Application>().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE) Injekt.get<Application>().getSharedPreferences(preferenceKey(), Context.MODE_PRIVATE)
fun sourcePreferences(key: String): SharedPreferences =
Injekt.get<Application>().getSharedPreferences(key, Context.MODE_PRIVATE)