Browse Source

Use existing worker for manual backup creation (#6718)

* Use existing worker for manual backup creation

This will show the "creating backup" notification when auto backup is
running. Complete or error notification will continue to be shown only on
manual job.

* Make sure disabling auto backup don't cancel running manual backup job
Ivan Iskandar 3 years ago
parent
commit
d53bb4c337

+ 0 - 4
app/src/main/AndroidManifest.xml

@@ -181,10 +181,6 @@
             android:name=".data.updater.AppUpdateService"
             android:name=".data.updater.AppUpdateService"
             android:exported="false" />
             android:exported="false" />
 
 
-        <service
-            android:name=".data.backup.BackupCreateService"
-            android:exported="false" />
-
         <service
         <service
             android:name=".data.backup.BackupRestoreService"
             android:name=".data.backup.BackupRestoreService"
             android:exported="false" />
             android:exported="false" />

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/backup/AbstractBackupManager.kt

@@ -21,7 +21,7 @@ abstract class AbstractBackupManager(protected val context: Context) {
     internal val trackManager: TrackManager by injectLazy()
     internal val trackManager: TrackManager by injectLazy()
     protected val preferences: PreferencesHelper by injectLazy()
     protected val preferences: PreferencesHelper by injectLazy()
 
 
-    abstract fun createBackup(uri: Uri, flags: Int, isJob: Boolean): String
+    abstract fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String
 
 
     /**
     /**
      * Returns manga
      * Returns manga

+ 11 - 0
app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupConst.kt

@@ -11,4 +11,15 @@ object BackupConst {
 
 
     const val BACKUP_TYPE_LEGACY = 0
     const val BACKUP_TYPE_LEGACY = 0
     const val BACKUP_TYPE_FULL = 1
     const val BACKUP_TYPE_FULL = 1
+
+    // Filter options
+    internal const val BACKUP_CATEGORY = 0x1
+    internal const val BACKUP_CATEGORY_MASK = 0x1
+    internal const val BACKUP_CHAPTER = 0x2
+    internal const val BACKUP_CHAPTER_MASK = 0x2
+    internal const val BACKUP_HISTORY = 0x4
+    internal const val BACKUP_HISTORY_MASK = 0x4
+    internal const val BACKUP_TRACK = 0x8
+    internal const val BACKUP_TRACK_MASK = 0x8
+    internal const val BACKUP_ALL = 0xF
 }
 }

+ 0 - 114
app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateService.kt

@@ -1,114 +0,0 @@
-package eu.kanade.tachiyomi.data.backup
-
-import android.app.Service
-import android.content.Context
-import android.content.Intent
-import android.net.Uri
-import android.os.IBinder
-import android.os.PowerManager
-import androidx.core.content.ContextCompat
-import androidx.core.net.toUri
-import com.hippo.unifile.UniFile
-import eu.kanade.tachiyomi.data.backup.full.FullBackupManager
-import eu.kanade.tachiyomi.data.notification.Notifications
-import eu.kanade.tachiyomi.util.system.acquireWakeLock
-import eu.kanade.tachiyomi.util.system.isServiceRunning
-
-/**
- * Service for backing up library information to a JSON file.
- */
-class BackupCreateService : Service() {
-
-    companion object {
-        // Filter options
-        internal const val BACKUP_CATEGORY = 0x1
-        internal const val BACKUP_CATEGORY_MASK = 0x1
-        internal const val BACKUP_CHAPTER = 0x2
-        internal const val BACKUP_CHAPTER_MASK = 0x2
-        internal const val BACKUP_HISTORY = 0x4
-        internal const val BACKUP_HISTORY_MASK = 0x4
-        internal const val BACKUP_TRACK = 0x8
-        internal const val BACKUP_TRACK_MASK = 0x8
-        internal const val BACKUP_ALL = 0xF
-
-        /**
-         * Returns the status of the service.
-         *
-         * @param context the application context.
-         * @return true if the service is running, false otherwise.
-         */
-        fun isRunning(context: Context): Boolean =
-            context.isServiceRunning(BackupCreateService::class.java)
-
-        /**
-         * Make a backup from library
-         *
-         * @param context context of application
-         * @param uri path of Uri
-         * @param flags determines what to backup
-         */
-        fun start(context: Context, uri: Uri, flags: Int) {
-            if (!isRunning(context)) {
-                val intent = Intent(context, BackupCreateService::class.java).apply {
-                    putExtra(BackupConst.EXTRA_URI, uri)
-                    putExtra(BackupConst.EXTRA_FLAGS, flags)
-                }
-                ContextCompat.startForegroundService(context, intent)
-            }
-        }
-    }
-
-    /**
-     * Wake lock that will be held until the service is destroyed.
-     */
-    private lateinit var wakeLock: PowerManager.WakeLock
-
-    private lateinit var notifier: BackupNotifier
-
-    override fun onCreate() {
-        super.onCreate()
-
-        notifier = BackupNotifier(this)
-        wakeLock = acquireWakeLock(javaClass.name)
-
-        startForeground(Notifications.ID_BACKUP_PROGRESS, notifier.showBackupProgress().build())
-    }
-
-    override fun stopService(name: Intent?): Boolean {
-        destroyJob()
-        return super.stopService(name)
-    }
-
-    override fun onDestroy() {
-        destroyJob()
-        super.onDestroy()
-    }
-
-    private fun destroyJob() {
-        if (wakeLock.isHeld) {
-            wakeLock.release()
-        }
-    }
-
-    /**
-     * This method needs to be implemented, but it's not used/needed.
-     */
-    override fun onBind(intent: Intent): IBinder? = null
-
-    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
-        if (intent == null) return START_NOT_STICKY
-
-        try {
-            val uri = intent.getParcelableExtra<Uri>(BackupConst.EXTRA_URI)!!
-            val backupFlags = intent.getIntExtra(BackupConst.EXTRA_FLAGS, 0)
-            val backupFileUri = FullBackupManager(this).createBackup(uri, backupFlags, false)?.toUri()
-            val unifile = UniFile.fromUri(this, backupFileUri)
-            notifier.showBackupComplete(unifile)
-        } catch (e: Exception) {
-            notifier.showBackupError(e.message)
-        }
-
-        stopSelf(startId)
-        return START_NOT_STICKY
-    }
-}

+ 46 - 5
app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt

@@ -1,15 +1,23 @@
 package eu.kanade.tachiyomi.data.backup
 package eu.kanade.tachiyomi.data.backup
 
 
 import android.content.Context
 import android.content.Context
+import android.net.Uri
 import androidx.core.net.toUri
 import androidx.core.net.toUri
 import androidx.work.ExistingPeriodicWorkPolicy
 import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.ExistingWorkPolicy
+import androidx.work.OneTimeWorkRequestBuilder
 import androidx.work.PeriodicWorkRequestBuilder
 import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkInfo
 import androidx.work.WorkManager
 import androidx.work.WorkManager
 import androidx.work.Worker
 import androidx.work.Worker
 import androidx.work.WorkerParameters
 import androidx.work.WorkerParameters
+import androidx.work.workDataOf
+import com.hippo.unifile.UniFile
 import eu.kanade.tachiyomi.data.backup.full.FullBackupManager
 import eu.kanade.tachiyomi.data.backup.full.FullBackupManager
+import eu.kanade.tachiyomi.data.notification.Notifications
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.util.system.logcat
 import eu.kanade.tachiyomi.util.system.logcat
+import eu.kanade.tachiyomi.util.system.notificationManager
 import logcat.LogPriority
 import logcat.LogPriority
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 import uy.kohesive.injekt.api.get
@@ -20,23 +28,42 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
 
 
     override fun doWork(): Result {
     override fun doWork(): Result {
         val preferences = Injekt.get<PreferencesHelper>()
         val preferences = Injekt.get<PreferencesHelper>()
-        val uri = preferences.backupsDirectory().get().toUri()
-        val flags = BackupCreateService.BACKUP_ALL
+        val notifier = BackupNotifier(context)
+        val uri = inputData.getString(LOCATION_URI_KEY)?.let { Uri.parse(it) }
+            ?: preferences.backupsDirectory().get().toUri()
+        val flags = inputData.getInt(BACKUP_FLAGS_KEY, BackupConst.BACKUP_ALL)
+        val isAutoBackup = inputData.getBoolean(IS_AUTO_BACKUP_KEY, false)
+
+        context.notificationManager.notify(Notifications.ID_BACKUP_PROGRESS, notifier.showBackupProgress().build())
         return try {
         return try {
-            FullBackupManager(context).createBackup(uri, flags, true)
+            val location = FullBackupManager(context).createBackup(uri, flags, isAutoBackup)
+            if (!isAutoBackup) notifier.showBackupComplete(UniFile.fromUri(context, location.toUri()))
             Result.success()
             Result.success()
         } catch (e: Exception) {
         } catch (e: Exception) {
             logcat(LogPriority.ERROR, e)
             logcat(LogPriority.ERROR, e)
+            if (!isAutoBackup) notifier.showBackupError(e.message)
             Result.failure()
             Result.failure()
+        } finally {
+            context.notificationManager.cancel(Notifications.ID_BACKUP_PROGRESS)
         }
         }
     }
     }
 
 
     companion object {
     companion object {
         private const val TAG = "BackupCreator"
         private const val TAG = "BackupCreator"
 
 
+        private const val IS_AUTO_BACKUP_KEY = "is_auto_backup" // Boolean
+        private const val LOCATION_URI_KEY = "location_uri" // String
+        private const val BACKUP_FLAGS_KEY = "backup_flags" // Int
+
+        fun isManualJobRunning(context: Context): Boolean {
+            val list = WorkManager.getInstance(context).getWorkInfosByTag(TAG).get()
+            return list.find { it.state == WorkInfo.State.RUNNING } != null
+        }
+
         fun setupTask(context: Context, prefInterval: Int? = null) {
         fun setupTask(context: Context, prefInterval: Int? = null) {
             val preferences = Injekt.get<PreferencesHelper>()
             val preferences = Injekt.get<PreferencesHelper>()
             val interval = prefInterval ?: preferences.backupInterval().get()
             val interval = prefInterval ?: preferences.backupInterval().get()
+            val workManager = WorkManager.getInstance(context)
             if (interval > 0) {
             if (interval > 0) {
                 val request = PeriodicWorkRequestBuilder<BackupCreatorJob>(
                 val request = PeriodicWorkRequestBuilder<BackupCreatorJob>(
                     interval.toLong(),
                     interval.toLong(),
@@ -45,12 +72,26 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
                     TimeUnit.MINUTES
                     TimeUnit.MINUTES
                 )
                 )
                     .addTag(TAG)
                     .addTag(TAG)
+                    .setInputData(workDataOf(IS_AUTO_BACKUP_KEY to true))
                     .build()
                     .build()
 
 
-                WorkManager.getInstance(context).enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
+                workManager.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
             } else {
             } else {
-                WorkManager.getInstance(context).cancelAllWorkByTag(TAG)
+                workManager.cancelUniqueWork(TAG)
             }
             }
         }
         }
+
+        fun startNow(context: Context, uri: Uri, flags: Int) {
+            val inputData = workDataOf(
+                IS_AUTO_BACKUP_KEY to false,
+                LOCATION_URI_KEY to uri.toString(),
+                BACKUP_FLAGS_KEY to flags
+            )
+            val request = OneTimeWorkRequestBuilder<BackupCreatorJob>()
+                .addTag(TAG)
+                .setInputData(inputData)
+                .build()
+            WorkManager.getInstance(context).enqueueUniqueWork("$TAG:manual", ExistingWorkPolicy.KEEP, request)
+        }
     }
     }
 }
 }

