krontab/src/main/kotlin/dev/inmo/krontab/KronSchedulerWork.kt

148 lines
5.3 KiB
Kotlin
Raw Normal View History

2021-01-02 17:56:31 +00:00
package dev.inmo.krontab
import android.content.Context
import androidx.work.*
import com.soywiz.klock.DateTime
import java.util.concurrent.TimeUnit
2021-01-03 06:45:38 +00:00
/**
* 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
*/
2021-01-02 17:56:31 +00:00
suspend fun <T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
workName: String,
delayMillis: Long,
workClass: Class<T>,
2021-01-03 06:45:38 +00:00
existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {}
2021-01-02 17:56:31 +00:00
) = WorkManager.getInstance(applicationContext).enqueueUniqueWork(
workName,
2021-01-03 06:45:38 +00:00
existingWorkPolicy,
2021-01-02 17:56:31 +00:00
OneTimeWorkRequest.Builder(workClass).apply {
setInitialDelay(delayMillis, TimeUnit.MILLISECONDS)
setUpRequest()
}.build()
)
2021-01-03 06:45:38 +00:00
/**
* This method is shortcut for [enqueueKronSchedulerWork] with reified [T] parameter
*/
2021-01-02 17:56:31 +00:00
suspend inline fun <reified T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
workName: String,
delayMillis: Long,
2021-01-03 06:45:38 +00:00
existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {}
) = enqueueKronSchedulerWork(workName, delayMillis, T::class.java, existingWorkPolicy, setUpRequest)
2021-01-02 17:56:31 +00:00
2021-01-03 06:45:38 +00:00
/**
* 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
*/
2021-01-02 17:56:31 +00:00
suspend fun <T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
workName: String,
initialScheduler: KronScheduler,
workClass: Class<T>,
2021-01-03 06:45:38 +00:00
existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {}
2021-01-02 17:56:31 +00:00
): Operation? {
val now = DateTime.now()
val nextTriggerTime = initialScheduler.next(now)
val delayMillis = nextTriggerTime ?.minus(now) ?.millisecondsLong ?: return null
2021-01-03 06:45:38 +00:00
return enqueueKronSchedulerWork(workName, delayMillis, workClass, existingWorkPolicy, setUpRequest)
2021-01-02 17:56:31 +00:00
}
2021-01-03 06:45:38 +00:00
/**
* This method is shortcut for [enqueueKronSchedulerWork] with reified [T]
*/
2021-01-02 17:56:31 +00:00
suspend inline fun <reified T : KronSchedulerWork> Context.enqueueKronSchedulerWork(
workName: String,
initialScheduler: KronScheduler,
2021-01-03 06:45:38 +00:00
existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
noinline setUpRequest: suspend OneTimeWorkRequest.Builder.() -> Unit = {}
) = enqueueKronSchedulerWork(workName, initialScheduler, T::class.java, existingWorkPolicy, setUpRequest)
2021-01-02 17:56:31 +00:00
/**
2021-01-03 06:45:38 +00:00
* Use this class as a super class in case you wish to implement krontab-based enqueuing of works
*
* @see enqueueKronSchedulerWork
* @see KrontabTemplateSchedulerWork
2021-01-02 17:56:31 +00:00
*/
abstract class KronSchedulerWork(
context: Context,
workerParams: WorkerParameters
) : CoroutineWorker(
context,
workerParams
) {
2021-01-03 06:45:38 +00:00
/**
* This variable will be used to reorder new work after that one is happen
*
* @see prolongOnException
* @see prolongOnFailure
* @see prolongOnSuccess
*/
2021-01-02 17:56:31 +00:00
protected abstract val workName: String
2021-01-03 06:45:38 +00:00
/**
* 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
*/
2021-01-02 17:56:31 +00:00
protected open val prolongOnFailure
2021-01-03 06:45:38 +00:00
get() = prolongOnSuccess
/**
* Set this to false in case when this work must not be enqueued after exception happen
*/
2021-01-02 17:56:31 +00:00
protected open val prolongOnException = false
2021-01-03 06:45:38 +00:00
/**
* [KronScheduler] of this method will be used to [prolong] this worker
*/
2021-01-02 17:56:31 +00:00
protected abstract suspend fun kronScheduler(): KronScheduler?
2021-01-03 06:45:38 +00:00
/**
* This method is replacement of [doWork]. It is required to wrap work with [prolong]ing and handling of complete
* state
*/
2021-01-02 17:56:31 +00:00
protected abstract suspend fun onWork(): Result
2021-01-03 06:45:38 +00:00
/**
* Override this method in case you have some additional settings for future [OneTimeWorkRequest]
*/
2021-01-02 17:56:31 +00:00
protected open suspend fun OneTimeWorkRequest.Builder.setUpRequest() {}
2021-01-03 06:45:38 +00:00
/**
* 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]
*/
2021-01-02 17:56:31 +00:00
protected suspend fun prolong() {
2021-01-03 06:45:38 +00:00
applicationContext.enqueueKronSchedulerWork(
workName,
kronScheduler() ?: return,
this::class.java
) {
setUpRequest()
2021-01-02 17:56:31 +00:00
}
}
override suspend fun doWork(): Result {
val result = try {
onWork()
} catch (e: Throwable) {
if (prolongOnException) {
prolong()
}
throw e
}
2021-01-03 06:45:38 +00:00
when (result) {
is Result.Failure -> if (prolongOnFailure) prolong()
is Result.Success -> if (prolongOnSuccess) prolong()
2021-01-02 17:56:31 +00:00
}
return result
}
}