Split restoring logic into smaller classes

This commit is contained in:
arkon 2023-12-16 11:43:18 -05:00
parent 5fec881387
commit cd16522805
14 changed files with 318 additions and 260 deletions

View File

@ -37,9 +37,9 @@ import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
import eu.kanade.presentation.util.relativeTimeSpanString
import eu.kanade.tachiyomi.data.backup.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
import eu.kanade.tachiyomi.data.backup.BackupRestoreJob
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.DeviceUtil

View File

@ -29,8 +29,8 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags
import eu.kanade.tachiyomi.data.backup.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.models.Backup
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.toast

View File

@ -7,7 +7,7 @@ import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.tachiyomi.core.security.SecurityPreferences
import eu.kanade.tachiyomi.data.backup.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.network.NetworkPreferences

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.data.backup
package eu.kanade.tachiyomi.data.backup.create
internal object BackupCreateFlags {
const val BACKUP_CATEGORY = 0x1

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.data.backup
package eu.kanade.tachiyomi.data.backup.create
import android.content.Context
import android.net.Uri
@ -14,6 +14,8 @@ import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.backup.BackupNotifier
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.isRunning

View File

@ -1,14 +1,15 @@
package eu.kanade.tachiyomi.data.backup
package eu.kanade.tachiyomi.data.backup.create
import android.content.Context
import android.net.Uri
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_APP_PREFS
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_CATEGORY
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_CHAPTER
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_HISTORY
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_SOURCE_PREFS
import eu.kanade.tachiyomi.data.backup.BackupCreateFlags.BACKUP_TRACK
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_APP_PREFS
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_CATEGORY
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_CHAPTER
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_HISTORY
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_SOURCE_PREFS
import eu.kanade.tachiyomi.data.backup.create.BackupCreateFlags.BACKUP_TRACK
import eu.kanade.tachiyomi.data.backup.models.Backup
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupChapter

View File

@ -8,7 +8,9 @@ import kotlinx.serialization.protobuf.ProtoNumber
data class BrokenBackupSource(
@ProtoNumber(0) var name: String = "",
@ProtoNumber(1) var sourceId: Long,
)
) {
fun toBackupSource() = BackupSource(name, sourceId)
}
@Serializable
data class BackupSource(

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.data.backup
package eu.kanade.tachiyomi.data.backup.restore
import android.content.Context
import android.net.Uri
@ -9,6 +9,7 @@ import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import eu.kanade.tachiyomi.data.backup.BackupNotifier
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.isRunning
@ -28,13 +29,12 @@ 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 sync = inputData.getBoolean(SYNC_KEY, false)
val isSync = inputData.getBoolean(SYNC_KEY, false)
setForegroundSafely()
return try {
val restorer = BackupRestorer(context, notifier)
restorer.syncFromBackup(uri, sync)
BackupRestorer(context, notifier, isSync).restore(uri)
Result.success()
} catch (e: Exception) {
if (e is CancellationException) {

View File

@ -0,0 +1,156 @@
package eu.kanade.tachiyomi.data.backup.restore
import android.content.Context
import android.net.Uri
import eu.kanade.tachiyomi.data.backup.BackupNotifier
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
import eu.kanade.tachiyomi.util.BackupUtil
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch
import tachiyomi.core.i18n.stringResource
import tachiyomi.i18n.MR
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class BackupRestorer(
private val context: Context,
private val notifier: BackupNotifier,
private val isSync: Boolean,
private val categoriesRestorer: CategoriesRestorer = CategoriesRestorer(),
private val preferenceRestorer: PreferenceRestorer = PreferenceRestorer(context),
private val mangaRestorer: MangaRestorer = MangaRestorer(),
) {
private var restoreAmount = 0
private var restoreProgress = 0
private val errors = mutableListOf<Pair<Date, String>>()
/**
* Mapping of source ID to source name from backup data
*/
private var sourceMapping: Map<Long, String> = emptyMap()
suspend fun restore(uri: Uri) {
val startTime = System.currentTimeMillis()
restoreFromFile(uri)
val time = System.currentTimeMillis() - startTime
val logFile = writeErrorLog()
notifier.showRestoreComplete(
time,
errors.size,
logFile.parent,
logFile.name,
isSync,
)
}
private suspend fun restoreFromFile(uri: Uri) {
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 }
coroutineScope {
restoreCategories(backup.backupCategories)
restoreAppPreferences(backup.backupPreferences)
restoreSourcePreferences(backup.backupSourcePreferences)
restoreManga(backup.backupManga, backup.backupCategories)
// TODO: optionally trigger online library + tracker update
}
}
private fun CoroutineScope.restoreCategories(backupCategories: List<BackupCategory>) = launch {
ensureActive()
categoriesRestorer.restoreCategories(backupCategories)
restoreProgress += 1
notifier.showRestoreProgress(
context.stringResource(MR.strings.categories),
restoreProgress,
restoreAmount,
isSync,
)
}
private fun CoroutineScope.restoreManga(
backupMangas: List<BackupManga>,
backupCategories: List<BackupCategory>,
) = launch {
mangaRestorer.sortByNew(backupMangas)
.forEach {
ensureActive()
try {
mangaRestorer.restoreManga(it, backupCategories)
} catch (e: Exception) {
val sourceName = sourceMapping[it.source] ?: it.source.toString()
errors.add(Date() to "${it.title} [$sourceName]: ${e.message}")
}
restoreProgress += 1
notifier.showRestoreProgress(it.title, restoreProgress, restoreAmount, isSync)
}
}
private fun CoroutineScope.restoreAppPreferences(preferences: List<BackupPreference>) = launch {
ensureActive()
preferenceRestorer.restoreAppPreferences(preferences)
restoreProgress += 1
notifier.showRestoreProgress(
context.stringResource(MR.strings.app_settings),
restoreProgress,
restoreAmount,
isSync,
)
}
private fun CoroutineScope.restoreSourcePreferences(preferences: List<BackupSourcePreferences>) = launch {
ensureActive()
preferenceRestorer.restoreSourcePreferences(preferences)
restoreProgress += 1
notifier.showRestoreProgress(
context.stringResource(MR.strings.source_settings),
restoreProgress,
restoreAmount,
isSync,
)
}
private fun writeErrorLog(): File {
try {
if (errors.isNotEmpty()) {
val file = context.createFileInCacheDir("tachiyomi_restore.txt")
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
file.bufferedWriter().use { out ->
errors.forEach { (date, message) ->
out.write("[${sdf.format(date)}] $message\n")
}
}
return file
}
} catch (e: Exception) {
// Empty
}
return File("")
}
}

View File

@ -0,0 +1,36 @@
package eu.kanade.tachiyomi.data.backup.restore
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import tachiyomi.data.DatabaseHandler
import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.library.service.LibraryPreferences
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class CategoriesRestorer(
private val handler: DatabaseHandler = Injekt.get(),
private val getCategories: GetCategories = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(),
) {
suspend fun restoreCategories(backupCategories: List<BackupCategory>) {
if (backupCategories.isNotEmpty()) {
val dbCategories = getCategories.await()
val dbCategoriesByName = dbCategories.associateBy { it.name }
val categories = backupCategories.map {
dbCategoriesByName[it.name]
?: handler.awaitOneExecutable {
categoriesQueries.insert(it.name, it.order, it.flags)
categoriesQueries.selectLastInsertedRowId()
}.let { id -> it.toCategory(id) }
}
libraryPreferences.categorizedDisplaySettings().set(
(dbCategories + categories)
.distinctBy { it.flags }
.size > 1,
)
}
}
}

View File

@ -1,57 +1,29 @@
package eu.kanade.tachiyomi.data.backup
package eu.kanade.tachiyomi.data.backup.restore
import android.content.Context
import android.net.Uri
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
import eu.kanade.tachiyomi.data.backup.models.BackupChapter
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
import eu.kanade.tachiyomi.data.backup.models.BackupManga
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
import eu.kanade.tachiyomi.data.backup.models.BackupSource
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
import eu.kanade.tachiyomi.data.backup.models.BackupTracking
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.library.LibraryUpdateJob
import eu.kanade.tachiyomi.source.sourcePreferences
import eu.kanade.tachiyomi.util.BackupUtil
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.ensureActive
import tachiyomi.core.i18n.stringResource
import tachiyomi.core.preference.AndroidPreferenceStore
import tachiyomi.core.preference.PreferenceStore
import tachiyomi.data.DatabaseHandler
import tachiyomi.data.UpdateStrategyColumnAdapter
import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.interactor.FetchInterval
import tachiyomi.domain.manga.interactor.GetMangaByUrlAndSourceId
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.track.interactor.GetTracks
import tachiyomi.domain.track.interactor.InsertTrack
import tachiyomi.domain.track.model.Track
import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
import java.text.SimpleDateFormat
import java.time.ZonedDateTime
import java.util.Date
import java.util.Locale
import kotlin.math.max
class BackupRestorer(
private val context: Context,
private val notifier: BackupNotifier,
class MangaRestorer(
private val handler: DatabaseHandler = Injekt.get(),
private val getCategories: GetCategories = Injekt.get(),
private val getMangaByUrlAndSourceId: GetMangaByUrlAndSourceId = Injekt.get(),
@ -59,167 +31,48 @@ class BackupRestorer(
private val updateManga: UpdateManga = Injekt.get(),
private val getTracks: GetTracks = Injekt.get(),
private val insertTrack: InsertTrack = Injekt.get(),
private val fetchInterval: FetchInterval = Injekt.get(),
private val preferenceStore: PreferenceStore = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(),
fetchInterval: FetchInterval = Injekt.get(),
) {
private var restoreAmount = 0
private var restoreProgress = 0
private var now = ZonedDateTime.now()
private var currentFetchWindow = fetchInterval.getWindow(now)
/**
* Mapping of source ID to source name from backup data
*/
private var sourceMapping: Map<Long, String> = emptyMap()
private val errors = mutableListOf<Pair<Date, String>>()
suspend fun syncFromBackup(uri: Uri, sync: Boolean) {
val startTime = System.currentTimeMillis()
prepareState()
restoreFromFile(uri, sync)
val endTime = System.currentTimeMillis()
val time = endTime - startTime
val logFile = writeErrorLog()
notifier.showRestoreComplete(
time,
errors.size,
logFile.parent,
logFile.name,
sync,
)
}
private fun writeErrorLog(): File {
try {
if (errors.isNotEmpty()) {
val file = context.createFileInCacheDir("tachiyomi_restore.txt")
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
file.bufferedWriter().use { out ->
errors.forEach { (date, message) ->
out.write("[${sdf.format(date)}] $message\n")
}
}
return file
}
} catch (e: Exception) {
// Empty
}
return File("")
}
private fun prepareState() {
init {
now = ZonedDateTime.now()
currentFetchWindow = fetchInterval.getWindow(now)
}
private suspend fun restoreFromFile(uri: Uri, sync: Boolean) {
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.backupBrokenSources.map { BackupSource(it.name, it.sourceId) } + backup.backupSources
sourceMapping = backupMaps.associate { it.sourceId to it.name }
coroutineScope {
ensureActive()
restoreCategories(backup.backupCategories)
ensureActive()
restoreAppPreferences(backup.backupPreferences)
ensureActive()
restoreSourcePreferences(backup.backupSourcePreferences)
backup.backupManga.sortByNew()
.forEach {
ensureActive()
restoreManga(it, backup.backupCategories, sync)
}
// TODO: optionally trigger online library + tracker update
}
}
private suspend fun List<BackupManga>.sortByNew(): List<BackupManga> {
suspend fun sortByNew(backupMangas: List<BackupManga>): List<BackupManga> {
val urlsBySource = handler.awaitList { mangasQueries.getAllMangaSourceAndUrl() }
.groupBy({ it.source }, { it.url })
return this
return backupMangas
.sortedWith(
compareBy<BackupManga> { it.url in urlsBySource[it.source].orEmpty() }
.then(compareByDescending { it.lastModifiedAt }),
)
}
private suspend fun restoreCategories(backupCategories: List<BackupCategory>) {
if (backupCategories.isNotEmpty()) {
val dbCategories = getCategories.await()
val dbCategoriesByName = dbCategories.associateBy { it.name }
val categories = backupCategories.map {
dbCategoriesByName[it.name]
?: handler.awaitOneExecutable {
categoriesQueries.insert(it.name, it.order, it.flags)
categoriesQueries.selectLastInsertedRowId()
}.let { id -> it.toCategory(id) }
}
libraryPreferences.categorizedDisplaySettings().set(
(dbCategories + categories)
.distinctBy { it.flags }
.size > 1,
)
}
restoreProgress += 1
notifier.showRestoreProgress(
context.stringResource(MR.strings.categories),
restoreProgress,
restoreAmount,
false,
)
}
private suspend fun restoreManga(
suspend fun restoreManga(
backupManga: BackupManga,
backupCategories: List<BackupCategory>,
sync: Boolean,
) {
try {
val dbManga = findExistingManga(backupManga)
val manga = backupManga.getMangaImpl()
val restoredManga = if (dbManga == null) {
restoreNewManga(manga)
} else {
restoreExistingManga(manga, dbManga)
}
restoreMangaDetails(
manga = restoredManga,
chapters = backupManga.chapters,
categories = backupManga.categories,
backupCategories = backupCategories,
history = backupManga.history + backupManga.brokenHistory.map { it.toBackupHistory() },
tracks = backupManga.tracking,
)
} catch (e: Exception) {
val sourceName = sourceMapping[backupManga.source] ?: backupManga.source.toString()
errors.add(Date() to "${backupManga.title} [$sourceName]: ${e.message}")
val dbManga = findExistingManga(backupManga)
val manga = backupManga.getMangaImpl()
val restoredManga = if (dbManga == null) {
restoreNewManga(manga)
} else {
restoreExistingManga(manga, dbManga)
}
restoreProgress += 1
notifier.showRestoreProgress(backupManga.title, restoreProgress, restoreAmount, sync)
restoreMangaDetails(
manga = restoredManga,
chapters = backupManga.chapters,
categories = backupManga.categories,
backupCategories = backupCategories,
history = backupManga.history + backupManga.brokenHistory.map { it.toBackupHistory() },
tracks = backupManga.tracking,
)
}
private suspend fun findExistingManga(backupManga: BackupManga): Manga? {
@ -546,75 +399,4 @@ class BackupRestorer(
}
private fun Track.forComparison() = this.copy(id = 0L, mangaId = 0L)
private fun restoreAppPreferences(preferences: List<BackupPreference>) {
restorePreferences(preferences, preferenceStore)
LibraryUpdateJob.setupTask(context)
BackupCreateJob.setupTask(context)
restoreProgress += 1
notifier.showRestoreProgress(
context.stringResource(MR.strings.app_settings),
restoreProgress,
restoreAmount,
false,
)
}
private fun restoreSourcePreferences(preferences: List<BackupSourcePreferences>) {
preferences.forEach {
val sourcePrefs = AndroidPreferenceStore(context, sourcePreferences(it.sourceKey))
restorePreferences(it.prefs, sourcePrefs)
}
restoreProgress += 1
notifier.showRestoreProgress(
context.stringResource(MR.strings.source_settings),
restoreProgress,
restoreAmount,
false,
)
}
private fun restorePreferences(
toRestore: List<BackupPreference>,
preferenceStore: PreferenceStore,
) {
val prefs = preferenceStore.getAll()
toRestore.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)
}
}
}
}
}
}

View File

@ -0,0 +1,79 @@
package eu.kanade.tachiyomi.data.backup.restore
import android.content.Context
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
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.library.LibraryUpdateJob
import eu.kanade.tachiyomi.source.sourcePreferences
import tachiyomi.core.preference.AndroidPreferenceStore
import tachiyomi.core.preference.PreferenceStore
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class PreferenceRestorer(
private val context: Context,
private val preferenceStore: PreferenceStore = Injekt.get(),
) {
fun restoreAppPreferences(preferences: List<BackupPreference>) {
restorePreferences(preferences, preferenceStore)
LibraryUpdateJob.setupTask(context)
BackupCreateJob.setupTask(context)
}
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) {
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)
}
}
}
}
}
}

View File

@ -7,7 +7,7 @@ import android.content.Intent
import android.net.Uri
import android.os.Build
import androidx.core.net.toUri
import eu.kanade.tachiyomi.data.backup.BackupRestoreJob
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.updater.AppUpdateDownloadJob

View File

@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.util
import android.content.Context
import android.net.Uri
import eu.kanade.tachiyomi.data.backup.BackupCreator
import eu.kanade.tachiyomi.data.backup.create.BackupCreator
import eu.kanade.tachiyomi.data.backup.models.Backup
import eu.kanade.tachiyomi.data.backup.models.BackupSerializer
import okio.buffer