Convert EH Gallery Updater from a JobService to WorkManager
This commit is contained in:
parent
0d8af29eeb
commit
e9e3340c08
@ -34,7 +34,7 @@ android {
|
||||
minSdkVersion(AndroidConfig.minSdk)
|
||||
targetSdkVersion(AndroidConfig.targetSdk)
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
versionCode = 20
|
||||
versionCode = 21
|
||||
versionName = "1.7.0"
|
||||
|
||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||
|
@ -189,10 +189,6 @@
|
||||
android:exported="false" />
|
||||
|
||||
<!-- EH -->
|
||||
<service
|
||||
android:name="exh.eh.EHentaiUpdateWorker"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="true" />
|
||||
<activity
|
||||
android:name="exh.ui.intercept.InterceptActivity"
|
||||
android:label="@string/app_name"
|
||||
|
@ -30,6 +30,7 @@ import eu.kanade.tachiyomi.ui.library.LibrarySort
|
||||
import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
|
||||
import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||
import exh.eh.EHentaiUpdateWorker
|
||||
import exh.log.xLogE
|
||||
import exh.log.xLogW
|
||||
import exh.merged.sql.models.MergedMangaReference
|
||||
@ -331,6 +332,10 @@ object EXHMigrations {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldVersion under 21) {
|
||||
// Setup EH updater task after migrating to WorkManager
|
||||
EHentaiUpdateWorker.scheduleBackground(context)
|
||||
}
|
||||
|
||||
// if (oldVersion under 1) { } (1 is current release version)
|
||||
// do stuff here when releasing changed crap
|
||||
|
@ -192,8 +192,8 @@ object DebugFunctions {
|
||||
|
||||
fun convertAllExhentaiGalleriesToEhentai() = convertSources(EXH_SOURCE_ID, EH_SOURCE_ID)
|
||||
|
||||
fun testLaunchEhentaiBackgroundUpdater(): String {
|
||||
return EHentaiUpdateWorker.launchBackgroundTest(app)
|
||||
fun testLaunchEhentaiBackgroundUpdater() {
|
||||
EHentaiUpdateWorker.launchBackgroundTest(app)
|
||||
}
|
||||
|
||||
fun rescheduleEhentaiBackgroundUpdater() {
|
||||
|
@ -1,12 +1,14 @@
|
||||
package exh.eh
|
||||
|
||||
import android.app.job.JobInfo
|
||||
import android.app.job.JobParameters
|
||||
import android.app.job.JobScheduler
|
||||
import android.app.job.JobService
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import com.elvishew.xlog.Logger
|
||||
import com.elvishew.xlog.XLog
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
@ -14,7 +16,9 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateNotifier
|
||||
import eu.kanade.tachiyomi.data.preference.CHARGING
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.UNMETERED_NETWORK
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.model.toSChapter
|
||||
import eu.kanade.tachiyomi.source.model.toSManga
|
||||
@ -30,103 +34,35 @@ import exh.source.isEhBasedManga
|
||||
import exh.util.cancellable
|
||||
import exh.util.days
|
||||
import exh.util.executeOnIO
|
||||
import exh.util.hours
|
||||
import exh.util.jobScheduler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.single
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.ArrayList
|
||||
|
||||
class EHentaiUpdateWorker : JobService() {
|
||||
private val scope = CoroutineScope(Dispatchers.Default + Job())
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class EHentaiUpdateWorker(private val context: Context, workerParams: WorkerParameters) :
|
||||
CoroutineWorker(context, workerParams) {
|
||||
private val db: DatabaseHelper by injectLazy()
|
||||
private val prefs: PreferencesHelper by injectLazy()
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
private val updateHelper: EHentaiUpdateHelper by injectLazy()
|
||||
private val logger: Logger = xLog()
|
||||
|
||||
private val updateNotifier by lazy { LibraryUpdateNotifier(this) }
|
||||
private val updateNotifier by lazy { LibraryUpdateNotifier(context) }
|
||||
|
||||
/**
|
||||
* This method is called if the system has determined that you must stop execution of your job
|
||||
* even before you've had a chance to call [.jobFinished].
|
||||
*
|
||||
*
|
||||
* This will happen if the requirements specified at schedule time are no longer met. For
|
||||
* example you may have requested WiFi with
|
||||
* [android.app.job.JobInfo.Builder.setRequiredNetworkType], yet while your
|
||||
* job was executing the user toggled WiFi. Another example is if you had specified
|
||||
* [android.app.job.JobInfo.Builder.setRequiresDeviceIdle], and the phone left its
|
||||
* idle maintenance window. You are solely responsible for the behavior of your application
|
||||
* upon receipt of this message; your app will likely start to misbehave if you ignore it.
|
||||
*
|
||||
*
|
||||
* Once this method returns, the system releases the wakelock that it is holding on
|
||||
* behalf of the job.
|
||||
*
|
||||
* @param params The parameters identifying this job, as supplied to
|
||||
* the job in the [.onStartJob] callback.
|
||||
* @return `true` to indicate to the JobManager whether you'd like to reschedule
|
||||
* this job based on the retry criteria provided at job creation-time; or `false`
|
||||
* to end the job entirely. Regardless of the value returned, your job must stop executing.
|
||||
*/
|
||||
override fun onStopJob(params: JobParameters?): Boolean {
|
||||
runBlocking { scope.coroutineContext[Job]?.cancelAndJoin() }
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to indicate that the job has begun executing. Override this method with the
|
||||
* logic for your job. Like all other component lifecycle callbacks, this method executes
|
||||
* on your application's main thread.
|
||||
*
|
||||
*
|
||||
* Return `true` from this method if your job needs to continue running. If you
|
||||
* do this, the job remains active until you call
|
||||
* [.jobFinished] to tell the system that it has completed
|
||||
* its work, or until the job's required constraints are no longer satisfied. For
|
||||
* example, if the job was scheduled using
|
||||
* [setRequiresCharging(true)][JobInfo.Builder.setRequiresCharging],
|
||||
* it will be immediately halted by the system if the user unplugs the device from power,
|
||||
* the job's [.onStopJob] callback will be invoked, and the app
|
||||
* will be expected to shut down all ongoing work connected with that job.
|
||||
*
|
||||
*
|
||||
* The system holds a wakelock on behalf of your app as long as your job is executing.
|
||||
* This wakelock is acquired before this method is invoked, and is not released until either
|
||||
* you call [.jobFinished], or after the system invokes
|
||||
* [.onStopJob] to notify your job that it is being shut down
|
||||
* prematurely.
|
||||
*
|
||||
*
|
||||
* Returning `false` from this method means your job is already finished. The
|
||||
* system's wakelock for the job will be released, and [.onStopJob]
|
||||
* will not be invoked.
|
||||
*
|
||||
* @param params Parameters specifying info about this job, including the optional
|
||||
* extras configured with [ This object serves to identify this specific running job instance when calling][JobInfo.Builder.setExtras]
|
||||
*/
|
||||
override fun onStartJob(params: JobParameters): Boolean {
|
||||
scope.launch {
|
||||
override suspend fun doWork(): Result {
|
||||
return try {
|
||||
startUpdating()
|
||||
logger.d("Update job completed!")
|
||||
jobFinished(params, false)
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
Result.failure()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun startUpdating() {
|
||||
@ -165,7 +101,7 @@ class EHentaiUpdateWorker : JobService() {
|
||||
|
||||
var failuresThisIteration = 0
|
||||
var updatedThisIteration = 0
|
||||
val updatedManga = ArrayList<Pair<Manga, Array<Chapter>>>()
|
||||
val updatedManga = mutableListOf<Pair<Manga, Array<Chapter>>>()
|
||||
val modifiedThisIteration = mutableSetOf<Long>()
|
||||
|
||||
try {
|
||||
@ -290,86 +226,50 @@ class EHentaiUpdateWorker : JobService() {
|
||||
|
||||
private val MIN_BACKGROUND_UPDATE_FREQ = 1.days.inWholeMilliseconds
|
||||
|
||||
private const val JOB_ID_UPDATE_BACKGROUND = 700000
|
||||
private const val JOB_ID_UPDATE_BACKGROUND_TEST = 700001
|
||||
private const val TAG = "EHBackgroundUpdater"
|
||||
|
||||
private val logger by lazy { XLog.tag("EHUpdaterScheduler") }
|
||||
|
||||
private fun Context.componentName(): ComponentName =
|
||||
ComponentName(this, EHentaiUpdateWorker::class.java)
|
||||
|
||||
private fun Context.baseBackgroundJobInfo(isTest: Boolean): JobInfo.Builder =
|
||||
JobInfo.Builder(
|
||||
if (isTest) JOB_ID_UPDATE_BACKGROUND_TEST
|
||||
else JOB_ID_UPDATE_BACKGROUND,
|
||||
componentName()
|
||||
)
|
||||
|
||||
private fun Context.periodicBackgroundJobInfo(
|
||||
period: Long,
|
||||
requireCharging: Boolean,
|
||||
requireUnmetered: Boolean
|
||||
): JobInfo = baseBackgroundJobInfo(false)
|
||||
.setPeriodic(period)
|
||||
.setPersisted(true)
|
||||
.setRequiredNetworkType(
|
||||
if (requireUnmetered) JobInfo.NETWORK_TYPE_UNMETERED
|
||||
else JobInfo.NETWORK_TYPE_ANY
|
||||
)
|
||||
.apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
setRequiresBatteryNotLow(true)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
setEstimatedNetworkBytes(
|
||||
15000L * UPDATES_PER_ITERATION,
|
||||
1000L * UPDATES_PER_ITERATION
|
||||
)
|
||||
}
|
||||
}
|
||||
.setRequiresCharging(requireCharging)
|
||||
// .setRequiresDeviceIdle(true) Job never seems to run with this
|
||||
.build()
|
||||
|
||||
private fun Context.testBackgroundJobInfo(): JobInfo = baseBackgroundJobInfo(true)
|
||||
.setOverrideDeadline(1)
|
||||
.build()
|
||||
|
||||
fun launchBackgroundTest(context: Context): String =
|
||||
if (context.jobScheduler.schedule(context.testBackgroundJobInfo()) == JobScheduler.RESULT_FAILURE) {
|
||||
logger.e("Failed to schedule background test job!")
|
||||
"Failed"
|
||||
} else {
|
||||
logger.d("Successfully scheduled background test job!")
|
||||
"Success"
|
||||
}
|
||||
fun launchBackgroundTest(context: Context) {
|
||||
WorkManager.getInstance(context).enqueue(OneTimeWorkRequestBuilder<EHentaiUpdateWorker>().build())
|
||||
}
|
||||
|
||||
fun scheduleBackground(context: Context, prefInterval: Int? = null) {
|
||||
cancelBackground(context)
|
||||
|
||||
val preferences = Injekt.get<PreferencesHelper>()
|
||||
val duration = prefInterval ?: preferences.exhAutoUpdateFrequency().get()
|
||||
if (duration > 0) {
|
||||
val interval = prefInterval ?: preferences.exhAutoUpdateFrequency().get()
|
||||
if (interval > 0) {
|
||||
val restrictions = preferences.exhAutoUpdateRequirements().get()
|
||||
val acRestriction = "ac" in restrictions
|
||||
val wifiRestriction = "wifi" in restrictions
|
||||
|
||||
val jobInfo = context.periodicBackgroundJobInfo(
|
||||
duration.hours.inWholeMilliseconds,
|
||||
acRestriction,
|
||||
wifiRestriction
|
||||
)
|
||||
|
||||
if (context.jobScheduler.schedule(jobInfo) == JobScheduler.RESULT_FAILURE) {
|
||||
logger.e("Failed to schedule background update job!")
|
||||
val acRestriction = CHARGING in restrictions
|
||||
val wifiRestriction = if (UNMETERED_NETWORK in restrictions) {
|
||||
NetworkType.UNMETERED
|
||||
} else {
|
||||
logger.d("Successfully scheduled background update job!")
|
||||
NetworkType.CONNECTED
|
||||
}
|
||||
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(wifiRestriction)
|
||||
.setRequiresCharging(acRestriction)
|
||||
.build()
|
||||
|
||||
val request = PeriodicWorkRequestBuilder<EHentaiUpdateWorker>(
|
||||
interval.toLong(),
|
||||
TimeUnit.HOURS,
|
||||
10,
|
||||
TimeUnit.MINUTES
|
||||
)
|
||||
.addTag(TAG)
|
||||
.setConstraints(constraints)
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(context).enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.REPLACE, request)
|
||||
logger.d("Successfully scheduled background update job!")
|
||||
} else {
|
||||
cancelBackground(context)
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelBackground(context: Context) {
|
||||
context.jobScheduler.cancel(JOB_ID_UPDATE_BACKGROUND)
|
||||
WorkManager.getInstance(context).cancelAllWorkByTag(TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user