Skip to content

Instantly share code, notes, and snippets.

@ubarua123
Created March 27, 2021 07:12
Show Gist options
  • Save ubarua123/9286ab2d9bb645121ba1b0c1ccc1b8fe to your computer and use it in GitHub Desktop.
Save ubarua123/9286ab2d9bb645121ba1b0c1ccc1b8fe to your computer and use it in GitHub Desktop.
[Android] Koltin idiomatic way to create WorkManager requests and constraints
import android.content.Context
import android.net.Uri
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.work.*
import java.time.Duration
import java.util.concurrent.TimeUnit
/**
* Build your constraints using the [ConstraintBuilder]
*/
fun constraintsBuilder(block: ConstraintBuilder.() -> Unit): Constraints = ConstraintBuilder().apply(block).build()
class ConstraintBuilder {
var networkType: NetworkType? = null
var requiresCharging: Boolean = false
var requiresStorageNotLow: Boolean = false
var requiresBatteryNotLow: Boolean = false
var requiresDeviceIdle: Boolean = false
var contentUriTriggers: Uri? = null
var triggerForDescendants: Boolean = false
@RequiresApi(Build.VERSION_CODES.N)
var contentMaxDelay: Long = -1
@RequiresApi(Build.VERSION_CODES.N)
var contentDelay: Long = -1
var timeUnit: TimeUnit = TimeUnit.MILLISECONDS
fun build(): Constraints {
val builder = Constraints.Builder()
networkType?.let {
builder.setRequiredNetworkType(it)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder.setTriggerContentMaxDelay(contentMaxDelay, timeUnit)
builder.setTriggerContentUpdateDelay(contentDelay, timeUnit)
contentUriTriggers?.let {
builder.addContentUriTrigger(it, triggerForDescendants)
}
}
return builder.setRequiresCharging(requiresCharging)
.setRequiresBatteryNotLow(requiresBatteryNotLow)
.setRequiresDeviceIdle(requiresDeviceIdle)
.setRequiresStorageNotLow(requiresStorageNotLow).build()
}
}
/**
* **Enqueue a [OneTimeWorkRequest]. Use [OneTimeRequestBuilder] to construct a request in an Idiomatic Way.**
*
* This is a top level method. You start from here and use the builders along the way to create the work request.
*
* Example usage:
* ```
*enqueueOneTimeWork(context = getApplicationContext(), oneTimeWorkRequestBuilder<*WorkerClass*> {
* this.tag = tag
* constraints = constraintsBuilder {
* networkType = NetworkType.CONNECTED
* }
* backOffPolicy = getDefaultBackOffPolicy()
* data = dataBuilder {
* putInt("id", id)
* putString("extra_string", someText)
* }
* })
* ```
*
* @see [enqueMultipleRequests]
* @see [enqueueUniqueWork]
*
* @param context - Application Context
* @param oneTimeWorkRequest - [OneTimeWorkRequest] object. Build via [oneTimeWorkRequestBuilder]
* */
fun enqueueOneTimeWork(context: Context, oneTimeWorkRequest: OneTimeWorkRequest) {
WorkManager.getInstance(context).enqueue(oneTimeWorkRequest)
}
fun enqueMultipleRequests(context: Context, oneTimeWorkRequest: List<OneTimeWorkRequest>) {
WorkManager.getInstance(context).enqueue(oneTimeWorkRequest)
}
/**
* Enqueues a [OneTimeWorkRequest] as a unique work. Use one of the builders to construct a work request.
* @see OneTimeRequestBuilder
*/
fun enqueueUniqueWork(context: Context, uniqueWork: UniqueWork) {
WorkManager.getInstance(context).enqueueUniqueWork(uniqueWork.uniqueWorkTag!!, uniqueWork.existingWorkPolicy, uniqueWork.workRequest as OneTimeWorkRequest)
}
/** Enqueue a PeriodicWork. All periodic works are Unique hence [UniqueWork]
*
* Usage
*
* ```
* enqueuePeriodicWork(applicationContext, uniqueWorkBuilder {
*
* this.workRequest = periodicWorkRequestBuilder<WorkerClass> {
* this.backOffPolicy = getDefaultBackOffPolicy()
*
* constraints = constraintsBuilder {
* networkType = NetworkType.CONNECTED
* initialDelayDuration = Duration.ofMinutes(1)
* }
*
* this.periodicExistingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.REPLACE
*
* this.data = dataBuilder {
* putInt("param1", 123)
* }
* }
* this.uniqueWorkTag = "work-tag-1"
* })
* ```
* @see [UniqueWorkBuilder] */
fun enqueuePeriodicWork(context: Context, uniqueWork: UniqueWork) {
WorkManager.getInstance(context).enqueueUniquePeriodicWork(uniqueWork.uniqueWorkTag!!, uniqueWork.periodicWorkPolicy, uniqueWork.workRequest as PeriodicWorkRequest)
}
/** Constructs a [UniqueWorkBuilder] */
fun uniqueWorkBuilder(block: UniqueWorkBuilder.() -> Unit): UniqueWork = UniqueWorkBuilder().apply(block).build()
/**
* Helper class to build a [UniqueWork]
*/
class UniqueWorkBuilder {
/** */
var uniqueWorkTag: String? = null
/** Set an [ExistingWorkPolicy] */
var existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE
/** The [WorkRequest] object. Use [OneTimeWorkRequestBuilder] or [PeriodicWorkRequestBuilder]
* Note : Mandatory Field
*/
var workRequest: WorkRequest? = null
/** if its a periodic task, then set an [ExistingPeriodicWorkPolicy] */
var periodicExistingPeriodicWorkPolicy: ExistingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.REPLACE
fun build(): UniqueWork {
return UniqueWork(uniqueWorkTag, existingWorkPolicy, periodicExistingPeriodicWorkPolicy, workRequest!!)
}
}
/** Data class to pass Work params for a unique task */
data class UniqueWork(val uniqueWorkTag: String? = null,
val existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE,
var periodicWorkPolicy: ExistingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.REPLACE,
val workRequest: WorkRequest)
/** Handy lamba type builder method to construct a [OneTimeWorkRequest] */
inline fun <reified T : ListenableWorker> oneTimeWorkRequestBuilder(block: OneTimeRequestBuilder<T>.() -> Unit): OneTimeWorkRequest {
return with(OneTimeRequestBuilder<T>()) {
workerClass = T::class.java
apply(block)
build()
}
}
/** [OneTimeWorkRequest] builder helper class in a kotlin idiomatic way*/
class OneTimeRequestBuilder<T : ListenableWorker> {
/** Initiak Delay in milliseconds */
var initialDelay: Long = 0
/** Work constraints */
var constraints: Constraints? = null
/** [Data] */
var data: Data? = null
lateinit var workerClass: Class<T>
var tag: String? = null
var backOffPolicy: BackOffPolicyPoko? = null
@RequiresApi(Build.VERSION_CODES.O)
var initialDelayDuration: Duration? = Duration.ZERO
fun build(): OneTimeWorkRequest {
val builder = OneTimeWorkRequest.Builder(workerClass)
constraints?.let { builder.setConstraints(it) }
data?.let { builder.setInputData(it) }
tag?.let { builder.addTag(it) }
backOffPolicy?.let { builder.setBackoffCriteria(it.backoffPolicy, it.delay, it.timeUnit) }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && initialDelayDuration != null)
builder.setInitialDelay(initialDelayDuration!!)
else
builder.setInitialDelay(initialDelay, TimeUnit.MILLISECONDS)
return builder.build()
}
}
/** Handy lamba type builder method to construct a [PeriodicWorkRequest] */
inline fun <reified T : ListenableWorker> periodicWorkRequestBuilder(block: PeriodicRequestBuilder<T>.() -> Unit): PeriodicWorkRequest {
return with(PeriodicRequestBuilder<T>()) {
workerClass = T::class.java
apply(block)
build()
}
}
inline fun <reified T : ListenableWorker> periodicWorkRequestBuilderWithFlex(block: PeriodicRequestBuilder<T>.() -> Unit): PeriodicWorkRequest {
return with(PeriodicRequestBuilder<T>()) {
workerClass = T::class.java
apply(block)
buildWithFlexInterval()
}
}
@RequiresApi(Build.VERSION_CODES.O)
inline fun <reified T : ListenableWorker> periodicWorkRequestBuilderDuration(block: PeriodicRequestBuilder<T>.() -> Unit): PeriodicWorkRequest {
return with(PeriodicRequestBuilder<T>()) {
workerClass = T::class.java
apply(block)
buildDuration()
}
}
@RequiresApi(Build.VERSION_CODES.O)
inline fun <reified T : ListenableWorker> periodicWorkRequestBuilderWithFlexAndDuration(block: PeriodicRequestBuilder<T>.() -> Unit): PeriodicWorkRequest {
return with(PeriodicRequestBuilder<T>()) {
workerClass = T::class.java
apply(block)
buildWithDurationAndFlexInterval()
}
}
class PeriodicRequestBuilder<T : ListenableWorker> {
/** Initiak Delay in milliseconds */
var initialDelay: Long = 0
@RequiresApi(Build.VERSION_CODES.O)
var initialDelayDuration: Duration? = Duration.ZERO
/** Work constraints */
var constraints: Constraints? = null
/** [Data] */
var data: Data? = null
lateinit var workerClass: Class<T>
var tag: String? = null
var backOffPolicy: BackOffPolicyPoko? = null
var repeatInterval: Long = 0
/** Common [TimeUnit] for
* * RepeatInterval
* * Flex Interval
*/
var timeUnit: TimeUnit = TimeUnit.MILLISECONDS
var flexInterval: Long = 0
@RequiresApi(Build.VERSION_CODES.O)
var repeatIntervalDuration: Duration? = Duration.ZERO
@RequiresApi(Build.VERSION_CODES.O)
var flexIntervalDuration: Duration? = null
fun build(): PeriodicWorkRequest {
val builder = PeriodicWorkRequest.Builder(workerClass, repeatInterval, timeUnit)
setValues(builder)
return builder.build()
}
@RequiresApi(Build.VERSION_CODES.O)
fun buildDuration(): PeriodicWorkRequest {
val builder = PeriodicWorkRequest.Builder(workerClass, repeatIntervalDuration!!)
return builder.build()
}
fun buildWithFlexInterval(): PeriodicWorkRequest {
val builder = PeriodicWorkRequest.Builder(workerClass, repeatInterval, timeUnit, flexInterval, timeUnit)
setValues(builder)
return builder.build()
}
@RequiresApi(Build.VERSION_CODES.O)
fun buildWithDurationAndFlexInterval(): PeriodicWorkRequest {
val builder = PeriodicWorkRequest.Builder(workerClass, repeatIntervalDuration!!, flexIntervalDuration!!)
setValues(builder)
return builder.build()
}
private fun setValues(builder: PeriodicWorkRequest.Builder) {
constraints?.let { builder.setConstraints(it) }
data?.let { builder.setInputData(it) }
tag?.let { builder.addTag(it) }
backOffPolicy?.let { builder.setBackoffCriteria(it.backoffPolicy, it.delay, it.timeUnit) }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && initialDelayDuration != null)
builder.setInitialDelay(initialDelayDuration!!)
else
builder.setInitialDelay(initialDelay, TimeUnit.MILLISECONDS)
}
}
/** Helper method to prepare a [Data] object
*
* Usage:
* ```
* dataBuilder {
* putInt("param1", 24)
* putString("param2", "Name")
* .
* .
* .
* }
*
*/
inline fun dataBuilder(block: Data.Builder.() -> Unit): Data = Data.Builder().apply(block).build()
/** Prepare a [BackOffPolicyPoko] via this builder to apply to [OneTimeRequestBuilder] */
inline fun backOffPolicyBuilder(block: BackOffPolicyBuilder.() -> Unit): BackOffPolicyPoko = BackOffPolicyBuilder().apply(block).build()
/**
* Prepare a [BackOffPolicyPoko] With Default settings
* * [TimeUnit] = MILLISECONDS
* * Delay = OneTimeWorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS = 30,0000
* * [BackoffPolicy] = [BackoffPolicy.LINEAR]
*/
fun getDefaultBackOffPolicy(): BackOffPolicyPoko = BackOffPolicyBuilder().default()
class BackOffPolicyBuilder {
var backoffPolicy: BackoffPolicy = BackoffPolicy.LINEAR
/** Delay in Miliseconds */
var delay: Long = OneTimeWorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS
var timeUnit: TimeUnit = TimeUnit.MILLISECONDS
fun default(): BackOffPolicyPoko {
return BackOffPolicyPoko()
}
fun build(): BackOffPolicyPoko {
return BackOffPolicyPoko(backoffPolicy, delay, timeUnit)
}
}
/** Plain 'Ol Kotlin Object (Poko) for storing [BackoffPolicy] */
data class BackOffPolicyPoko(val backoffPolicy: BackoffPolicy = BackoffPolicy.LINEAR,
val delay: Long = OneTimeWorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS,
val timeUnit: TimeUnit = TimeUnit.MILLISECONDS)
@ubarua123
Copy link
Author

Examples:

enqueueOneTimeWork(context, oneTimeWorkRequestBuilder<DatabasePreLoaderWorker> { 
                            tag = DB_PRELOADER_TAG
                        })
enqueueUniqueWork(getApplicationContext, uniqueWorkBuilder {
            uniqueWorkTag = "work_${id1}_${id2}"
            existingWorkPolicy = ExistingWorkPolicy.REPLACE

            workRequest = oneTimeWorkRequestBuilder<WorkerClass> {
                data = dataBuilder {
                    // ... bunch of put statements
                }
                initialDelay = Duration.ofSeconds(10).toMillis()
                backOffPolicy = backOffPolicyBuilder {
                    backoffPolicy = BackoffPolicy.EXPONENTIAL
                    delay = 10
                }
                tag = "tag"
            }
        })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment