From e04d14ccb1ef36d15c13b60ebb3c848ec918ab86 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Sun, 3 Jan 2021 12:45:38 +0600 Subject: [PATCH] fixed issues --- .../dev/inmo/krontab/KronSchedulerWork.kt | 105 +++++++++++++----- .../krontab/KrontabTemplateSchedulerWork.kt | 68 ++++++++++++ 2 files changed, 147 insertions(+), 26 deletions(-) create mode 100644 src/main/kotlin/dev/inmo/krontab/KrontabTemplateSchedulerWork.kt diff --git a/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt b/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt index c770cc3..e22d4b2 100644 --- a/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt +++ b/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt @@ -5,45 +5,68 @@ import androidx.work.* import com.soywiz.klock.DateTime import java.util.concurrent.TimeUnit +/** + * This method will enqueue [OneTimeWorkRequest] with [workName] and [existingWorkPolicy]. Use [setUpRequest] callback + * in case you need some additional actions to do before request will be enqueued + */ suspend fun Context.enqueueKronSchedulerWork( workName: String, delayMillis: Long, workClass: Class, - setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit + existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE, + setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {} ) = WorkManager.getInstance(applicationContext).enqueueUniqueWork( workName, - ExistingWorkPolicy.REPLACE, + existingWorkPolicy, OneTimeWorkRequest.Builder(workClass).apply { setInitialDelay(delayMillis, TimeUnit.MILLISECONDS) setUpRequest() }.build() ) +/** + * This method is shortcut for [enqueueKronSchedulerWork] with reified [T] parameter + */ suspend inline fun Context.enqueueKronSchedulerWork( workName: String, delayMillis: Long, - noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit -) = enqueueKronSchedulerWork(workName, delayMillis, T::class.java, setUpRequest) + existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE, + noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {} +) = enqueueKronSchedulerWork(workName, delayMillis, T::class.java, existingWorkPolicy, setUpRequest) +/** + * This method is shortcut for [enqueueKronSchedulerWork] with [initialScheduler]. It will try to calculate delay by + * itself. In case if [KronScheduler.next] of [initialScheduler] will return null, work WILL NOT be enqueued + * + * @return null in case if [KronScheduler.next] of [initialScheduler] has returned null and work has not been enqueued + */ suspend fun Context.enqueueKronSchedulerWork( workName: String, initialScheduler: KronScheduler, workClass: Class, - setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit + existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE, + setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {} ): Operation? { val now = DateTime.now() val nextTriggerTime = initialScheduler.next(now) val delayMillis = nextTriggerTime ?.minus(now) ?.millisecondsLong ?: return null - return enqueueKronSchedulerWork(workName, delayMillis, workClass, setUpRequest) + return enqueueKronSchedulerWork(workName, delayMillis, workClass, existingWorkPolicy, setUpRequest) } +/** + * This method is shortcut for [enqueueKronSchedulerWork] with reified [T] + */ suspend inline fun Context.enqueueKronSchedulerWork( workName: String, initialScheduler: KronScheduler, - noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit -) = enqueueKronSchedulerWork(workName, initialScheduler, T::class.java, setUpRequest) + existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE, + noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {} +) = enqueueKronSchedulerWork(workName, initialScheduler, T::class.java, existingWorkPolicy, setUpRequest) /** - * Use this class as an super class in case you wish to implement krontab-based enqueuing of works + * Use this class as a super class in case you wish to implement krontab-based enqueuing of works + * + * @see enqueueKronSchedulerWork + * @see KrontabTemplateSchedulerWork */ abstract class KronSchedulerWork( context: Context, @@ -52,29 +75,58 @@ abstract class KronSchedulerWork( context, workerParams ) { + /** + * This variable will be used to reorder new work after that one is happen + * + * @see prolongOnException + * @see prolongOnFailure + * @see prolongOnSuccess + */ protected abstract val workName: String - protected open val prolongWork: Boolean = true - protected open val prolongOnFailure - get() = prolongWork - protected open val prolongOnException = false - protected open val prolongOnNextAbsent = false + /** + * Set this to false in case when this work must not be enqueued after successful complete + */ + protected open val prolongOnSuccess: Boolean = true + /** + * Set this to false in case when this work must not be enqueued after failure complete + */ + protected open val prolongOnFailure + get() = prolongOnSuccess + /** + * Set this to false in case when this work must not be enqueued after exception happen + */ + protected open val prolongOnException = false + + /** + * [KronScheduler] of this method will be used to [prolong] this worker + */ protected abstract suspend fun kronScheduler(): KronScheduler? + + /** + * This method is replacement of [doWork]. It is required to wrap work with [prolong]ing and handling of complete + * state + */ protected abstract suspend fun onWork(): Result + + /** + * Override this method in case you have some additional settings for future [OneTimeWorkRequest] + */ protected open suspend fun OneTimeWorkRequest.Builder.setUpRequest() {} + /** + * This method will [enqueueKronSchedulerWork] using [workName], [kronScheduler] and default + * [ExistingWorkPolicy.REPLACE]. You can call this method in case you want to enqueue work by yourself, but you must + * be sure that you set up to false [prolongOnSuccess], [prolongOnFailure] and [prolongOnException] + */ protected suspend fun prolong() { - val now = DateTime.now() - val nextTriggerTime = kronScheduler() ?.let { - if (prolongOnNextAbsent) { - it.nextOrRelative(now) - } else { - it.next(now) - } + applicationContext.enqueueKronSchedulerWork( + workName, + kronScheduler() ?: return, + this::class.java + ) { + setUpRequest() } - val delayMillis = nextTriggerTime ?.minus(now) ?.millisecondsLong ?: return - - applicationContext.enqueueKronSchedulerWork(workName, delayMillis, this::class.java) { setUpRequest() } } override suspend fun doWork(): Result { @@ -86,8 +138,9 @@ abstract class KronSchedulerWork( } throw e } - if (result !is Result.Failure || prolongOnFailure) { - prolong() + when (result) { + is Result.Failure -> if (prolongOnFailure) prolong() + is Result.Success -> if (prolongOnSuccess) prolong() } return result } diff --git a/src/main/kotlin/dev/inmo/krontab/KrontabTemplateSchedulerWork.kt b/src/main/kotlin/dev/inmo/krontab/KrontabTemplateSchedulerWork.kt new file mode 100644 index 0000000..a4d85ee --- /dev/null +++ b/src/main/kotlin/dev/inmo/krontab/KrontabTemplateSchedulerWork.kt @@ -0,0 +1,68 @@ +package dev.inmo.krontab + +import android.content.Context +import androidx.work.* + +const val krontabTemplateWorkField = "krontabTemplate" + +/** + * Will [enqueueKronSchedulerWork] with [KronScheduler] from [krontabTemplate] and call [setUpRequest] on setting up + * [OneTimeWorkRequest.Builder] with [Data] which will be used to [OneTimeWorkRequest.Builder.setInputData] after + * [setUpRequest] completed + */ +suspend inline fun Context.enqueueKrontabTemplateSchedulerWork( + workName: String, + krontabTemplate: KrontabTemplate, + existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE, + noinline setUpRequest: suspend OneTimeWorkRequest.Builder.( + data: Data + ) -> Unit = {} +) = enqueueKronSchedulerWork(workName, krontabTemplate.toKronScheduler(), T::class.java, existingWorkPolicy) { + val data = workDataOf( + krontabTemplateWorkField to krontabTemplate + ) + setUpRequest(data) + setInputData(data) +} + +/** + * Extend this class in case you wish to base on [KrontabTemplate]. It will automatically handle request of + * [kronScheduler] and put it in [setUpRequest] + */ +abstract class KrontabTemplateSchedulerWork( + context: Context, + workerParams: WorkerParameters +) : KronSchedulerWork(context, workerParams) { + /** + * Will try to get [KrontabTemplate] from [getInputData] by key [krontabTemplateWorkField] + * + * @see setUpRequest + */ + protected val krontabTemplate: KrontabTemplate? + get() = inputData.getString(krontabTemplateWorkField) + + /** + * Override this methods instead of old [setUpRequest] in case you wish to set up some work request parameters + * + * @param data This parameter will be used to put data inside of [OneTimeWorkRequest.Builder] after this method + * will be completed + */ + protected open suspend fun OneTimeWorkRequest.Builder.setUpRequest(data: Data) {} + + /** + * Will automatically put [krontabTemplate] into work data, call [setUpRequest] with future [Data] object and then + * call [OneTimeWorkRequest.Builder.setInputData] with that [Data] object + */ + override suspend fun OneTimeWorkRequest.Builder.setUpRequest() { + val data = workDataOf( + krontabTemplateWorkField to krontabTemplate, + ) + setUpRequest(data) + setInputData(data) + } + + /** + * Will return [KronScheduler] in case if [krontabTemplate] was not null + */ + override suspend fun kronScheduler(): KronScheduler? = krontabTemplate ?.toKronScheduler() +}