Created
March 27, 2021 07:12
-
-
Save ubarua123/9286ab2d9bb645121ba1b0c1ccc1b8fe to your computer and use it in GitHub Desktop.
[Android] Koltin idiomatic way to create WorkManager requests and constraints
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Examples: