mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +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 | ||||
|      * | ||||
|   | ||||
| @@ -3,11 +3,7 @@ package eu.kanade.tachiyomi.ui.setting | ||||
| import android.Manifest.permission.WRITE_EXTERNAL_STORAGE | ||||
| import android.app.Activity | ||||
| import android.app.Dialog | ||||
| import android.content.ActivityNotFoundException | ||||
| import android.content.BroadcastReceiver | ||||
| import android.content.Context | ||||
| import android.content.Intent | ||||
| import android.content.IntentFilter | ||||
| import android.content.* | ||||
| import android.net.Uri | ||||
| import android.os.Build | ||||
| import android.os.Bundle | ||||
| @@ -15,22 +11,17 @@ import android.support.v7.preference.PreferenceScreen | ||||
| import android.view.View | ||||
| import com.afollestad.materialdialogs.MaterialDialog | ||||
| import com.hippo.unifile.UniFile | ||||
| import com.nononsenseapps.filepicker.FilePickerActivity | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.backup.BackupConst | ||||
| import eu.kanade.tachiyomi.data.backup.BackupCreateService | ||||
| import eu.kanade.tachiyomi.data.backup.BackupCreatorJob | ||||
| import eu.kanade.tachiyomi.data.backup.BackupRestoreService | ||||
| import eu.kanade.tachiyomi.data.backup.models.Backup | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.data.preference.getOrDefault | ||||
| import eu.kanade.tachiyomi.ui.base.controller.DialogController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.popControllerWithTag | ||||
| import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe | ||||
| import eu.kanade.tachiyomi.util.* | ||||
| import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import java.io.File | ||||
| import java.util.concurrent.TimeUnit | ||||
| import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys | ||||
| @@ -464,4 +455,4 @@ class SettingsBackupController : SettingsController() { | ||||
|         const val TAG_RESTORING_BACKUP_DIALOG = "RestoringBackupDialog" | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user