mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 06:17:57 +01:00 
			
		
		
		
	Refactor backup service
This commit is contained in:
		| @@ -4,10 +4,7 @@ import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID | ||||
|  | ||||
| object BackupConst { | ||||
|  | ||||
|     const val INTENT_FILTER = "SettingsBackupFragment" | ||||
|     const val ACTION_BACKUP_COMPLETED = "$ID.$INTENT_FILTER.ACTION_BACKUP_COMPLETED" | ||||
|     const val ACTION_BACKUP_ERROR = "$ID.$INTENT_FILTER.ACTION_BACKUP_ERROR" | ||||
|     const val ACTION = "$ID.$INTENT_FILTER.ACTION" | ||||
|     const val EXTRA_ERROR_MESSAGE = "$ID.$INTENT_FILTER.EXTRA_ERROR_MESSAGE" | ||||
|     const val EXTRA_URI = "$ID.$INTENT_FILTER.EXTRA_URI" | ||||
|     private const val NAME = "BackupRestoreServices" | ||||
|     const val EXTRA_URI = "$ID.$NAME.EXTRA_URI" | ||||
|     const val EXTRA_FLAGS = "$ID.$NAME.EXTRA_FLAGS" | ||||
| } | ||||
|   | ||||
| @@ -1,25 +1,22 @@ | ||||
| package eu.kanade.tachiyomi.data.backup | ||||
|  | ||||
| import android.app.IntentService | ||||
| import android.app.Service | ||||
| import android.content.Context | ||||
| import android.content.Intent | ||||
| import android.net.Uri | ||||
| import com.google.gson.JsonArray | ||||
| import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import android.os.Build | ||||
| import android.os.IBinder | ||||
| import android.os.PowerManager | ||||
| import com.hippo.unifile.UniFile | ||||
| import eu.kanade.tachiyomi.data.notification.Notifications | ||||
| import eu.kanade.tachiyomi.util.system.isServiceRunning | ||||
|  | ||||
| /** | ||||
|  * [IntentService] used to backup [Manga] information to [JsonArray] | ||||
|  * Service for backing up library information to a JSON file. | ||||
|  */ | ||||
| class BackupCreateService : IntentService(NAME) { | ||||
| class BackupCreateService : Service() { | ||||
|  | ||||
|     companion object { | ||||
|         // Name of class | ||||
|         private const val NAME = "BackupCreateService" | ||||
|  | ||||
|         // Options for backup | ||||
|         private const val EXTRA_FLAGS = "$ID.$NAME.EXTRA_FLAGS" | ||||
|  | ||||
|         // Filter options | ||||
|         internal const val BACKUP_CATEGORY = 0x1 | ||||
|         internal const val BACKUP_CATEGORY_MASK = 0x1 | ||||
| @@ -31,6 +28,15 @@ class BackupCreateService : IntentService(NAME) { | ||||
|         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 | ||||
|          * | ||||
| @@ -38,24 +44,78 @@ class BackupCreateService : IntentService(NAME) { | ||||
|          * @param uri path of Uri | ||||
|          * @param flags determines what to backup | ||||
|          */ | ||||
|         fun makeBackup(context: Context, uri: Uri, flags: Int) { | ||||
|             val intent = Intent(context, BackupCreateService::class.java).apply { | ||||
|                 putExtra(BackupConst.EXTRA_URI, uri) | ||||
|                 putExtra(EXTRA_FLAGS, flags) | ||||
|         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) | ||||
|                 } | ||||
|                 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { | ||||
|                     context.startService(intent) | ||||
|                 } else { | ||||
|                     context.startForegroundService(intent) | ||||
|                 } | ||||
|             } | ||||
|             context.startService(intent) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private val backupManager by lazy { BackupManager(this) } | ||||
|     /** | ||||
|      * Wake lock that will be held until the service is destroyed. | ||||
|      */ | ||||
|     private lateinit var wakeLock: PowerManager.WakeLock | ||||
|  | ||||
|     override fun onHandleIntent(intent: Intent?) { | ||||
|         if (intent == null) return | ||||
|     private lateinit var backupManager: BackupManager | ||||
|     private lateinit var notifier: BackupNotifier | ||||
|  | ||||
|         // Get values | ||||
|         val uri = intent.getParcelableExtra<Uri>(BackupConst.EXTRA_URI) | ||||
|         val flags = intent.getIntExtra(EXTRA_FLAGS, 0) | ||||
|         // Create backup | ||||
|         backupManager.createBackup(uri, flags, false) | ||||
|     override fun onCreate() { | ||||
|         super.onCreate() | ||||
|         notifier = BackupNotifier(this) | ||||
|  | ||||
|         startForeground(Notifications.ID_BACKUP_PROGRESS, notifier.showBackupProgress().build()) | ||||
|  | ||||
|         wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock( | ||||
|             PowerManager.PARTIAL_WAKE_LOCK, "${javaClass.name}:WakeLock" | ||||
|         ) | ||||
|         wakeLock.acquire() | ||||
|     } | ||||
|  | ||||
|     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) | ||||
|             backupManager = BackupManager(this) | ||||
|  | ||||
|             val backupFileUri = Uri.parse(backupManager.createBackup(uri, backupFlags, false)) | ||||
|             val unifile = UniFile.fromUri(this, backupFileUri) | ||||
|             notifier.showBackupComplete(unifile) | ||||
|         } catch (e: Exception) { | ||||
|             notifier.showBackupError(e.message) | ||||
|         } | ||||
|  | ||||
|         stopSelf(startId) | ||||
|         return START_NOT_STICKY | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -20,9 +20,10 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet | ||||
|         val backupManager = BackupManager(context) | ||||
|         val uri = Uri.parse(preferences.backupsDirectory().get()) | ||||
|         val flags = BackupCreateService.BACKUP_ALL | ||||
|         return if (backupManager.createBackup(uri, flags, true)) { | ||||
|         return try { | ||||
|             backupManager.createBackup(uri, flags, true) | ||||
|             Result.success() | ||||
|         } else { | ||||
|         } catch (e: Exception) { | ||||
|             Result.failure() | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| package eu.kanade.tachiyomi.data.backup | ||||
|  | ||||
| import android.content.Context | ||||
| import android.content.Intent | ||||
| import android.net.Uri | ||||
| import com.github.salomonbrys.kotson.fromJson | ||||
| import com.github.salomonbrys.kotson.registerTypeAdapter | ||||
| @@ -49,7 +48,6 @@ import eu.kanade.tachiyomi.data.track.TrackManager | ||||
| import eu.kanade.tachiyomi.source.Source | ||||
| import eu.kanade.tachiyomi.source.SourceManager | ||||
| import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource | ||||
| import eu.kanade.tachiyomi.util.system.sendLocalBroadcast | ||||
| import kotlin.math.max | ||||
| import rx.Observable | ||||
| import timber.log.Timber | ||||
| @@ -102,7 +100,7 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) { | ||||
|      * @param uri path of Uri | ||||
|      * @param isJob backup called from job | ||||
|      */ | ||||
|     fun createBackup(uri: Uri, flags: Int, isJob: Boolean): Boolean { | ||||
|     fun createBackup(uri: Uri, flags: Int, isJob: Boolean): String? { | ||||
|         // Create root object | ||||
|         val root = JsonObject() | ||||
|  | ||||
| @@ -155,6 +153,8 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) { | ||||
|                 newFile.openOutputStream().bufferedWriter().use { | ||||
|                     parser.toJson(root, it) | ||||
|                 } | ||||
|  | ||||
|                 return newFile.uri.toString() | ||||
|             } else { | ||||
|                 val file = UniFile.fromUri(context, uri) | ||||
|                     ?: throw Exception("Couldn't create backup file") | ||||
| @@ -162,25 +162,11 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) { | ||||
|                     parser.toJson(root, it) | ||||
|                 } | ||||
|  | ||||
|                 // Show completed dialog | ||||
|                 val intent = Intent(BackupConst.INTENT_FILTER).apply { | ||||
|                     putExtra(BackupConst.ACTION, BackupConst.ACTION_BACKUP_COMPLETED) | ||||
|                     putExtra(BackupConst.EXTRA_URI, file.uri.toString()) | ||||
|                 } | ||||
|                 context.sendLocalBroadcast(intent) | ||||
|                 return file.uri.toString() | ||||
|             } | ||||
|             return true | ||||
|         } catch (e: Exception) { | ||||
|             Timber.e(e) | ||||
|             if (!isJob) { | ||||
|                 // Show error dialog | ||||
|                 val intent = Intent(BackupConst.INTENT_FILTER).apply { | ||||
|                     putExtra(BackupConst.ACTION, BackupConst.ACTION_BACKUP_ERROR) | ||||
|                     putExtra(BackupConst.EXTRA_ERROR_MESSAGE, e.message) | ||||
|                 } | ||||
|                 context.sendLocalBroadcast(intent) | ||||
|             } | ||||
|             return false | ||||
|             throw e | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package eu.kanade.tachiyomi.ui.setting.backup | ||||
| package eu.kanade.tachiyomi.data.backup | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.graphics.BitmapFactory | ||||
| @@ -25,16 +25,17 @@ internal class BackupNotifier(private val context: Context) { | ||||
|         context.notificationManager.notify(id, build()) | ||||
|     } | ||||
| 
 | ||||
|     fun showBackupProgress() { | ||||
|         with(notificationBuilder) { | ||||
|             setContentTitle(context.getString(R.string.backup)) | ||||
|             setContentText(context.getString(R.string.creating_backup)) | ||||
|     fun showBackupProgress(): NotificationCompat.Builder { | ||||
|         val builder = with(notificationBuilder) { | ||||
|             setContentTitle(context.getString(R.string.creating_backup)) | ||||
| 
 | ||||
|             setProgress(0, 0, true) | ||||
|             setOngoing(true) | ||||
|         } | ||||
| 
 | ||||
|         notificationBuilder.show(Notifications.ID_BACKUP_PROGRESS) | ||||
|         builder.show(Notifications.ID_BACKUP_PROGRESS) | ||||
| 
 | ||||
|         return builder | ||||
|     } | ||||
| 
 | ||||
|     fun showBackupError(error: String?) { | ||||
| @@ -59,7 +60,7 @@ internal class BackupNotifier(private val context: Context) { | ||||
|             setContentTitle(context.getString(R.string.backup_created)) | ||||
| 
 | ||||
|             if (unifile.filePath != null) { | ||||
|                 setContentText(context.getString(R.string.file_saved, unifile.filePath)) | ||||
|                 setContentText(unifile.filePath) | ||||
|             } | ||||
| 
 | ||||
|             // Remove progress bar | ||||
| @@ -32,7 +32,6 @@ import eu.kanade.tachiyomi.data.database.models.TrackImpl | ||||
| import eu.kanade.tachiyomi.data.notification.Notifications | ||||
| import eu.kanade.tachiyomi.data.track.TrackManager | ||||
| import eu.kanade.tachiyomi.source.Source | ||||
| import eu.kanade.tachiyomi.ui.setting.backup.BackupNotifier | ||||
| import eu.kanade.tachiyomi.util.system.isServiceRunning | ||||
| import java.io.File | ||||
| import java.text.SimpleDateFormat | ||||
| @@ -47,7 +46,7 @@ import timber.log.Timber | ||||
| import uy.kohesive.injekt.injectLazy | ||||
|  | ||||
| /** | ||||
|  * Restores backup from json file | ||||
|  * Restores backup from a JSON file. | ||||
|  */ | ||||
| class BackupRestoreService : Service() { | ||||
|  | ||||
| @@ -116,16 +115,12 @@ class BackupRestoreService : Service() { | ||||
|     private val errors = mutableListOf<Pair<Date, String>>() | ||||
|  | ||||
|     private lateinit var backupManager: BackupManager | ||||
|     private lateinit var notifier: BackupNotifier | ||||
|  | ||||
|     private val db: DatabaseHelper by injectLazy() | ||||
|  | ||||
|     private val trackManager: TrackManager by injectLazy() | ||||
|  | ||||
|     private lateinit var notifier: BackupNotifier | ||||
|  | ||||
|     /** | ||||
|      * Method called when the service is created. It injects dependencies and acquire the wake lock. | ||||
|      */ | ||||
|     override fun onCreate() { | ||||
|         super.onCreate() | ||||
|         notifier = BackupNotifier(this) | ||||
| @@ -133,7 +128,7 @@ class BackupRestoreService : Service() { | ||||
|         startForeground(Notifications.ID_RESTORE_PROGRESS, notifier.showRestoreProgress().build()) | ||||
|  | ||||
|         wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock( | ||||
|             PowerManager.PARTIAL_WAKE_LOCK, "BackupRestoreService:WakeLock" | ||||
|             PowerManager.PARTIAL_WAKE_LOCK, "${javaClass.name}:WakeLock" | ||||
|         ) | ||||
|         wakeLock.acquire() | ||||
|     } | ||||
|   | ||||
| @@ -4,10 +4,7 @@ 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.net.Uri | ||||
| import android.os.Bundle | ||||
| import android.view.View | ||||
| @@ -16,7 +13,6 @@ import com.afollestad.materialdialogs.MaterialDialog | ||||
| import com.afollestad.materialdialogs.list.listItemsMultiChoice | ||||
| import com.hippo.unifile.UniFile | ||||
| 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 | ||||
| @@ -24,7 +20,6 @@ import eu.kanade.tachiyomi.data.backup.models.Backup | ||||
| import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys | ||||
| import eu.kanade.tachiyomi.ui.base.controller.DialogController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe | ||||
| import eu.kanade.tachiyomi.ui.setting.backup.BackupNotifier | ||||
| import eu.kanade.tachiyomi.util.preference.defaultValue | ||||
| import eu.kanade.tachiyomi.util.preference.entriesRes | ||||
| import eu.kanade.tachiyomi.util.preference.intListPreference | ||||
| @@ -35,9 +30,7 @@ import eu.kanade.tachiyomi.util.preference.preferenceCategory | ||||
| import eu.kanade.tachiyomi.util.preference.summaryRes | ||||
| import eu.kanade.tachiyomi.util.preference.titleRes | ||||
| import eu.kanade.tachiyomi.util.system.getFilePicker | ||||
| import eu.kanade.tachiyomi.util.system.registerLocalReceiver | ||||
| import eu.kanade.tachiyomi.util.system.toast | ||||
| import eu.kanade.tachiyomi.util.system.unregisterLocalReceiver | ||||
| import kotlinx.coroutines.flow.launchIn | ||||
| import kotlinx.coroutines.flow.onEach | ||||
|  | ||||
| @@ -48,24 +41,11 @@ class SettingsBackupController : SettingsController() { | ||||
|      */ | ||||
|     private var backupFlags = 0 | ||||
|  | ||||
|     private val notifier by lazy { BackupNotifier(preferences.context) } | ||||
|  | ||||
|     private val receiver = BackupBroadcastReceiver() | ||||
|  | ||||
|     init { | ||||
|         preferences.context.registerLocalReceiver(receiver, IntentFilter(BackupConst.INTENT_FILTER)) | ||||
|     } | ||||
|  | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|         requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 500) | ||||
|     } | ||||
|  | ||||
|     override fun onDestroy() { | ||||
|         super.onDestroy() | ||||
|         preferences.context.unregisterLocalReceiver(receiver) | ||||
|     } | ||||
|  | ||||
|     override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { | ||||
|         titleRes = R.string.backup | ||||
|  | ||||
| @@ -74,7 +54,7 @@ class SettingsBackupController : SettingsController() { | ||||
|             summaryRes = R.string.pref_create_backup_summ | ||||
|  | ||||
|             onClick { | ||||
|                 if (!isBackupStarted) { | ||||
|                 if (!BackupCreateService.isRunning(context)) { | ||||
|                     val ctrl = CreateBackupDialog() | ||||
|                     ctrl.targetController = this@SettingsBackupController | ||||
|                     ctrl.showDialog(router) | ||||
| @@ -193,11 +173,8 @@ class SettingsBackupController : SettingsController() { | ||||
|                 val file = UniFile.fromUri(activity, uri) | ||||
|  | ||||
|                 activity.toast(R.string.creating_backup) | ||||
|                 notifier.showBackupProgress() | ||||
|  | ||||
|                 BackupCreateService.makeBackup(activity, file.uri, backupFlags) | ||||
|  | ||||
|                 isBackupStarted = true | ||||
|                 BackupCreateService.start(activity, file.uri, backupFlags) | ||||
|             } | ||||
|             CODE_BACKUP_RESTORE -> if (data != null && resultCode == Activity.RESULT_OK) { | ||||
|                 val uri = data.data | ||||
| @@ -286,30 +263,9 @@ class SettingsBackupController : SettingsController() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     inner class BackupBroadcastReceiver : BroadcastReceiver() { | ||||
|         override fun onReceive(context: Context, intent: Intent) { | ||||
|             when (intent.getStringExtra(BackupConst.ACTION)) { | ||||
|                 BackupConst.ACTION_BACKUP_COMPLETED -> { | ||||
|                     isBackupStarted = false | ||||
|  | ||||
|                     val uri = Uri.parse(intent.getStringExtra(BackupConst.EXTRA_URI)) | ||||
|                     val unifile = UniFile.fromUri(activity, uri) | ||||
|                     notifier.showBackupComplete(unifile) | ||||
|                 } | ||||
|                 BackupConst.ACTION_BACKUP_ERROR -> { | ||||
|                     isBackupStarted = false | ||||
|  | ||||
|                     notifier.showBackupError(intent.getStringExtra(BackupConst.EXTRA_ERROR_MESSAGE)) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private companion object { | ||||
|         const val CODE_BACKUP_CREATE = 501 | ||||
|         const val CODE_BACKUP_RESTORE = 502 | ||||
|         const val CODE_BACKUP_DIR = 503 | ||||
|  | ||||
|         var isBackupStarted = false | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -321,7 +321,6 @@ | ||||
|     <string name="restore_duration">%02d min, %02d sec</string> | ||||
|     <string name="restore_completed_content">Done in %1$s with %2$s errors</string> | ||||
|     <string name="backup_restore_content">Restore uses sources to fetch data, carrier costs may apply.\n\nMake sure you have installed all necessary extensions and are logged in to sources and tracking services before restoring.</string> | ||||
|     <string name="file_saved">File saved at %1$s</string> | ||||
|     <string name="backup_in_progress">Backup is already in progress</string> | ||||
|     <string name="backup_choice">What do you want to backup?</string> | ||||
|     <string name="creating_backup">Creating backup</string> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user