diff --git a/backgroundwork/build.gradle.kts b/backgroundwork/build.gradle.kts
index a7da5e6f3..0f212b4ea 100644
--- a/backgroundwork/build.gradle.kts
+++ b/backgroundwork/build.gradle.kts
@@ -28,4 +28,10 @@ android {
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.appcompat)
+ implementation(libs.androidx.work.runtime)
+ implementation(libs.androidx.work.runtime.ktx)
+ implementation(libs.androidx.work.multiprocess)
+ implementation(libs.androidx.concurrent.futures.ktx)
+ implementation(libs.google.android.material)
+ implementation(libs.guava.android)
}
diff --git a/backgroundwork/src/main/AndroidManifest.xml b/backgroundwork/src/main/AndroidManifest.xml
index 7b483aecc..01d4830e1 100644
--- a/backgroundwork/src/main/AndroidManifest.xml
+++ b/backgroundwork/src/main/AndroidManifest.xml
@@ -18,7 +18,28 @@
xmlns:tools="http://schemas.android.com/tools">
+
+
+
+
+
-
+
+
+
+
+
+
+
diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt
new file mode 100644
index 000000000..f86c8f31b
--- /dev/null
+++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2026 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.snippets.backgroundwork
+
+import android.content.ComponentName
+import android.content.Context
+import androidx.work.CoroutineWorker
+import androidx.work.Data
+import androidx.work.OneTimeWorkRequest
+import androidx.work.WorkerParameters
+import androidx.work.multiprocess.RemoteCoroutineWorker
+import androidx.work.multiprocess.RemoteListenableWorker.ARGUMENT_CLASS_NAME
+import androidx.work.multiprocess.RemoteListenableWorker.ARGUMENT_PACKAGE_NAME
+import androidx.work.multiprocess.RemoteWorkerService
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+private object SimpleCoroutineDownloadWorker {
+ // [START android_background_coroutine_download_worker]
+ class CoroutineDownloadWorker(
+ context: Context,
+ params: WorkerParameters
+ ) : CoroutineWorker(context, params) {
+
+ override suspend fun doWork(): Result {
+ val data = downloadSynchronously("https://www.google.com")
+ saveData(data)
+ return Result.success()
+ }
+ }
+ // [END android_background_coroutine_download_worker]
+}
+
+private object WithContextCoroutineDownloadWorker {
+ // [START android_background_coroutine_download_worker_with_context]
+ class CoroutineDownloadWorker(
+ context: Context,
+ params: WorkerParameters
+ ) : CoroutineWorker(context, params) {
+
+ override suspend fun doWork(): Result {
+ return withContext(Dispatchers.IO) {
+ val data = downloadSynchronously("https://www.google.com")
+ saveData(data)
+ Result.success()
+ }
+ }
+ }
+ // [END android_background_coroutine_download_worker_with_context]
+}
+
+private fun remoteCoroutineWorkerRequest(): OneTimeWorkRequest {
+ // [START android_background_remote_coroutine_worker_request]
+ val PACKAGE_NAME = "com.example.background.multiprocess"
+
+ val serviceName = RemoteWorkerService::class.java.name
+ val componentName = ComponentName(PACKAGE_NAME, serviceName)
+
+ val data: Data = Data.Builder()
+ .putString(ARGUMENT_PACKAGE_NAME, componentName.packageName)
+ .putString(ARGUMENT_CLASS_NAME, componentName.className)
+ .build()
+
+ return OneTimeWorkRequest.Builder(ExampleRemoteCoroutineWorker::class.java)
+ .setInputData(data)
+ .build()
+ // [END android_background_remote_coroutine_worker_request]
+}
+
+private fun downloadSynchronously(url: String): String = ""
+private fun saveData(data: String) {}
+
+class ExampleRemoteCoroutineWorker(
+ context: Context,
+ parameters: WorkerParameters
+) : RemoteCoroutineWorker(context, parameters) {
+ override suspend fun doRemoteWork(): Result = Result.success()
+}
diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt
new file mode 100644
index 000000000..ff9d0d896
--- /dev/null
+++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2026 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.snippets.backgroundwork
+
+import android.app.Application
+import android.content.Context
+import androidx.work.Configuration
+import androidx.work.WorkManager
+
+// [START android_background_custom_configuration_on_demand]
+class MyApplication() : Application(), Configuration.Provider {
+ override val workManagerConfiguration: Configuration
+ get() = Configuration.Builder()
+ .setMinimumLoggingLevel(android.util.Log.INFO)
+ .build()
+}
+// [END android_background_custom_configuration_on_demand]
+
+private fun Context.manualInitialization() {
+ // [START android_background_custom_configuration_manual_init]
+ // provide custom configuration.
+ val myConfig = Configuration.Builder()
+ .setMinimumLoggingLevel(android.util.Log.INFO)
+ .build()
+
+ // initialize WorkManager.
+ WorkManager.initialize(this, myConfig)
+ // [END android_background_custom_configuration_manual_init]
+}
diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt
new file mode 100644
index 000000000..f8564cfc7
--- /dev/null
+++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2026 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.snippets.backgroundwork
+
+import android.app.Notification
+import android.content.Context
+import androidx.work.BackoffPolicy
+import androidx.work.Constraints
+import androidx.work.CoroutineWorker
+import androidx.work.ForegroundInfo
+import androidx.work.NetworkType
+import androidx.work.OneTimeWorkRequest
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.OutOfQuotaPolicy
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.WorkRequest
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+import androidx.work.workDataOf
+import java.util.concurrent.TimeUnit
+
+private fun enqueueWorkSample(myContext: Context) {
+ // [START android_background_enqueue_work_request]
+ val myWorkRequest = OneTimeWorkRequestBuilder().build()
+ WorkManager.getInstance(myContext).enqueue(myWorkRequest)
+ // [END android_background_enqueue_work_request]
+}
+
+private fun scheduleOneTimeWorkFrom() {
+ // [START android_background_schedule_one_time_work_from]
+ val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)
+ // [END android_background_schedule_one_time_work_from]
+}
+
+private fun scheduleOneTimeWorkBuilder() {
+ // [START android_background_schedule_one_time_work_builder]
+ val uploadWorkRequest: WorkRequest =
+ OneTimeWorkRequestBuilder()
+ // Additional configuration.
+ .build()
+ // [END android_background_schedule_one_time_work_builder]
+}
+
+private fun expeditedWorkRequest(context: Context) {
+ // [START android_background_expedited_work_request]
+ val request = OneTimeWorkRequestBuilder()
+ .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
+ .build()
+
+ WorkManager.getInstance(context)
+ .enqueue(request)
+ // [END android_background_expedited_work_request]
+}
+
+// [START android_background_expedited_coroutine_worker]
+class ExpeditedWorker(appContext: Context, workerParams: WorkerParameters) :
+ CoroutineWorker(appContext, workerParams) {
+
+ override suspend fun getForegroundInfo(): ForegroundInfo {
+ return ForegroundInfo(
+ NOTIFICATION_ID, createNotification()
+ )
+ }
+
+ override suspend fun doWork(): Result {
+ TODO()
+ }
+
+ private fun createNotification(): Notification {
+ TODO()
+ }
+
+ // [START_EXCLUDE silent]
+ companion object {
+ private const val NOTIFICATION_ID = 1
+ }
+ // [END_EXCLUDE]
+}
+// [END android_background_expedited_coroutine_worker]
+
+private fun schedulePeriodicWork() {
+ // [START android_background_periodic_work_request]
+ val saveRequest =
+ PeriodicWorkRequestBuilder(1, TimeUnit.HOURS)
+ // Additional configuration.
+ .build()
+ // [END android_background_periodic_work_request]
+}
+
+private fun schedulePeriodicWorkFlex() {
+ // [START android_background_periodic_work_request_flex]
+ val myUploadWork = PeriodicWorkRequestBuilder(
+ 1, TimeUnit.HOURS, // repeatInterval (the period cycle).
+ 15, TimeUnit.MINUTES
+ ) // flexInterval.
+ .build()
+ // [END android_background_periodic_work_request_flex]
+}
+
+private fun workConstraints() {
+ // [START android_background_work_constraints]
+ val constraints = Constraints.Builder()
+ .setRequiredNetworkType(NetworkType.UNMETERED)
+ .setRequiresCharging(true)
+ .build()
+
+ val myWorkRequest: WorkRequest =
+ OneTimeWorkRequestBuilder()
+ .setConstraints(constraints)
+ .build()
+ // [END android_background_work_constraints]
+}
+
+private fun delayedWork() {
+ // [START android_background_delayed_work]
+ val myWorkRequest = OneTimeWorkRequestBuilder()
+ .setInitialDelay(10, TimeUnit.MINUTES)
+ .build()
+ // [END android_background_delayed_work]
+}
+
+private fun retryBackoffPolicy() {
+ // [START android_background_retry_backoff_policy]
+ val myWorkRequest = OneTimeWorkRequestBuilder()
+ .setBackoffCriteria(
+ BackoffPolicy.LINEAR,
+ WorkRequest.MIN_BACKOFF_MILLIS,
+ TimeUnit.MILLISECONDS
+ )
+ .build()
+ // [END android_background_retry_backoff_policy]
+}
+
+private fun tagWork() {
+ // [START android_background_tag_work]
+ val myWorkRequest = OneTimeWorkRequestBuilder()
+ .addTag("cleanup")
+ .build()
+ // [END android_background_tag_work]
+}
+
+// [START android_background_assign_input_data]
+// Define the Worker requiring input.
+class UploadWork(appContext: Context, workerParams: WorkerParameters) :
+ Worker(appContext, workerParams) {
+
+ override fun doWork(): Result {
+ val imageUriInput =
+ inputData.getString("IMAGE_URI") ?: return Result.failure()
+
+ uploadFile(imageUriInput)
+ return Result.success()
+ }
+ // [START_EXCLUDE]
+ private fun uploadFile(uri: String) {}
+ // [END_EXCLUDE]
+}
+
+// Create a WorkRequest for your Worker and sending it input.
+val myUploadWork = OneTimeWorkRequestBuilder()
+ .setInputData(
+ workDataOf(
+ "IMAGE_URI" to "http://..."
+ )
+ )
+ .build()
+// [END android_background_assign_input_data]
+
+class MyWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
+ override fun doWork(): Result = Result.success()
+}
+
+class SyncWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
+ override fun doWork(): Result = Result.success()
+}
+
+class SaveImageToFileWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
+ override fun doWork(): Result = Result.success()
+}
diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ListenableWorkerThreading.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ListenableWorkerThreading.kt
new file mode 100644
index 000000000..a3202b62c
--- /dev/null
+++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ListenableWorkerThreading.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2026 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.snippets.backgroundwork
+
+import android.content.ComponentName
+import android.content.Context
+import androidx.concurrent.futures.CallbackToFutureAdapter
+import androidx.work.Data
+import androidx.work.ListenableWorker
+import androidx.work.OneTimeWorkRequest
+import androidx.work.WorkerParameters
+import androidx.work.multiprocess.RemoteListenableWorker
+import androidx.work.multiprocess.RemoteListenableWorker.ARGUMENT_CLASS_NAME
+import androidx.work.multiprocess.RemoteListenableWorker.ARGUMENT_PACKAGE_NAME
+import androidx.work.multiprocess.RemoteWorkerService
+import com.google.common.util.concurrent.ListenableFuture
+import java.io.IOException
+import java.util.concurrent.Executor
+import java.util.concurrent.Executors
+
+private object CallbackWorkerBasic {
+ // [START android_background_callback_worker]
+ class CallbackWorker(
+ context: Context,
+ params: WorkerParameters
+ ) : ListenableWorker(context, params) {
+ override fun startWork(): ListenableFuture {
+ return CallbackToFutureAdapter.getFuture { completer ->
+ val callback = object : Callback {
+ var successes = 0
+
+ override fun onFailure(call: Call, e: IOException) {
+ completer.setException(e)
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ successes++
+ if (successes == 100) {
+ completer.set(Result.success())
+ }
+ }
+ }
+
+ repeat(100) {
+ downloadAsynchronously("https://example.com", callback)
+ }
+
+ callback
+ }
+ }
+ }
+ // [END android_background_callback_worker]
+}
+
+private object CallbackWorkerCancellation {
+ // [START android_background_callback_worker_cancellation]
+ class CallbackWorker(
+ context: Context,
+ params: WorkerParameters
+ ) : ListenableWorker(context, params) {
+ override fun startWork(): ListenableFuture {
+ return CallbackToFutureAdapter.getFuture { completer ->
+ val callback = object : Callback {
+ var successes = 0
+
+ override fun onFailure(call: Call, e: IOException) {
+ completer.setException(e)
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ ++successes
+ if (successes == 100) {
+ completer.set(Result.success())
+ }
+ }
+ }
+
+ completer.addCancellationListener(cancelDownloadsRunnable, executor)
+
+ repeat(100) {
+ downloadAsynchronously("https://example.com", callback)
+ }
+
+ callback
+ }
+ }
+ }
+ // [END android_background_callback_worker_cancellation]
+}
+
+private fun remoteListenableWorkerRequest(): OneTimeWorkRequest {
+ // [START android_background_remote_listenable_worker_request]
+ val PACKAGE_NAME = "com.example.background.multiprocess"
+
+ val serviceName = RemoteWorkerService::class.java.name
+ val componentName = ComponentName(PACKAGE_NAME, serviceName)
+
+ val data: Data = Data.Builder()
+ .putString(ARGUMENT_PACKAGE_NAME, componentName.packageName)
+ .putString(ARGUMENT_CLASS_NAME, componentName.className)
+ .build()
+
+ return OneTimeWorkRequest.Builder(ExampleRemoteListenableWorker::class.java)
+ .setInputData(data)
+ .build()
+ // [END android_background_remote_listenable_worker_request]
+}
+
+private interface Callback {
+ fun onFailure(call: Call, e: IOException)
+ fun onResponse(call: Call, response: Response)
+}
+
+private class Call
+private class Response
+
+private fun downloadAsynchronously(url: String, callback: Callback) {}
+private val cancelDownloadsRunnable = Runnable {}
+private val executor: Executor = Executors.newSingleThreadExecutor()
+
+class ExampleRemoteListenableWorker(
+ context: Context,
+ parameters: WorkerParameters
+) : RemoteListenableWorker(context, parameters) {
+ override fun startRemoteWork(): ListenableFuture =
+ CallbackToFutureAdapter.getFuture { it.set(Result.success()) }
+}
diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt
new file mode 100644
index 000000000..78677a9cd
--- /dev/null
+++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2026 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.snippets.backgroundwork
+
+import android.annotation.SuppressLint
+import android.app.Notification
+import android.app.NotificationManager
+import android.content.Context
+import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION
+import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.core.app.NotificationCompat
+import androidx.work.CoroutineWorker
+import androidx.work.ForegroundInfo
+import androidx.work.WorkManager
+import androidx.work.WorkerParameters
+
+@SuppressLint("ObsoleteSdkInt")
+// [START android_background_long_running_coroutine_worker]
+class DownloadWorker(context: Context, parameters: WorkerParameters) :
+ CoroutineWorker(context, parameters) {
+
+ private val notificationManager =
+ context.getSystemService(Context.NOTIFICATION_SERVICE) as
+ NotificationManager
+
+ // [START_EXCLUDE silent]
+ private val notificationId = 1
+ // [END_EXCLUDE]
+ override suspend fun doWork(): Result {
+ val inputUrl = inputData.getString(KEY_INPUT_URL)
+ ?: return Result.failure()
+ val outputFile = inputData.getString(KEY_OUTPUT_FILE_NAME)
+ ?: return Result.failure()
+ // Mark the Worker as important.
+ val progress = "Starting Download"
+ setForeground(createForegroundInfo(progress))
+ download(inputUrl, outputFile)
+ return Result.success()
+ }
+
+ private fun download(inputUrl: String, outputFile: String) {
+ // Downloads a file and updates bytes read.
+ // Calls setForeground() periodically when it needs to update
+ // the ongoing Notification.
+ }
+ // Creates an instance of ForegroundInfo which can be used to update the
+ // ongoing notification.
+ private fun createForegroundInfo(progress: String): ForegroundInfo {
+ val id = applicationContext.getString(R.string.notification_channel_id)
+ val title = applicationContext.getString(R.string.notification_title)
+ val cancel = applicationContext.getString(R.string.cancel_download)
+ // This PendingIntent can be used to cancel the worker.
+ val intent = WorkManager.getInstance(applicationContext)
+ .createCancelPendingIntent(getId())
+
+ // Create a Notification channel if necessary.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ createChannel()
+ }
+
+ val notification = NotificationCompat.Builder(applicationContext, id)
+ .setContentTitle(title)
+ .setTicker(title)
+ .setContentText(progress)
+ .setSmallIcon(R.drawable.ic_work_notification)
+ .setOngoing(true)
+ // Add the cancel action to the notification which can
+ // be used to cancel the worker.
+ .addAction(android.R.drawable.ic_delete, cancel, intent)
+ .build()
+
+ return ForegroundInfo(notificationId, notification)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ private fun createChannel() {
+ // Create a Notification channel.
+ }
+
+ companion object {
+ const val KEY_INPUT_URL = "KEY_INPUT_URL"
+ const val KEY_OUTPUT_FILE_NAME = "KEY_OUTPUT_FILE_NAME"
+ }
+}
+// [END android_background_long_running_coroutine_worker]
+
+class ForegroundServiceTypeSnippet(
+ context: Context,
+ parameters: WorkerParameters
+) : CoroutineWorker(context, parameters) {
+ override suspend fun doWork(): Result = Result.success()
+
+ private val NOTIFICATION_ID = 1
+ private lateinit var notification: Notification
+
+ // [START android_background_long_running_foreground_service_type]
+ private fun createForegroundInfo(progress: String): ForegroundInfo {
+ // ...
+ return ForegroundInfo(
+ NOTIFICATION_ID,
+ notification,
+ FOREGROUND_SERVICE_TYPE_LOCATION or
+ FOREGROUND_SERVICE_TYPE_MICROPHONE
+ )
+ }
+ // [END android_background_long_running_foreground_service_type]
+}
diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt
new file mode 100644
index 000000000..a106bed20
--- /dev/null
+++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2026 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.snippets.backgroundwork
+
+import android.content.Context
+import android.view.View
+import androidx.work.Constraints
+import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.PeriodicWorkRequestBuilder
+import androidx.work.WorkInfo
+import androidx.work.WorkManager
+import androidx.work.WorkQuery
+import androidx.work.WorkRequest
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+import com.google.android.material.snackbar.Snackbar
+import com.google.common.util.concurrent.ListenableFuture
+import java.util.concurrent.TimeUnit
+
+private fun manageWorkEnqueue(requireContext: () -> Context) {
+ // [START android_background_manage_work_enqueue]
+ // [START_EXCLUDE silent]
+ /*
+ // [END_EXCLUDE]
+ val myWork: WorkRequest = // ... OneTime or PeriodicWork.
+ // [START_EXCLUDE silent]
+ */
+ val myWork: WorkRequest = OneTimeWorkRequestBuilder().build()
+ // [END_EXCLUDE]
+ WorkManager.getInstance(requireContext()).enqueue(myWork)
+ // [END android_background_manage_work_enqueue]
+}
+
+private fun Context.uniquePeriodicWork() {
+ // [START android_background_unique_periodic_work]
+ val sendLogsWorkRequest =
+ PeriodicWorkRequestBuilder(24, TimeUnit.HOURS)
+ .setConstraints(
+ Constraints.Builder()
+ .setRequiresCharging(true)
+ .build()
+ )
+ .build()
+ WorkManager.getInstance(this).enqueueUniquePeriodicWork(
+ "sendLogs",
+ ExistingPeriodicWorkPolicy.KEEP,
+ sendLogsWorkRequest
+ )
+ // [END android_background_unique_periodic_work]
+}
+
+private fun observeWorkQuery(workManager: WorkManager, syncWorker: WorkRequest) {
+ // [START android_background_observe_work_query]
+ // by id.
+ workManager.getWorkInfoById(syncWorker.id) // ListenableFuture
+
+ // by name.
+ workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture>
+
+ // by tag.
+ workManager.getWorkInfosByTag("syncTag") // ListenableFuture>
+ // [END android_background_observe_work_query]
+}
+
+private suspend fun observeWorkFlow(
+ workManager: WorkManager,
+ syncWorker: WorkRequest,
+ requireView: () -> View
+) {
+ // [START android_background_observe_work_flow]
+ workManager.getWorkInfoByIdFlow(syncWorker.id)
+ .collect { workInfo ->
+ if (workInfo?.state == WorkInfo.State.SUCCEEDED) {
+ Snackbar.make(
+ requireView(),
+ R.string.work_completed, Snackbar.LENGTH_SHORT
+ )
+ .show()
+ }
+ }
+ // [END android_background_observe_work_flow]
+}
+
+private fun complexWorkQueries(workManager: WorkManager) {
+ // [START android_background_complex_work_queries]
+ val workQuery = WorkQuery.Builder
+ .fromTags(listOf("syncTag"))
+ .addStates(listOf(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
+ .addUniqueWorkNames(
+ listOf("preProcess", "sync")
+ )
+ .build()
+
+ val workInfos: ListenableFuture> = workManager.getWorkInfos(workQuery)
+ // [END android_background_complex_work_queries]
+}
+
+private fun cancelWork(workManager: WorkManager, syncWorker: WorkRequest) {
+ // [START android_background_cancel_work]
+ // by id.
+ workManager.cancelWorkById(syncWorker.id)
+
+ // by name.
+ workManager.cancelUniqueWork("sync")
+
+ // by tag.
+ workManager.cancelAllWorkByTag("syncTag")
+ // [END android_background_cancel_work]
+}
+
+class SendLogsWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
+ override fun doWork(): Result = Result.success()
+}
diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt
new file mode 100644
index 000000000..dbc2748d6
--- /dev/null
+++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2026 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.snippets.backgroundwork
+
+import android.content.Context
+import androidx.work.CoroutineWorker
+import androidx.work.WorkInfo
+import androidx.work.WorkManager
+import androidx.work.WorkRequest
+import androidx.work.WorkerParameters
+import androidx.work.workDataOf
+import java.util.UUID
+import kotlinx.coroutines.delay
+
+// [START android_background_observe_progress_worker]
+// import android.content.Context
+// import androidx.work.CoroutineWorker
+// import androidx.work.WorkerParameters
+// import kotlinx.coroutines.delay
+
+class ProgressWorker(context: Context, parameters: WorkerParameters) :
+ CoroutineWorker(context, parameters) {
+
+ companion object {
+ const val Progress = "Progress"
+ private const val delayDuration = 1L
+ }
+
+ override suspend fun doWork(): Result {
+ val firstUpdate = workDataOf(Progress to 0)
+ val lastUpdate = workDataOf(Progress to 100)
+ setProgress(firstUpdate)
+ delay(delayDuration)
+ setProgress(lastUpdate)
+ return Result.success()
+ }
+}
+// [END android_background_observe_progress_worker]
+
+private suspend fun observeProgressFlow(applicationContext: Context, requestId: UUID) {
+ // [START android_background_observe_progress_flow]
+ WorkManager.getInstance(applicationContext)
+ // requestId is the WorkRequest id.
+ .getWorkInfoByIdFlow(requestId)
+ .collect { workInfo: WorkInfo? ->
+ if (workInfo != null) {
+ val progress = workInfo.progress
+ val value = progress.getInt("Progress", 0)
+ // Do something with progress information.
+ }
+ }
+ // [END android_background_observe_progress_flow]
+}
+
+private suspend fun observeStopReason(workManager: WorkManager, syncWorker: WorkRequest) {
+ // [START android_background_observe_stop_reason]
+ workManager.getWorkInfoByIdFlow(syncWorker.id)
+ .collect { workInfo ->
+ if (workInfo != null) {
+ val stopReason = workInfo.stopReason
+ logStopReason(syncWorker.id, stopReason)
+ }
+ }
+ // [END android_background_observe_stop_reason]
+}
+
+private fun logStopReason(id: UUID, stopReason: Int) {}
diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt
new file mode 100644
index 000000000..265163e5f
--- /dev/null
+++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2026 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.snippets.backgroundwork
+
+import android.content.Context
+import androidx.concurrent.futures.await
+import androidx.work.Constraints
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import androidx.work.WorkRequest
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+import java.util.UUID
+
+private const val PHOTO_UPLOAD_WORK_NAME = "photo_upload"
+
+// [START android_background_update_photo_upload_work]
+suspend fun updatePhotoUploadWork(context: Context) {
+ // Get instance of WorkManager.
+ val workManager = WorkManager.getInstance(context)
+
+ // Retrieve the work request ID. In this example, the work being updated is unique
+ // work so we can retrieve the ID using the unique work name.
+ val photoUploadWorkInfoList = workManager.getWorkInfosForUniqueWork(
+ PHOTO_UPLOAD_WORK_NAME
+ ).await()
+
+ val existingWorkRequestId = photoUploadWorkInfoList.firstOrNull()?.id ?: return
+
+ // Update the constraints of the WorkRequest to not require a charging device.
+ val newConstraints = Constraints.Builder()
+ // Add other constraints as required here.
+ .setRequiresCharging(false)
+ .build()
+
+ // Create new WorkRequest from existing Worker, new constraints, and the id of the old WorkRequest.
+ val updatedWorkRequest: WorkRequest =
+ OneTimeWorkRequestBuilder()
+ .setConstraints(newConstraints)
+ .setId(existingWorkRequestId)
+ .build()
+
+ // Pass the new WorkRequest to updateWork().
+ workManager.updateWork(updatedWorkRequest)
+}
+// [END android_background_update_photo_upload_work]
+
+private fun trackWorkGeneration(context: Context, oldWorkRequestId: UUID) {
+ // [START android_background_track_work_generation]
+ // Get instance of WorkManager.
+ val workManager = WorkManager.getInstance(context)
+
+ // Retrieve WorkInfo instance.
+ val workInfo = workManager.getWorkInfoById(oldWorkRequestId).get()
+
+ // Retrieve the generation.
+ val generation = workInfo?.generation
+ // [END android_background_track_work_generation]
+}
+
+class MyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
+ override fun doWork(): Result = Result.success()
+}
diff --git a/backgroundwork/src/main/res/drawable/ic_work_notification.xml b/backgroundwork/src/main/res/drawable/ic_work_notification.xml
new file mode 100644
index 000000000..9d6286b2f
--- /dev/null
+++ b/backgroundwork/src/main/res/drawable/ic_work_notification.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/backgroundwork/src/main/res/values/strings.xml b/backgroundwork/src/main/res/values/strings.xml
new file mode 100644
index 000000000..497dc72be
--- /dev/null
+++ b/backgroundwork/src/main/res/values/strings.xml
@@ -0,0 +1,22 @@
+
+
+
+ download_channel
+ Downloading
+ Cancel
+ Work completed
+
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 39a34593d..e2fdd2089 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -286,6 +286,7 @@ androidx-window-core = { module = "androidx.window:window-core", version.ref = "
androidx-window-java = { module = "androidx.window:window-java", version.ref = "androidx-window-java" }
androidx-work-runtime = { module = "androidx.work:work-runtime", version.ref = "androidx-work-runtime" }
androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work-runtime" }
+androidx-work-multiprocess = { module = "androidx.work:work-multiprocess", version.ref = "androidx-work-runtime" }
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
billing = { module = "com.android.billingclient:billing-ktx", version.ref = "playbilling" }
camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camera-camera2" }