+ 11 - 11
app/src/main/java/eu/kanade/tachiyomi/data/backup/full/FullBackupManager.kt

@@ -4,14 +4,14 @@ import android.content.Context
 import android.net.Uri
 import android.net.Uri
 import com.hippo.unifile.UniFile
 import com.hippo.unifile.UniFile
 import eu.kanade.tachiyomi.data.backup.AbstractBackupManager
 import eu.kanade.tachiyomi.data.backup.AbstractBackupManager
-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
-import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_CHAPTER_MASK
-import eu.kanade.tachiyomi.data.backup.BackupCreateService.Companion.BACKUP_HISTORY
-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.BackupConst.BACKUP_CATEGORY
+import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CATEGORY_MASK
+import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER
+import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_CHAPTER_MASK
+import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY
+import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_HISTORY_MASK
+import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK
+import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK_MASK
 import eu.kanade.tachiyomi.data.backup.full.models.Backup
 import eu.kanade.tachiyomi.data.backup.full.models.Backup
 import eu.kanade.tachiyomi.data.backup.full.models.BackupCategory
 import eu.kanade.tachiyomi.data.backup.full.models.BackupCategory
 import eu.kanade.tachiyomi.data.backup.full.models.BackupChapter
 import eu.kanade.tachiyomi.data.backup.full.models.BackupChapter
