Refactor backup and restore to support cross device sync. (#9699)

* refactor: backup and restore to support cross device sync.

* chore: Updated string resources

* refactor: change function name.

* refactor: Use URI SyncHolder.kt not needed anymore.
This commit is contained in:
KaiserBh 2023-07-23 08:39:56 +10:00 committed by GitHub
parent 46e3b9e40d
commit 7b2764e8f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 37 additions and 21 deletions

View file

@ -81,7 +81,7 @@ class BackupManager(
backupMangas(databaseManga, flags),
backupCategories(flags),
emptyList(),
backupExtensionInfo(databaseManga),
prepExtensionInfoForSync(databaseManga),
)
var file: UniFile? = null
@ -135,7 +135,7 @@ class BackupManager(
}
}
private fun backupExtensionInfo(mangas: List<Manga>): List<BackupSource> {
fun prepExtensionInfoForSync(mangas: List<Manga>): List<BackupSource> {
return mangas
.asSequence()
.map(Manga::source)
@ -150,7 +150,7 @@ class BackupManager(
*
* @return list of [BackupCategory] to be backed up
*/
private suspend fun backupCategories(options: Int): List<BackupCategory> {
suspend fun backupCategories(options: Int): List<BackupCategory> {
// Check if user wants category information in backup
return if (options and BACKUP_CATEGORY_MASK == BACKUP_CATEGORY) {
getCategories.await()
@ -161,7 +161,7 @@ class BackupManager(
}
}
private suspend fun backupMangas(mangas: List<Manga>, flags: Int): List<BackupManga> {
suspend fun backupMangas(mangas: List<Manga>, flags: Int): List<BackupManga> {
return mangas.map {
backupManga(it, flags)
}
@ -514,7 +514,7 @@ class BackupManager(
}
}
private suspend fun updateManga(manga: Manga): Long {
suspend fun updateManga(manga: Manga): Long {
handler.await(true) {
mangasQueries.update(
source = manga.source,

View file

@ -79,9 +79,9 @@ class BackupNotifier(private val context: Context) {
}
}
fun showRestoreProgress(content: String = "", progress: Int = 0, maxAmount: Int = 100): NotificationCompat.Builder {
fun showRestoreProgress(content: String = "", contentTitle: String = context.getString(R.string.restoring_backup), progress: Int = 0, maxAmount: Int = 100): NotificationCompat.Builder {
val builder = with(progressNotificationBuilder) {
setContentTitle(context.getString(R.string.restoring_backup))
setContentTitle(contentTitle)
if (!preferences.hideNotificationContent().get()) {
setContentText(content)
@ -114,7 +114,7 @@ class BackupNotifier(private val context: Context) {
}
}
fun showRestoreComplete(time: Long, errorCount: Int, path: String?, file: String?) {
fun showRestoreComplete(time: Long, errorCount: Int, path: String?, file: String?, contentTitle: String = context.getString(R.string.restore_completed)) {
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
val timeString = context.getString(
@ -126,7 +126,7 @@ class BackupNotifier(private val context: Context) {
)
with(completeNotificationBuilder) {
setContentTitle(context.getString(R.string.restore_completed))
setContentTitle(contentTitle)
setContentText(context.resources.getQuantityString(R.plurals.restore_completed_message, errorCount, timeString, errorCount))
clearActions()

View file

@ -26,6 +26,7 @@ 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, false)
try {
setForeground(getForegroundInfo())
@ -35,7 +36,7 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
return try {
val restorer = BackupRestorer(context, notifier)
restorer.restoreBackup(uri)
restorer.syncFromBackup(uri, sync)
Result.success()
} catch (e: Exception) {
if (e is CancellationException) {
@ -63,9 +64,10 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
return context.workManager.isRunning(TAG)
}
fun start(context: Context, uri: Uri) {
fun start(context: Context, uri: Uri, sync: Boolean = false) {
val inputData = workDataOf(
LOCATION_URI_KEY to uri.toString(),
SYNC to sync,
)
val request = OneTimeWorkRequestBuilder<BackupRestoreJob>()
.addTag(TAG)
@ -83,3 +85,5 @@ class BackupRestoreJob(private val context: Context, workerParams: WorkerParamet
private const val TAG = "BackupRestore"
private const val LOCATION_URI_KEY = "location_uri" // String
private const val SYNC = "sync" // Boolean

View file

@ -36,12 +36,12 @@ class BackupRestorer(
private val errors = mutableListOf<Pair<Date, String>>()
suspend fun restoreBackup(uri: Uri): Boolean {
suspend fun syncFromBackup(uri: Uri, sync: Boolean): Boolean {
val startTime = System.currentTimeMillis()
restoreProgress = 0
errors.clear()
if (!performRestore(uri)) {
if (!performRestore(uri, sync)) {
return false
}
@ -50,7 +50,11 @@ class BackupRestorer(
val logFile = writeErrorLog()
notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name)
if (sync) {
notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name, contentTitle = context.getString(R.string.library_sync_complete))
} else {
notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name)
}
return true
}
@ -73,7 +77,7 @@ class BackupRestorer(
return File("")
}
private suspend fun performRestore(uri: Uri): Boolean {
private suspend fun performRestore(uri: Uri, sync: Boolean): Boolean {
val backup = BackupUtil.decodeBackup(context, uri)
restoreAmount = backup.backupManga.size + 1 // +1 for categories
@ -94,7 +98,7 @@ class BackupRestorer(
return@coroutineScope false
}
restoreManga(it, backup.backupCategories)
restoreManga(it, backup.backupCategories, sync)
}
// TODO: optionally trigger online library + tracker update
true
@ -105,10 +109,10 @@ class BackupRestorer(
backupManager.restoreCategories(backupCategories)
restoreProgress += 1
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories))
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories), context.getString(R.string.restoring_backup))
}
private suspend fun restoreManga(backupManga: BackupManga, backupCategories: List<BackupCategory>) {
private suspend fun restoreManga(backupManga: BackupManga, backupCategories: List<BackupCategory>, sync: Boolean) {
val manga = backupManga.getMangaImpl()
val chapters = backupManga.getChaptersImpl()
val categories = backupManga.categories.map { it.toInt() }
@ -134,7 +138,11 @@ class BackupRestorer(
}
restoreProgress += 1
showRestoreProgress(restoreProgress, restoreAmount, manga.title)
if (sync) {
showRestoreProgress(restoreProgress, restoreAmount, manga.title, context.getString(R.string.syncing_library))
} else {
showRestoreProgress(restoreProgress, restoreAmount, manga.title, context.getString(R.string.restoring_backup))
}
}
/**
@ -182,7 +190,7 @@ class BackupRestorer(
* @param amount total restoreAmount of manga
* @param title title of restored manga
*/
private fun showRestoreProgress(progress: Int, amount: Int, title: String) {
notifier.showRestoreProgress(title, progress, amount)
private fun showRestoreProgress(progress: Int, amount: Int, title: String, contentTitle: String) {
notifier.showRestoreProgress(title, contentTitle, progress, amount)
}
}

View file

@ -516,6 +516,10 @@
<string name="restoring_backup_canceled">Canceled restore</string>
<string name="backup_info">You should keep copies of backups in other places as well.</string>
<!-- Sync section -->
<string name="syncing_library">Syncing library</string>
<string name="library_sync_complete">Library sync complete</string>
<!-- Advanced section -->
<string name="label_network">Network</string>
<string name="pref_clear_cookies">Clear cookies</string>