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")
// 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

@ -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)
}

@ -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

@ -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)
}
}
}
}

@ -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)
}
}
}
}

@ -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
override suspend fun doWork(): Result = coroutineScope {
val result = try {
UpdateChecker.getUpdateChecker().checkForUpdate()
} catch (e: Exception) {
Result.failure()
}
if (result is UpdateResult.NewUpdate<*>) {
val url = result.release.downloadLink
val intent = Intent(context, UpdaterService::class.java).apply {
putExtra(UpdaterService.EXTRA_DOWNLOAD_URL, url)
}
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
)
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
}
return Result.SUCCESS
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)
}
}
}

@ -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)
}
}
}
}

@ -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)
}
}

@ -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) {

@ -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) {

Loading…
Cancel
Save