mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	Run periodic backups without launching services
This commit is contained in:
		@@ -4,17 +4,8 @@ import android.app.IntentService
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import com.github.salomonbrys.kotson.set
 | 
			
		||||
import com.google.gson.JsonArray
 | 
			
		||||
import com.google.gson.JsonObject
 | 
			
		||||
import com.hippo.unifile.UniFile
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup.CATEGORIES
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup.MANGAS
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup.VERSION
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.util.sendLocalBroadcast
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -26,8 +17,6 @@ class BackupCreateService : IntentService(NAME) {
 | 
			
		||||
        // Name of class
 | 
			
		||||
        private const val NAME = "BackupCreateService"
 | 
			
		||||
 | 
			
		||||
        // Backup called from job
 | 
			
		||||
        private const val EXTRA_IS_JOB = "$ID.$NAME.EXTRA_IS_JOB"
 | 
			
		||||
        // Options for backup
 | 
			
		||||
        private const val EXTRA_FLAGS = "$ID.$NAME.EXTRA_FLAGS"
 | 
			
		||||
 | 
			
		||||
@@ -48,12 +37,10 @@ class BackupCreateService : IntentService(NAME) {
 | 
			
		||||
         * @param context context of application
 | 
			
		||||
         * @param uri path of Uri
 | 
			
		||||
         * @param flags determines what to backup
 | 
			
		||||
         * @param isJob backup called from job
 | 
			
		||||
         */
 | 
			
		||||
        fun makeBackup(context: Context, uri: Uri, flags: Int, isJob: Boolean = false) {
 | 
			
		||||
        fun makeBackup(context: Context, uri: Uri, flags: Int) {
 | 
			
		||||
            val intent = Intent(context, BackupCreateService::class.java).apply {
 | 
			
		||||
                putExtra(BackupConst.EXTRA_URI, uri)
 | 
			
		||||
                putExtra(EXTRA_IS_JOB, isJob)
 | 
			
		||||
                putExtra(EXTRA_FLAGS, flags)
 | 
			
		||||
            }
 | 
			
		||||
            context.startService(intent)
 | 
			
		||||
@@ -68,95 +55,9 @@ class BackupCreateService : IntentService(NAME) {
 | 
			
		||||
 | 
			
		||||
        // Get values
 | 
			
		||||
        val uri = intent.getParcelableExtra<Uri>(BackupConst.EXTRA_URI)
 | 
			
		||||
        val isJob = intent.getBooleanExtra(EXTRA_IS_JOB, false)
 | 
			
		||||
        val flags = intent.getIntExtra(EXTRA_FLAGS, 0)
 | 
			
		||||
        // Create backup
 | 
			
		||||
        createBackupFromApp(uri, flags, isJob)
 | 
			
		||||
        backupManager.createBackup(uri, flags, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create backup Json file from database
 | 
			
		||||
     *
 | 
			
		||||
     * @param uri path of Uri
 | 
			
		||||
     * @param isJob backup called from job
 | 
			
		||||
     */
 | 
			
		||||
    private fun createBackupFromApp(uri: Uri, flags: Int, isJob: Boolean) {
 | 
			
		||||
        // Create root object
 | 
			
		||||
        val root = JsonObject()
 | 
			
		||||
 | 
			
		||||
        // Create manga array
 | 
			
		||||
        val mangaEntries = JsonArray()
 | 
			
		||||
 | 
			
		||||
        // Create category array
 | 
			
		||||
        val categoryEntries = JsonArray()
 | 
			
		||||
 | 
			
		||||
        // Add value's to root
 | 
			
		||||
        root[VERSION] = Backup.CURRENT_VERSION
 | 
			
		||||
        root[MANGAS] = mangaEntries
 | 
			
		||||
        root[CATEGORIES] = categoryEntries
 | 
			
		||||
 | 
			
		||||
        backupManager.databaseHelper.inTransaction {
 | 
			
		||||
            // Get manga from database
 | 
			
		||||
            val mangas = backupManager.getFavoriteManga()
 | 
			
		||||
 | 
			
		||||
            // Backup library manga and its dependencies
 | 
			
		||||
            mangas.forEach { manga ->
 | 
			
		||||
                mangaEntries.add(backupManager.backupMangaObject(manga, flags))
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Backup categories
 | 
			
		||||
            if ((flags and BACKUP_CATEGORY_MASK) == BACKUP_CATEGORY) {
 | 
			
		||||
                backupManager.backupCategories(categoryEntries)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // When BackupCreatorJob
 | 
			
		||||
            if (isJob) {
 | 
			
		||||
                // Get dir of file and create
 | 
			
		||||
                var dir = UniFile.fromUri(this, uri)
 | 
			
		||||
                dir = dir.createDirectory("automatic")
 | 
			
		||||
 | 
			
		||||
                // Delete older backups
 | 
			
		||||
                val numberOfBackups = backupManager.numberOfBackups()
 | 
			
		||||
                val backupRegex = Regex("""tachiyomi_\d+-\d+-\d+_\d+-\d+.json""")
 | 
			
		||||
                dir.listFiles { _, filename -> backupRegex.matches(filename) }
 | 
			
		||||
                        .orEmpty()
 | 
			
		||||
                        .sortedByDescending { it.name }
 | 
			
		||||
                        .drop(numberOfBackups - 1)
 | 
			
		||||
                        .forEach { it.delete() }
 | 
			
		||||
 | 
			
		||||
                // Create new file to place backup
 | 
			
		||||
                val newFile = dir.createFile(Backup.getDefaultFilename())
 | 
			
		||||
                        ?: throw Exception("Couldn't create backup file")
 | 
			
		||||
 | 
			
		||||
                newFile.openOutputStream().bufferedWriter().use {
 | 
			
		||||
                    backupManager.parser.toJson(root, it)
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                val file = UniFile.fromUri(this, uri)
 | 
			
		||||
                        ?: throw Exception("Couldn't create backup file")
 | 
			
		||||
                file.openOutputStream().bufferedWriter().use {
 | 
			
		||||
                    backupManager.parser.toJson(root, it)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Show completed dialog
 | 
			
		||||
                val intent = Intent(BackupConst.INTENT_FILTER).apply {
 | 
			
		||||
                    putExtra(BackupConst.ACTION, BackupConst.ACTION_BACKUP_COMPLETED_DIALOG)
 | 
			
		||||
                    putExtra(BackupConst.EXTRA_URI, file.uri.toString())
 | 
			
		||||
                }
 | 
			
		||||
                sendLocalBroadcast(intent)
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            Timber.e(e)
 | 
			
		||||
            if (!isJob) {
 | 
			
		||||
                // Show error dialog
 | 
			
		||||
                val intent = Intent(BackupConst.INTENT_FILTER).apply {
 | 
			
		||||
                    putExtra(BackupConst.ACTION, BackupConst.ACTION_ERROR_BACKUP_DIALOG)
 | 
			
		||||
                    putExtra(BackupConst.EXTRA_ERROR_MESSAGE, e.message)
 | 
			
		||||
                }
 | 
			
		||||
                sendLocalBroadcast(intent)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,10 @@ class BackupCreatorJob : Job() {
 | 
			
		||||
 | 
			
		||||
    override fun onRunJob(params: Params): Result {
 | 
			
		||||
        val preferences = Injekt.get<PreferencesHelper>()
 | 
			
		||||
        val backupManager = Injekt.get<BackupManager>()
 | 
			
		||||
        val uri = Uri.parse(preferences.backupsDirectory().getOrDefault())
 | 
			
		||||
        val flags = BackupCreateService.BACKUP_ALL
 | 
			
		||||
        BackupCreateService.makeBackup(context, uri, flags, true)
 | 
			
		||||
        backupManager.createBackup(uri, flags, true)
 | 
			
		||||
        return Result.SUCCESS
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -38,4 +39,4 @@ class BackupCreatorJob : Job() {
 | 
			
		||||
            JobManager.instance().cancelAllForTag(TAG)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,11 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.backup
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import com.github.salomonbrys.kotson.*
 | 
			
		||||
import com.google.gson.*
 | 
			
		||||
import com.hippo.unifile.UniFile
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_CATEGORY
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_CATEGORY_MASK
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_CHAPTER
 | 
			
		||||
@@ -11,6 +14,7 @@ import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_HIST
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_HISTORY_MASK
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_TRACK
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_TRACK_MASK
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup.CATEGORIES
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup.CHAPTERS
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup.CURRENT_VERSION
 | 
			
		||||
@@ -26,8 +30,10 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.TrackManager
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.source.SourceManager
 | 
			
		||||
import eu.kanade.tachiyomi.util.sendLocalBroadcast
 | 
			
		||||
import eu.kanade.tachiyomi.util.syncChaptersWithSource
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
 | 
			
		||||
@@ -85,6 +91,92 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
 | 
			
		||||
        else -> throw Exception("Json version unknown")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create backup Json file from database
 | 
			
		||||
     *
 | 
			
		||||
     * @param uri path of Uri
 | 
			
		||||
     * @param isJob backup called from job
 | 
			
		||||
     */
 | 
			
		||||
    fun createBackup(uri: Uri, flags: Int, isJob: Boolean) {
 | 
			
		||||
        // Create root object
 | 
			
		||||
        val root = JsonObject()
 | 
			
		||||
 | 
			
		||||
        // Create manga array
 | 
			
		||||
        val mangaEntries = JsonArray()
 | 
			
		||||
 | 
			
		||||
        // Create category array
 | 
			
		||||
        val categoryEntries = JsonArray()
 | 
			
		||||
 | 
			
		||||
        // Add value's to root
 | 
			
		||||
        root[Backup.VERSION] = Backup.CURRENT_VERSION
 | 
			
		||||
        root[Backup.MANGAS] = mangaEntries
 | 
			
		||||
        root[CATEGORIES] = categoryEntries
 | 
			
		||||
 | 
			
		||||
        databaseHelper.inTransaction {
 | 
			
		||||
            // Get manga from database
 | 
			
		||||
            val mangas = getFavoriteManga()
 | 
			
		||||
 | 
			
		||||
            // Backup library manga and its dependencies
 | 
			
		||||
            mangas.forEach { manga ->
 | 
			
		||||
                mangaEntries.add(backupMangaObject(manga, flags))
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Backup categories
 | 
			
		||||
            if ((flags and BACKUP_CATEGORY_MASK) == BACKUP_CATEGORY) {
 | 
			
		||||
                backupCategories(categoryEntries)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // When BackupCreatorJob
 | 
			
		||||
            if (isJob) {
 | 
			
		||||
                // Get dir of file and create
 | 
			
		||||
                var dir = UniFile.fromUri(context, uri)
 | 
			
		||||
                dir = dir.createDirectory("automatic")
 | 
			
		||||
 | 
			
		||||
                // Delete older backups
 | 
			
		||||
                val numberOfBackups = numberOfBackups()
 | 
			
		||||
                val backupRegex = Regex("""tachiyomi_\d+-\d+-\d+_\d+-\d+.json""")
 | 
			
		||||
                dir.listFiles { _, filename -> backupRegex.matches(filename) }
 | 
			
		||||
                        .orEmpty()
 | 
			
		||||
                        .sortedByDescending { it.name }
 | 
			
		||||
                        .drop(numberOfBackups - 1)
 | 
			
		||||
                        .forEach { it.delete() }
 | 
			
		||||
 | 
			
		||||
                // Create new file to place backup
 | 
			
		||||
                val newFile = dir.createFile(Backup.getDefaultFilename())
 | 
			
		||||
                        ?: throw Exception("Couldn't create backup file")
 | 
			
		||||
 | 
			
		||||
                newFile.openOutputStream().bufferedWriter().use {
 | 
			
		||||
                    parser.toJson(root, it)
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                val file = UniFile.fromUri(context, uri)
 | 
			
		||||
                        ?: throw Exception("Couldn't create backup file")
 | 
			
		||||
                file.openOutputStream().bufferedWriter().use {
 | 
			
		||||
                    parser.toJson(root, it)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Show completed dialog
 | 
			
		||||
                val intent = Intent(BackupConst.INTENT_FILTER).apply {
 | 
			
		||||
                    putExtra(BackupConst.ACTION, BackupConst.ACTION_BACKUP_COMPLETED_DIALOG)
 | 
			
		||||
                    putExtra(BackupConst.EXTRA_URI, file.uri.toString())
 | 
			
		||||
                }
 | 
			
		||||
                context.sendLocalBroadcast(intent)
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            Timber.e(e)
 | 
			
		||||
            if (!isJob) {
 | 
			
		||||
                // Show error dialog
 | 
			
		||||
                val intent = Intent(BackupConst.INTENT_FILTER).apply {
 | 
			
		||||
                    putExtra(BackupConst.ACTION, BackupConst.ACTION_ERROR_BACKUP_DIALOG)
 | 
			
		||||
                    putExtra(BackupConst.EXTRA_ERROR_MESSAGE, e.message)
 | 
			
		||||
                }
 | 
			
		||||
                context.sendLocalBroadcast(intent)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Backup the categories of library
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user