parent
a51108cbe8
commit
83a67feb48
@ -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.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
@ -249,7 +250,16 @@ 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,
|
||||
),
|
||||
)
|
||||
onDismissRequest()
|
||||
},
|
||||
) {
|
||||
@ -283,7 +293,16 @@ 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,
|
||||
),
|
||||
)
|
||||
return@rememberLauncherForActivityResult
|
||||
}
|
||||
|
||||
|
@ -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<BackupRestoreJob>()
|
||||
.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
|
||||
|
@ -39,10 +39,10 @@ class BackupRestorer(
|
||||
*/
|
||||
private var sourceMapping: Map<Long, String> = 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
|
||||
|
||||
@ -57,20 +57,36 @@ 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 + 3 // +3 for categories, app prefs, source prefs
|
||||
|
||||
// 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
|
||||
}
|
||||
if (options.appSettings) {
|
||||
restoreAmount += 1
|
||||
}
|
||||
if (options.sourceSettings) {
|
||||
restoreAmount += 1
|
||||
}
|
||||
|
||||
coroutineScope {
|
||||
restoreCategories(backup.backupCategories)
|
||||
restoreAppPreferences(backup.backupPreferences)
|
||||
restoreSourcePreferences(backup.backupSourcePreferences)
|
||||
restoreManga(backup.backupManga, backup.backupCategories)
|
||||
if (options.library) {
|
||||
restoreCategories(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
|
||||
}
|
||||
@ -154,3 +170,19 @@ class BackupRestorer(
|
||||
return File("")
|
||||
}
|
||||
}
|
||||
|
||||
data class RestoreOptions(
|
||||
val appSettings: Boolean,
|
||||
val sourceSettings: Boolean,
|
||||
val library: Boolean,
|
||||
) {
|
||||
fun toBooleanArray() = booleanArrayOf(appSettings, sourceSettings, library)
|
||||
|
||||
companion object {
|
||||
fun fromBooleanArray(booleanArray: BooleanArray) = RestoreOptions(
|
||||
appSettings = booleanArray[0],
|
||||
sourceSettings = booleanArray[1],
|
||||
library = booleanArray[2],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user