Migrate to WorkManager

pull/3117/head
arkon 4 years ago committed by Jay
parent beeabe17c4
commit 329e8f1988

@ -151,7 +151,9 @@ dependencies {
implementation("org.jsoup:jsoup:1.13.1") implementation("org.jsoup:jsoup:1.13.1")
// Job scheduling // 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") implementation("com.google.android.gms:play-services-gcm:17.0.0")
// Changelog // Changelog

@ -8,14 +8,9 @@ import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner
import androidx.multidex.MultiDex 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.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault 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.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import org.acra.ACRA import org.acra.ACRA
@ -43,7 +38,6 @@ open class App : Application(), LifecycleObserver {
Injekt.importModule(AppModule(this)) Injekt.importModule(AppModule(this))
setupAcra() setupAcra()
setupJobManager()
setupNotificationChannels() setupNotificationChannels()
LocaleHelper.updateConfiguration(this, resources.configuration) LocaleHelper.updateConfiguration(this, resources.configuration)
@ -73,22 +67,6 @@ open class App : Application(), LifecycleObserver {
ACRA.init(this) 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() { protected open fun setupNotificationChannels() {
Notifications.createChannels(this) Notifications.createChannels(this)
} }

@ -1,10 +1,12 @@
package eu.kanade.tachiyomi package eu.kanade.tachiyomi
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
import eu.kanade.tachiyomi.data.download.DownloadProvider import eu.kanade.tachiyomi.data.download.DownloadProvider
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.data.updater.UpdaterJob import eu.kanade.tachiyomi.data.updater.UpdaterJob
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.ui.library.LibraryPresenter import eu.kanade.tachiyomi.ui.library.LibraryPresenter
import java.io.File import java.io.File
@ -64,8 +66,16 @@ object Migrations {
} }
if (oldVersion < 54) if (oldVersion < 54)
DownloadProvider(context).renameChaapters() DownloadProvider(context).renameChaapters()
if (oldVersion < 62) if (oldVersion < 62) {
LibraryPresenter.updateDB() 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 true
} }
return false return false

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

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

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

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

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

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

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

Loading…
Cancel
Save