Last active
November 20, 2021 07:38
-
-
Save ubarua123/ef719ef2bd136c176c2f351db08e8d0b to your computer and use it in GitHub Desktop.
[Android] WorkManager Builder Utility Class - Kotlin
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
/** | |
* Copyright 2021 | |
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
import android.content.Context | |
import android.net.Uri | |
import android.os.Build | |
import androidx.annotation.RequiresApi | |
import androidx.work.BackoffPolicy | |
import androidx.work.Constraints | |
import androidx.work.Data | |
import androidx.work.ExistingPeriodicWorkPolicy | |
import androidx.work.ExistingWorkPolicy | |
import androidx.work.ListenableWorker | |
import androidx.work.NetworkType | |
import androidx.work.OneTimeWorkRequest | |
import androidx.work.OneTimeWorkRequestBuilder | |
import androidx.work.Operation | |
import androidx.work.PeriodicWorkRequest | |
import androidx.work.PeriodicWorkRequestBuilder | |
import androidx.work.WorkContinuation | |
import androidx.work.WorkManager | |
import androidx.work.WorkRequest | |
import java.time.Duration | |
import java.util.concurrent.TimeUnit | |
/** | |
* Utility method to construct a constrains builder. | |
* | |
* Properties: | |
* * [NetworkType] | |
* * requiresCharging | |
* * requiresStorageNotLow | |
* * requiresBatteryNotLow | |
* * requiresDeviceIdle | |
* * contentUriTriggers | |
* * triggerForDescendants | |
* | |
* @param block | |
* @return | |
*/ | |
fun constraintsBuilder(block: ConstraintBuilder.() -> Unit): Constraints = ConstraintBuilder().apply(block).build() | |
val NETWORK_CONSTRAINT = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).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() | |
} | |
} | |
/** Enquue a [OneTimeWorkRequest]. Use [OneTimeRequestBuilder] to construct a request in an Idiomatic Way */ | |
fun enqueueGeneralWork(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): Operation { | |
return WorkManager.getInstance(context).enqueueUniqueWork(uniqueWork.uniqueWorkName, uniqueWork.existingWorkPolicy, uniqueWork.workRequest as OneTimeWorkRequest) | |
} | |
fun beginUniqueWork(context: Context, uniqueWork: UniqueWork): WorkContinuation { | |
return WorkManager.getInstance(context).beginUniqueWork(uniqueWork.uniqueWorkName, 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.uniqueWorkName, uniqueWork.periodicWorkPolicy, uniqueWork.workRequest as PeriodicWorkRequest) | |
} | |
/** Constructs a [UniqueWorkBuilder]. | |
* Parameters: | |
* ``` | |
* existingWorkPolicy | |
* workRequest - [WorkRequest] (One time work request, Perodic Work Request etc) | |
* periodicExistingPeriodicWorkPolicy | |
* ``` | |
*/ | |
fun uniqueWorkBuilder(uniqueWorkName: String, block: UniqueWorkBuilder.() -> Unit): UniqueWork = UniqueWorkBuilder(uniqueWorkName).apply(block).build() | |
/** | |
* Helper class to build a [UniqueWork] | |
* Parameters: | |
* existingWorkPolicy | |
* workRequest - [WorkRequest] (One time work request, Perodic Work Request etc) | |
* periodicExistingPeriodicWorkPolicy | |
*/ | |
class UniqueWorkBuilder constructor(val uniqueWorkName: String) { | |
/** 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(uniqueWorkName, existingWorkPolicy, periodicExistingPeriodicWorkPolicy, workRequest!!) | |
} | |
} | |
/** Data class to pass Work params for a unique task */ | |
data class UniqueWork(val uniqueWorkName: String, | |
val existingWorkPolicy: ExistingWorkPolicy = ExistingWorkPolicy.REPLACE, | |
var periodicWorkPolicy: ExistingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.REPLACE, | |
val workRequest: WorkRequest) | |
/** Handy lamba type builder method to construct a [OneTimeWorkRequest] | |
* | |
* ``` | |
* Fields | |
* * initialDelay | |
* * constraints | |
* * data | |
* * tag | |
* * backOffPolicy | |
* ``` | |
* */ | |
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
Sample Usage:
1.