Migrate to WorkManager

This commit is contained in:
arkon 2020-04-21 13:48:35 -04:00 committed by Jay
parent beeabe17c4
commit 329e8f1988
10 changed files with 188 additions and 160 deletions

View File

@ -151,7 +151,9 @@ dependencies {
implementation("org.jsoup:jsoup:1.13.1")
// Job scheduling
implementation("com.evernote:android-job:1.4.2")
val workManagerVersion = "2.3.3"
implementation("android.arch.work:work-runtime:$workManagerVersion")
implementation("android.arch.work:work-runtime-ktx:$workManagerVersion")
implementation("com.google.android.gms:play-services-gcm:17.0.0")
// Changelog

View File

@ -8,14 +8,9 @@ import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.multidex.MultiDex
import com.evernote.android.job.JobManager
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.data.updater.UpdaterJob
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.util.system.LocaleHelper
import org.acra.ACRA
@ -43,7 +38,6 @@ open class App : Application(), LifecycleObserver {
Injekt.importModule(AppModule(this))
setupAcra()
setupJobManager()
setupNotificationChannels()
LocaleHelper.updateConfiguration(this, resources.configuration)
@ -73,22 +67,6 @@ open class App : Application(), LifecycleObserver {
ACRA.init(this)
}
protected open fun setupJobManager() {
try {
JobManager.create(this).addJobCreator { tag ->
when (tag) {
LibraryUpdateJob.TAG -> LibraryUpdateJob()
UpdaterJob.TAG -> UpdaterJob()
BackupCreatorJob.TAG -> BackupCreatorJob()
ExtensionUpdateJob.TAG -> ExtensionUpdateJob()
else -> null
}
}
} catch (e: Exception) {
Timber.w("Can't initialize job manager")
}
}
protected open fun setupNotificationChannels() {
Notifications.createChannels(this)
}

View File

@ -1,10 +1,12 @@
package eu.kanade.tachiyomi
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
import eu.kanade.tachiyomi.data.download.DownloadProvider
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.data.updater.UpdaterJob
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.ui.library.LibraryPresenter
import java.io.File
@ -64,8 +66,16 @@ object Migrations {
}
if (oldVersion < 54)
DownloadProvider(context).renameChaapters()
if (oldVersion < 62)
if (oldVersion < 62) {
LibraryPresenter.updateDB()
// Restore jobs after migrating from Evernote's job scheduler to WorkManager.
if (BuildConfig.INCLUDE_UPDATER && preferences.automaticUpdates()) {
UpdaterJob.setupTask()
}
LibraryUpdateJob.setupTask()
BackupCreatorJob.setupTask()
ExtensionUpdateJob.setupTask()
}
return true
}
return false

View File

@ -1,44 +1,47 @@
package eu.kanade.tachiyomi.data.backup
import android.content.Context
import android.net.Uri
import com.evernote.android.job.Job
import com.evernote.android.job.JobManager
import com.evernote.android.job.JobRequest
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
class BackupCreatorJob : Job() {
class BackupCreatorJob(private val context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {
override fun onRunJob(params: Params): Result {
override fun doWork(): Result {
val preferences = Injekt.get<PreferencesHelper>()
val backupManager = BackupManager(context)
val uri = Uri.parse(preferences.backupsDirectory().getOrDefault())
val flags = BackupCreateService.BACKUP_ALL
backupManager.createBackup(uri, flags, true)
return Result.SUCCESS
return Result.success()
}
companion object {
const val TAG = "BackupCreator"
private const val TAG = "BackupCreator"
fun setupTask(prefInterval: Int? = null) {
val preferences = Injekt.get<PreferencesHelper>()
val interval = (prefInterval ?: preferences.backupInterval().getOrDefault()).toLong()
val interval = prefInterval ?: preferences.backupInterval().getOrDefault()
if (interval > 0) {
JobRequest.Builder(TAG)
.setPeriodic(TimeUnit.HOURS.toMillis(interval), TimeUnit.MINUTES.toMillis
(10))
.setUpdateCurrent(true)
val request = PeriodicWorkRequestBuilder<BackupCreatorJob>(
interval.toLong(), TimeUnit.HOURS,
10, TimeUnit.MINUTES)
.addTag(TAG)
.build()
.schedule()
}
}
fun cancelTask() {
JobManager.instance().cancelAllForTag(TAG)
WorkManager.getInstance().enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
} else {
WorkManager.getInstance().cancelAllWorkByTag(TAG)
}
}
}
}

View File

@ -1,51 +1,57 @@
package eu.kanade.tachiyomi.data.library
import com.evernote.android.job.Job
import com.evernote.android.job.JobManager
import com.evernote.android.job.JobRequest
import android.content.Context
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
class LibraryUpdateJob : Job() {
class LibraryUpdateJob(private val context: Context, workerParams: WorkerParameters) :
Worker(context, workerParams) {
override fun onRunJob(params: Params): Result {
override fun doWork(): Result {
LibraryUpdateService.start(context)
return Job.Result.SUCCESS
return Result.success()
}
companion object {
const val TAG = "LibraryUpdate"
private const val TAG = "LibraryUpdate"
fun setupTask(prefInterval: Int? = null) {
val preferences = Injekt.get<PreferencesHelper>()
val interval = (prefInterval ?: preferences.libraryUpdateInterval().getOrDefault())
.toLong()
val interval = prefInterval ?: preferences.libraryUpdateInterval().getOrDefault()
if (interval > 0) {
val restrictions = preferences.libraryUpdateRestriction()!!
val acRestriction = "ac" in restrictions
val wifiRestriction = if ("wifi" in restrictions)
JobRequest.NetworkType.UNMETERED
NetworkType.UNMETERED
else
JobRequest.NetworkType.CONNECTED
NetworkType.CONNECTED
JobRequest.Builder(TAG)
.setPeriodic(
TimeUnit.HOURS.toMillis(interval), TimeUnit.MINUTES.toMillis
(10))
val constraints = Constraints.Builder()
.setRequiredNetworkType(wifiRestriction)
.setRequiresCharging(acRestriction)
.setRequirementsEnforced(true)
.setUpdateCurrent(true)
.build()
.schedule()
}
}
fun cancelTask() {
JobManager.instance().cancelAllForTag(TAG)
val request = PeriodicWorkRequestBuilder<LibraryUpdateJob>(
interval.toLong(), TimeUnit.HOURS,
10, TimeUnit.MINUTES)
.addTag(TAG)
.setConstraints(constraints)
.build()
WorkManager.getInstance().enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
} else {
WorkManager.getInstance().cancelAllWorkByTag(TAG)
}
}
}
}

View File

@ -1,53 +1,55 @@
package eu.kanade.tachiyomi.data.updater
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import com.evernote.android.job.Job
import com.evernote.android.job.JobManager
import com.evernote.android.job.JobRequest
import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.notificationManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.coroutineScope
import java.util.concurrent.TimeUnit
class UpdaterJob : Job() {
class UpdaterJob(private val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) {
override fun onRunJob(params: Params): Result {
GlobalScope.launch(Dispatchers.IO) {
val result = try { UpdateChecker.getUpdateChecker().checkForUpdate() } catch (e: Exception) { return@launch }
if (result is UpdateResult.NewUpdate<*>) {
val url = result.release.downloadLink
val intent = Intent(context, UpdaterService::class.java).apply {
putExtra(UpdaterService.EXTRA_DOWNLOAD_URL, url)
}
NotificationCompat.Builder(context, Notifications.CHANNEL_COMMON).update {
setContentTitle(context.getString(R.string.app_name))
setContentText(context.getString(R.string.update_available))
setSmallIcon(android.R.drawable.stat_sys_download_done)
color = ContextCompat.getColor(context, R.color.colorAccent)
// Download action
addAction(
android.R.drawable.stat_sys_download_done,
context.getString(R.string.download),
PendingIntent.getService(
context,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
)
}
}
Result.SUCCESS
override suspend fun doWork(): Result = coroutineScope {
val result = try {
UpdateChecker.getUpdateChecker().checkForUpdate()
} catch (e: Exception) {
Result.failure()
}
return Result.SUCCESS
if (result is UpdateResult.NewUpdate<*>) {
val url = result.release.downloadLink
val intent = Intent(context, UpdaterService::class.java).apply {
putExtra(UpdaterService.EXTRA_DOWNLOAD_URL, url)
}
NotificationCompat.Builder(context, Notifications.CHANNEL_COMMON).update {
setContentTitle(context.getString(R.string.app_name))
setContentText(context.getString(R.string.update_available))
setSmallIcon(android.R.drawable.stat_sys_download_done)
color = ContextCompat.getColor(context, R.color.colorAccent)
// Download action
addAction(
android.R.drawable.stat_sys_download_done,
context.getString(R.string.download),
PendingIntent.getService(
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
)
)
}
}
Result.success()
}
fun NotificationCompat.Builder.update(block: NotificationCompat.Builder.() -> Unit) {
@ -56,20 +58,25 @@ class UpdaterJob : Job() {
}
companion object {
const val TAG = "UpdateChecker"
private const val TAG = "UpdateChecker"
fun setupTask() {
JobRequest.Builder(TAG)
.setPeriodic(TimeUnit.DAYS.toMillis(1), TimeUnit.HOURS.toMillis(1))
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setRequirementsEnforced(true)
.setUpdateCurrent(true)
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
.schedule()
val request = PeriodicWorkRequestBuilder<UpdaterJob>(
1, TimeUnit.DAYS,
1, TimeUnit.HOURS)
.addTag(TAG)
.setConstraints(constraints)
.build()
WorkManager.getInstance().enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
}
fun cancelTask() {
JobManager.instance().cancelAllForTag(TAG)
WorkManager.getInstance().cancelAllWorkByTag(TAG)
}
}
}

View File

@ -1,73 +1,95 @@
package eu.kanade.tachiyomi.extension
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import com.evernote.android.job.Job
import com.evernote.android.job.JobManager
import com.evernote.android.job.JobRequest
import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
import eu.kanade.tachiyomi.util.system.notification
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.coroutineScope
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.util.concurrent.TimeUnit
class ExtensionUpdateJob : Job() {
class ExtensionUpdateJob(private val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) {
override fun onRunJob(params: Params): Result {
GlobalScope.launch(Dispatchers.IO) {
val pendingUpdates = ExtensionGithubApi().checkForUpdates(context)
if (pendingUpdates.isNotEmpty()) {
val names = pendingUpdates.map { it.name }
val preferences: PreferencesHelper by injectLazy()
preferences.extensionUpdatesCount().set(pendingUpdates.size)
NotificationManagerCompat.from(context).apply {
notify(Notifications.ID_UPDATES_TO_EXTS,
context.notification(Notifications.CHANNEL_UPDATES_TO_EXTS) {
setContentTitle(
context.resources.getQuantityString(
R.plurals.extension_updates_available, names
.size, names.size
)
)
val extNames = names.joinToString(", ")
setContentText(extNames)
setStyle(NotificationCompat.BigTextStyle().bigText(extNames))
setSmallIcon(R.drawable.ic_extension_update)
color = ContextCompat.getColor(context, R.color.colorAccent)
setContentIntent(
NotificationReceiver.openExtensionsPendingActivity(
context
)
)
setAutoCancel(true)
})
}
}
override suspend fun doWork(): Result = coroutineScope {
val pendingUpdates = try {
ExtensionGithubApi().checkForUpdates(context)
} catch (e: Exception) {
return@coroutineScope Result.failure()
}
if (pendingUpdates.isNotEmpty()) {
createUpdateNotification(pendingUpdates.map { it.name })
}
Result.success()
}
private fun createUpdateNotification(names: List<String>) {
val preferences: PreferencesHelper by injectLazy()
preferences.extensionUpdatesCount().set(names.size)
NotificationManagerCompat.from(context).apply {
notify(Notifications.ID_UPDATES_TO_EXTS,
context.notification(Notifications.CHANNEL_UPDATES_TO_EXTS) {
setContentTitle(
context.resources.getQuantityString(
R.plurals.extension_updates_available, names
.size, names.size
)
)
val extNames = names.joinToString(", ")
setContentText(extNames)
setStyle(NotificationCompat.BigTextStyle().bigText(extNames))
setSmallIcon(R.drawable.ic_extension_update)
color = ContextCompat.getColor(context, R.color.colorAccent)
setContentIntent(
NotificationReceiver.openExtensionsPendingActivity(
context
)
)
setAutoCancel(true)
})
}
return Result.SUCCESS
}
companion object {
const val TAG = "ExtensionUpdate"
private const val TAG = "ExtensionUpdate"
fun setupTask() {
JobRequest.Builder(TAG).setPeriodic(TimeUnit.HOURS.toMillis(12),
TimeUnit.HOURS.toMillis(2))
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.setRequirementsEnforced(true)
.setUpdateCurrent(true)
.build().schedule()
}
fun setupTask(forceAutoUpdateJob: Boolean? = null) {
val preferences = Injekt.get<PreferencesHelper>()
val autoUpdateJob = forceAutoUpdateJob ?: preferences.automaticExtUpdates().getOrDefault()
if (autoUpdateJob) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
fun cancelTask() {
JobManager.instance().cancelAllForTag(TAG)
val request = PeriodicWorkRequestBuilder<ExtensionUpdateJob>(
12, TimeUnit.HOURS,
1, TimeUnit.HOURS)
.addTag(TAG)
.setConstraints(constraints)
.build()
WorkManager.getInstance().enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
} else {
WorkManager.getInstance().cancelAllWorkByTag(TAG)
}
}
}
}

View File

@ -71,7 +71,7 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
if (preferences.libraryUpdateInterval().getOrDefault() > 0 &&
!updatePref(preferences.libraryUpdateCategories(), view.include_global)) {
preferences.libraryUpdateInterval().set(0)
LibraryUpdateJob.cancelTask()
LibraryUpdateJob.setupTask(0)
}
}

View File

@ -94,7 +94,7 @@ class SettingsBackupController : SettingsController() {
onChange { newValue ->
// Always cancel the previous task, it seems that sometimes they are not updated
BackupCreatorJob.cancelTask()
BackupCreatorJob.setupTask(0)
val interval = newValue as Int
if (interval > 0) {

View File

@ -81,7 +81,7 @@ class SettingsLibraryController : SettingsController() {
onChange { newValue ->
// Always cancel the previous task, it seems that sometimes they are not updated.
LibraryUpdateJob.cancelTask()
LibraryUpdateJob.setupTask(0)
val interval = newValue as Int
if (interval > 0) {