mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	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:
		@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user