From 711ca69876e4b6bc7362d246822c5095fa308c2a Mon Sep 17 00:00:00 2001 From: arkon Date: Thu, 21 Dec 2023 22:16:42 -0500 Subject: [PATCH] Foundations for partial restores Related to #3136 (cherry picked from commit 83a67feb48c4e40994a334520c907f71d2fbf75e) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt --- .../settings/screen/SettingsDataScreen.kt | 29 +++++++- .../data/backup/restore/BackupRestoreJob.kt | 19 ++++- .../data/backup/restore/BackupRestorer.kt | 72 ++++++++++++++++--- 3 files changed, 105 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index 11f2aebbd..f867e6d1b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -40,6 +40,7 @@ import eu.kanade.presentation.util.relativeTimeSpanString import eu.kanade.tachiyomi.data.backup.BackupFileValidator import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob +import eu.kanade.tachiyomi.data.backup.restore.RestoreOptions import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.cache.PagePreviewCache import eu.kanade.tachiyomi.util.storage.DiskUtil @@ -251,7 +252,19 @@ object SettingsDataScreen : SearchableSettings { confirmButton = { TextButton( onClick = { - BackupRestoreJob.start(context, err.uri) + BackupRestoreJob.start( + context = context, + uri = err.uri, + // TODO: allow user-selectable restore options + options = RestoreOptions( + appSettings = true, + sourceSettings = true, + library = true, + // SY --> + savedSearches = true, + // SY <-- + ), + ) onDismissRequest() }, ) { @@ -285,7 +298,19 @@ object SettingsDataScreen : SearchableSettings { } if (results.missingSources.isEmpty() && results.missingTrackers.isEmpty()) { - BackupRestoreJob.start(context, it) + BackupRestoreJob.start( + context = context, + uri = it, + // TODO: allow user-selectable restore options + options = RestoreOptions( + appSettings = true, + sourceSettings = true, + library = true, + // SY --> + savedSearches = true, + // SY <-- + ), + ) return@rememberLauncherForActivityResult } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt index 9883eb023..f1b3a28c9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt @@ -30,13 +30,19 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet override suspend fun doWork(): Result { val uri = inputData.getString(LOCATION_URI_KEY)?.toUri() - ?: return Result.failure() + val options = inputData.getBooleanArray(OPTIONS_KEY) + ?.let { RestoreOptions.fromBooleanArray(it) } + + if (uri == null || options == null) { + return Result.failure() + } + val isSync = inputData.getBoolean(SYNC_KEY, false) setForegroundSafely() return try { - BackupRestorer(context, notifier, isSync).restore(uri) + BackupRestorer(context, notifier, isSync).restore(uri, options) Result.success() } catch (e: Exception) { if (e is CancellationException) { @@ -69,10 +75,16 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet return context.workManager.isRunning(TAG) } - fun start(context: Context, uri: Uri, sync: Boolean = false) { + fun start( + context: Context, + uri: Uri, + options: RestoreOptions, + sync: Boolean = false, + ) { val inputData = workDataOf( LOCATION_URI_KEY to uri.toString(), SYNC_KEY to sync, + OPTIONS_KEY to options.toBooleanArray(), ) val request = OneTimeWorkRequestBuilder() .addTag(TAG) @@ -91,3 +103,4 @@ private const val TAG = "BackupRestore" private const val LOCATION_URI_KEY = "location_uri" // String private const val SYNC_KEY = "sync" // Boolean +private const val OPTIONS_KEY = "options" // BooleanArray 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 053ecbe27..b6f6e05b6 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 @@ -45,10 +45,10 @@ class BackupRestorer( */ private var sourceMapping: Map = emptyMap() - suspend fun restore(uri: Uri) { + suspend fun restore(uri: Uri, options: RestoreOptions) { val startTime = System.currentTimeMillis() - restoreFromFile(uri) + restoreFromFile(uri, options) val time = System.currentTimeMillis() - startTime @@ -63,23 +63,46 @@ class BackupRestorer( ) } - private suspend fun restoreFromFile(uri: Uri) { + private suspend fun restoreFromFile(uri: Uri, options: RestoreOptions) { val backup = BackupUtil.decodeBackup(context, uri) - restoreAmount = backup.backupManga.size + 4 // +4 for categories, app prefs, source prefs, saved searches - // Store source mapping for error messages val backupMaps = backup.backupSources + backup.backupBrokenSources.map { it.toBackupSource() } sourceMapping = backupMaps.associate { it.sourceId to it.name } + if (options.library) { + restoreAmount += backup.backupManga.size + 1 // +1 for categories + } + // SY --> + if (options.savedSearches) { + restoreAmount += 1 + } + // SY <-- + if (options.appSettings) { + restoreAmount += 1 + } + if (options.sourceSettings) { + restoreAmount += 1 + } + coroutineScope { - restoreCategories(backup.backupCategories) + if (options.library) { + restoreCategories(backup.backupCategories) + } // SY --> - restoreSavedSearches(backup.backupSavedSearches) + if (options.savedSearches) { + restoreSavedSearches(backup.backupSavedSearches) + } // SY <-- - restoreAppPreferences(backup.backupPreferences) - restoreSourcePreferences(backup.backupSourcePreferences) - restoreManga(backup.backupManga, backup.backupCategories) + if (options.appSettings) { + restoreAppPreferences(backup.backupPreferences) + } + if (options.sourceSettings) { + restoreSourcePreferences(backup.backupSourcePreferences) + } + if (options.library) { + restoreManga(backup.backupManga, backup.backupCategories) + } // TODO: optionally trigger online library + tracker update } @@ -179,3 +202,32 @@ class BackupRestorer( return File("") } } + +data class RestoreOptions( + val appSettings: Boolean, + val sourceSettings: Boolean, + val library: Boolean, + // SY --> + val savedSearches: Boolean + // SY <-- +) { + fun toBooleanArray() = booleanArrayOf( + appSettings, + sourceSettings, + library, + // SY --> + savedSearches, + // SY <-- + ) + + companion object { + fun fromBooleanArray(booleanArray: BooleanArray) = RestoreOptions( + appSettings = booleanArray[0], + sourceSettings = booleanArray[1], + library = booleanArray[2], + // SY --> + savedSearches = booleanArray[3], + // SY <-- + ) + } +}