@@ -43,9 +43,9 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
      * Create backup Json file from database
      * Create backup Json file from database
      *
      *
      * @param uri path of Uri
      * @param uri path of Uri
-     * @param isJob backup called from job
+     * @param isAutoBackup backup called from scheduled backup job
      */
      */
-    override fun createBackup(uri: Uri, flags: Int, isJob: Boolean): String {
+    override fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean): String {
         // Create root object
         // Create root object
         var backup: Backup? = null
         var backup: Backup? = null
 
 
@@ -63,7 +63,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
         var file: UniFile? = null
         var file: UniFile? = null
         try {
         try {
             file = (
             file = (
-                if (isJob) {
+                if (isAutoBackup) {
                     // Get dir of file and create
                     // Get dir of file and create
                     var dir = UniFile.fromUri(context, uri)
                     var dir = UniFile.fromUri(context, uri)
                     dir = dir.createDirectory("automatic")
                     dir = dir.createDirectory("automatic")

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/data/backup/legacy/LegacyBackupManager.kt

@@ -55,9 +55,9 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
      * Create backup Json file from database
      * Create backup Json file from database
      *
      *
      * @param uri path of Uri
      * @param uri path of Uri
-     * @param isJob backup called from job
+     * @param isAutoBackup backup called from scheduled backup job
      */
      */
-    override fun createBackup(uri: Uri, flags: Int, isJob: Boolean) =
+    override fun createBackup(uri: Uri, flags: Int, isAutoBackup: Boolean) =
         throw IllegalStateException("Legacy backup creation is not supported")
         throw IllegalStateException("Legacy backup creation is not supported")
 
 
     fun restoreMangaNoFetch(manga: Manga, dbManga: Manga) {
     fun restoreMangaNoFetch(manga: Manga, dbManga: Manga) {

+ 6 - 11
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt

@@ -20,7 +20,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.hippo.unifile.UniFile
 import com.hippo.unifile.UniFile
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.backup.BackupConst
 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.BackupCreatorJob
 import eu.kanade.tachiyomi.data.backup.BackupRestoreService
 import eu.kanade.tachiyomi.data.backup.BackupRestoreService
 import eu.kanade.tachiyomi.data.backup.ValidatorParseException
 import eu.kanade.tachiyomi.data.backup.ValidatorParseException
@@ -70,7 +69,7 @@ class SettingsBackupController : SettingsController() {
                     context.toast(R.string.restore_miui_warning, Toast.LENGTH_LONG)
                     context.toast(R.string.restore_miui_warning, Toast.LENGTH_LONG)
                 }
                 }
 
 
-                if (!BackupCreateService.isRunning(context)) {
+                if (!BackupCreatorJob.isManualJobRunning(context)) {
                     val ctrl = CreateBackupDialog()
                     val ctrl = CreateBackupDialog()
                     ctrl.targetController = this@SettingsBackupController
                     ctrl.targetController = this@SettingsBackupController
                     ctrl.showDialog(router)
                     ctrl.showDialog(router)
@@ -197,11 +196,7 @@ class SettingsBackupController : SettingsController() {
                         Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                         Intent.FLAG_GRANT_WRITE_URI_PERMISSION
 
 
                     activity.contentResolver.takePersistableUriPermission(uri, flags)
                     activity.contentResolver.takePersistableUriPermission(uri, flags)
-                    BackupCreateService.start(
-                        activity,
-                        uri,
-                        backupFlags,
-                    )
+                    BackupCreatorJob.startNow(activity, uri, backupFlags)
                 }
                 }
                 CODE_BACKUP_RESTORE -> {
                 CODE_BACKUP_RESTORE -> {
                     RestoreBackupDialog(uri).showDialog(router)
                     RestoreBackupDialog(uri).showDialog(router)
@@ -252,10 +247,10 @@ class SettingsBackupController : SettingsController() {
                     selected.forEachIndexed { i, checked ->
                     selected.forEachIndexed { i, checked ->
                         if (checked) {
                         if (checked) {
                             when (i) {
                             when (i) {
-                                1 -> flags = flags or BackupCreateService.BACKUP_CATEGORY
-                                2 -> flags = flags or BackupCreateService.BACKUP_CHAPTER
-                                3 -> flags = flags or BackupCreateService.BACKUP_TRACK
-                                4 -> flags = flags or BackupCreateService.BACKUP_HISTORY
+                                1 -> flags = flags or BackupConst.BACKUP_CATEGORY
+                                2 -> flags = flags or BackupConst.BACKUP_CHAPTER
+                                3 -> flags = flags or BackupConst.BACKUP_TRACK
+                                4 -> flags = flags or BackupConst.BACKUP_HISTORY
                             }
                             }
                         }
                         }
                     }
                     }