Refactor backup service
This commit is contained in:
parent
52474e39d9
commit
d19d787f6e
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user