From 2e9bffab16bb3445e2842d148cd2902e9e9ef254 Mon Sep 17 00:00:00 2001 From: David Jubinville Date: Tue, 22 Sep 2026 20:24:07 +0000 Subject: [PATCH 1/7] Add code snippets for persistent background work --- backgroundwork/build.gradle.kts | 7 + .../CoroutineWorkerThreading.kt | 92 ++++++++ .../backgroundwork/CustomConfiguration.kt | 43 ++++ .../snippets/backgroundwork/DefineWork.kt | 204 ++++++++++++++++++ .../ListenableWorkerThreading.kt | 141 ++++++++++++ .../backgroundwork/LongRunningWorker.kt | 122 +++++++++++ .../snippets/backgroundwork/ManageWork.kt | 128 +++++++++++ .../snippets/backgroundwork/ObserveWork.kt | 79 +++++++ .../snippets/backgroundwork/UpdateWork.kt | 77 +++++++ .../res/drawable/ic_work_notification.xml | 25 +++ .../src/main/res/values/strings.xml | 22 ++ gradle/libs.versions.toml | 1 + 12 files changed, 941 insertions(+) create mode 100644 backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt create mode 100644 backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt create mode 100644 backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt create mode 100644 backgroundwork/src/main/java/com/example/snippets/backgroundwork/ListenableWorkerThreading.kt create mode 100644 backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt create mode 100644 backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt create mode 100644 backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt create mode 100644 backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt create mode 100644 backgroundwork/src/main/res/drawable/ic_work_notification.xml create mode 100644 backgroundwork/src/main/res/values/strings.xml diff --git a/backgroundwork/build.gradle.kts b/backgroundwork/build.gradle.kts index a7da5e6f3..c1cd72de0 100644 --- a/backgroundwork/build.gradle.kts +++ b/backgroundwork/build.gradle.kts @@ -28,4 +28,11 @@ 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) + implementation(libs.kotlinx.coroutines.guava) } 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..30925ed76 --- /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) {} + +private 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..34d88e5ec --- /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 manualInitialization(context: Context) { + // [START android_background_custom_configuration_manual_init] + // provide custom configuration + val myConfig = Configuration.Builder() + .setMinimumLoggingLevel(android.util.Log.INFO) + .build() + + // initialize WorkManager + WorkManager.initialize(context, 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..7b4448f63 --- /dev/null +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt @@ -0,0 +1,204 @@ +/* + * 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] + // [START_EXCLUDE silent] + /* + // [END_EXCLUDE] + val myWorkRequest = ... + // [START_EXCLUDE silent] + */ + val myWorkRequest = OneTimeWorkRequestBuilder().build() + // [END_EXCLUDE] + 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 silent] + private fun uploadFile(uri: String) {} + // [END_EXCLUDE] + // ... +} + +// Create a WorkRequest for your Worker and sending it input +private fun createUploadWork() { + val myUploadWork = OneTimeWorkRequestBuilder() + .setInputData( + workDataOf( + "IMAGE_URI" to "http://..." + ) + ) + .build() +} +// [END android_background_assign_input_data] + +internal class MyWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { + override fun doWork(): Result = Result.success() +} + +private class SyncWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { + override fun doWork(): Result = Result.success() +} + +private 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..025c75c23 --- /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() + +private 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..1023e82ff --- /dev/null +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt @@ -0,0 +1,122 @@ +/* + * 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.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 + +// [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] + +private class ForegroundServiceTypeSnippet( + context: Context, + parameters: WorkerParameters +) : CoroutineWorker(context, parameters) { + override suspend fun doWork(): Result = Result.success() + + // [START_EXCLUDE silent] + private val NOTIFICATION_ID = 1 + private lateinit var notification: Notification + // [END_EXCLUDE] + // [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..0f40e6c9c --- /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] +} + +private 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..b35926ae8 --- /dev/null +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt @@ -0,0 +1,79 @@ +/* + * 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 + +// [START android_background_observe_progress_worker] +import android.content.Context +import androidx.work.CoroutineWorker +import androidx.work.Data +import androidx.work.WorkerParameters +import kotlinx.coroutines.delay +// [END android_background_observe_progress_worker] +import androidx.work.WorkInfo +import androidx.work.WorkManager +import androidx.work.WorkRequest +import androidx.work.workDataOf +import java.util.UUID + +// [START android_background_observe_progress_worker] +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..fa8af31cf --- /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() + + // Call getGeneration to retrieve the generation. + val generation = workInfo?.generation + // [END android_background_track_work_generation] +} + +private 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..664af7567 --- /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" } From 7c9bccdd2343f8464cf15f07ca3136b849532753 Mon Sep 17 00:00:00 2001 From: David Jubinville Date: Wed, 23 Sep 2026 20:13:04 -0400 Subject: [PATCH 2/7] Make placeholder Worker classes public to fix WorkerHasAPublicModifier lint errors --- .../snippets/backgroundwork/CoroutineWorkerThreading.kt | 2 +- .../java/com/example/snippets/backgroundwork/DefineWork.kt | 4 ++-- .../snippets/backgroundwork/ListenableWorkerThreading.kt | 2 +- .../com/example/snippets/backgroundwork/LongRunningWorker.kt | 2 +- .../java/com/example/snippets/backgroundwork/ManageWork.kt | 2 +- .../java/com/example/snippets/backgroundwork/UpdateWork.kt | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt index 30925ed76..f86c8f31b 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt @@ -84,7 +84,7 @@ private fun remoteCoroutineWorkerRequest(): OneTimeWorkRequest { private fun downloadSynchronously(url: String): String = "" private fun saveData(data: String) {} -private class ExampleRemoteCoroutineWorker( +class ExampleRemoteCoroutineWorker( context: Context, parameters: WorkerParameters ) : RemoteCoroutineWorker(context, parameters) { diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt index 7b4448f63..792237de1 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt @@ -195,10 +195,10 @@ internal class MyWork(context: Context, workerParams: WorkerParameters) : Worker override fun doWork(): Result = Result.success() } -private class SyncWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +class SyncWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result = Result.success() } -private class SaveImageToFileWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +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 index 025c75c23..a3202b62c 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ListenableWorkerThreading.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ListenableWorkerThreading.kt @@ -132,7 +132,7 @@ private fun downloadAsynchronously(url: String, callback: Callback) {} private val cancelDownloadsRunnable = Runnable {} private val executor: Executor = Executors.newSingleThreadExecutor() -private class ExampleRemoteListenableWorker( +class ExampleRemoteListenableWorker( context: Context, parameters: WorkerParameters ) : RemoteListenableWorker(context, parameters) { diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt index 1023e82ff..f358fced6 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt @@ -98,7 +98,7 @@ class DownloadWorker(context: Context, parameters: WorkerParameters) : } // [END android_background_long_running_coroutine_worker] -private class ForegroundServiceTypeSnippet( +class ForegroundServiceTypeSnippet( context: Context, parameters: WorkerParameters ) : CoroutineWorker(context, parameters) { diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt index 0f40e6c9c..de31251f5 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt @@ -123,6 +123,6 @@ private fun cancelWork(workManager: WorkManager, syncWorker: WorkRequest) { // [END android_background_cancel_work] } -private class SendLogsWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +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/UpdateWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt index fa8af31cf..e20fda74d 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt @@ -72,6 +72,6 @@ private fun trackWorkGeneration(context: Context, oldWorkRequestId: UUID) { // [END android_background_track_work_generation] } -private class MyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +class MyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result = Result.success() } From 2b84b62a91d3e75a65f98748c8d656987c37a8b7 Mon Sep 17 00:00:00 2001 From: David Jubinville Date: Thu, 24 Sep 2026 17:03:55 +0000 Subject: [PATCH 3/7] Address code review feedback on persistent background work snippets - ObserveWork.kt: Remove duplicate [START android_background_observe_progress_worker] region tag around imports, delete unused androidx.work.Data import, and sort imports. - LongRunningWorker.kt: Remove stray [START_EXCLUDE]/[END_EXCLUDE] comments outside the android_background_long_running_foreground_service_type region tag, and add @SuppressLint("ObsoleteSdkInt") outside the DownloadWorker region tag. - CustomConfiguration.kt: Change manualInitialization to a Context extension function (private fun Context.manualInitialization()) so WorkManager.initialize(this, myConfig) matches the DAC snippet verbatim. - DefineWork.kt: Remove private fun createUploadWork() wrapper inside android_background_assign_input_data and keep val myUploadWork at top-level scope so both class UploadWork and val myUploadWork align at column 0 for DevSite rendering while satisfying Spotless (ktlint). - UpdateWork.kt: Restore suspend fun updatePhotoUploadWork() signature to match DAC by moving context to a file-level property with @SuppressLint("StaticFieldLeak") outside the region tag. --- .../snippets/backgroundwork/CustomConfiguration.kt | 4 ++-- .../example/snippets/backgroundwork/DefineWork.kt | 14 ++++++-------- .../snippets/backgroundwork/LongRunningWorker.kt | 5 +++-- .../example/snippets/backgroundwork/ObserveWork.kt | 7 ++----- .../example/snippets/backgroundwork/UpdateWork.kt | 5 ++++- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt index 34d88e5ec..64717d3a6 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt @@ -30,7 +30,7 @@ class MyApplication() : Application(), Configuration.Provider { } // [END android_background_custom_configuration_on_demand] -private fun manualInitialization(context: Context) { +private fun Context.manualInitialization() { // [START android_background_custom_configuration_manual_init] // provide custom configuration val myConfig = Configuration.Builder() @@ -38,6 +38,6 @@ private fun manualInitialization(context: Context) { .build() // initialize WorkManager - WorkManager.initialize(context, myConfig) + 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 index 792237de1..b717c3ff5 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt @@ -180,15 +180,13 @@ class UploadWork(appContext: Context, workerParams: WorkerParameters) : } // Create a WorkRequest for your Worker and sending it input -private fun createUploadWork() { - val myUploadWork = OneTimeWorkRequestBuilder() - .setInputData( - workDataOf( - "IMAGE_URI" to "http://..." - ) +val myUploadWork = OneTimeWorkRequestBuilder() + .setInputData( + workDataOf( + "IMAGE_URI" to "http://..." ) - .build() -} + ) + .build() // [END android_background_assign_input_data] internal class MyWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt index f358fced6..d241e3033 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt @@ -16,6 +16,7 @@ package com.example.snippets.backgroundwork +import android.annotation.SuppressLint import android.app.Notification import android.app.NotificationManager import android.content.Context @@ -29,6 +30,7 @@ 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) { @@ -104,10 +106,9 @@ class ForegroundServiceTypeSnippet( ) : CoroutineWorker(context, parameters) { override suspend fun doWork(): Result = Result.success() - // [START_EXCLUDE silent] private val NOTIFICATION_ID = 1 private lateinit var notification: Notification - // [END_EXCLUDE] + // [START android_background_long_running_foreground_service_type] private fun createForegroundInfo(progress: String): ForegroundInfo { // ... diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt index b35926ae8..b455c6656 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt @@ -16,18 +16,15 @@ package com.example.snippets.backgroundwork -// [START android_background_observe_progress_worker] import android.content.Context import androidx.work.CoroutineWorker -import androidx.work.Data -import androidx.work.WorkerParameters -import kotlinx.coroutines.delay -// [END android_background_observe_progress_worker] 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] class ProgressWorker(context: Context, parameters: WorkerParameters) : diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt index e20fda74d..f16ea13cc 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt @@ -16,6 +16,7 @@ package com.example.snippets.backgroundwork +import android.annotation.SuppressLint import android.content.Context import androidx.concurrent.futures.await import androidx.work.Constraints @@ -27,9 +28,11 @@ import androidx.work.WorkerParameters import java.util.UUID private const val PHOTO_UPLOAD_WORK_NAME = "photo_upload" +@SuppressLint("StaticFieldLeak") +private lateinit var context: Context // [START android_background_update_photo_upload_work] -suspend fun updatePhotoUploadWork(context: Context) { +suspend fun updatePhotoUploadWork() { // Get instance of WorkManager. val workManager = WorkManager.getInstance(context) From 7352051e8951e628264e6709a4b5f94a868315e0 Mon Sep 17 00:00:00 2001 From: David Jubinville Date: Fri, 25 Sep 2026 19:00:11 +0000 Subject: [PATCH 4/7] Address D18, D22, D26a, and D29 quality gate findings - ObserveWork.kt: Preserve the 5 instructional import statements inside android_background_observe_progress_worker as commented imports (// import ...) per D18. - CoroutineWorkerThreading.kt, DefineWork.kt, ListenableWorkerThreading.kt, LongRunningWorker.kt, ManageWork.kt, UpdateWork.kt: Mark top-level helper classes outside region tags private with @SuppressLint("WorkerHasAPublicModifier") per D26a. - DefineWork.kt: Use plain // [START_EXCLUDE] around uploadFile stub in UploadWork instead of silent exclude + hand-written // ... per D22. - CustomConfiguration.kt, DefineWork.kt, LongRunningWorker.kt, ManageWork.kt, ObserveWork.kt: Ensure all natural-language comments end with a full stop (.) per D29. --- .../CoroutineWorkerThreading.kt | 4 ++- .../backgroundwork/CustomConfiguration.kt | 4 +-- .../snippets/backgroundwork/DefineWork.kt | 25 +++++++++++-------- .../ListenableWorkerThreading.kt | 4 ++- .../backgroundwork/LongRunningWorker.kt | 17 +++++++------ .../snippets/backgroundwork/ManageWork.kt | 20 ++++++++------- .../snippets/backgroundwork/ObserveWork.kt | 10 ++++++-- .../snippets/backgroundwork/UpdateWork.kt | 3 ++- 8 files changed, 52 insertions(+), 35 deletions(-) diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt index f86c8f31b..b79871faf 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt @@ -16,6 +16,7 @@ package com.example.snippets.backgroundwork +import android.annotation.SuppressLint import android.content.ComponentName import android.content.Context import androidx.work.CoroutineWorker @@ -84,7 +85,8 @@ private fun remoteCoroutineWorkerRequest(): OneTimeWorkRequest { private fun downloadSynchronously(url: String): String = "" private fun saveData(data: String) {} -class ExampleRemoteCoroutineWorker( +@SuppressLint("WorkerHasAPublicModifier") +private class ExampleRemoteCoroutineWorker( context: Context, parameters: WorkerParameters ) : RemoteCoroutineWorker(context, parameters) { diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt index 64717d3a6..ff9d0d896 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CustomConfiguration.kt @@ -32,12 +32,12 @@ class MyApplication() : Application(), Configuration.Provider { private fun Context.manualInitialization() { // [START android_background_custom_configuration_manual_init] - // provide custom configuration + // provide custom configuration. val myConfig = Configuration.Builder() .setMinimumLoggingLevel(android.util.Log.INFO) .build() - // initialize WorkManager + // 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 index b717c3ff5..a3db7e798 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt @@ -16,6 +16,7 @@ package com.example.snippets.backgroundwork +import android.annotation.SuppressLint import android.app.Notification import android.content.Context import androidx.work.BackoffPolicy @@ -58,7 +59,7 @@ private fun scheduleOneTimeWorkBuilder() { // [START android_background_schedule_one_time_work_builder] val uploadWorkRequest: WorkRequest = OneTimeWorkRequestBuilder() - // Additional configuration + // Additional configuration. .build() // [END android_background_schedule_one_time_work_builder] } @@ -104,7 +105,7 @@ private fun schedulePeriodicWork() { // [START android_background_periodic_work_request] val saveRequest = PeriodicWorkRequestBuilder(1, TimeUnit.HOURS) - // Additional configuration + // Additional configuration. .build() // [END android_background_periodic_work_request] } @@ -112,9 +113,9 @@ private fun schedulePeriodicWork() { private fun schedulePeriodicWorkFlex() { // [START android_background_periodic_work_request_flex] val myUploadWork = PeriodicWorkRequestBuilder( - 1, TimeUnit.HOURS, // repeatInterval (the period cycle) + 1, TimeUnit.HOURS, // repeatInterval (the period cycle). 15, TimeUnit.MINUTES - ) // flexInterval + ) // flexInterval. .build() // [END android_background_periodic_work_request_flex] } @@ -162,7 +163,7 @@ private fun tagWork() { } // [START android_background_assign_input_data] -// Define the Worker requiring input +// Define the Worker requiring input. class UploadWork(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) { @@ -173,13 +174,12 @@ class UploadWork(appContext: Context, workerParams: WorkerParameters) : uploadFile(imageUriInput) return Result.success() } - // [START_EXCLUDE silent] + // [START_EXCLUDE] private fun uploadFile(uri: String) {} // [END_EXCLUDE] - // ... } -// Create a WorkRequest for your Worker and sending it input +// Create a WorkRequest for your Worker and sending it input. val myUploadWork = OneTimeWorkRequestBuilder() .setInputData( workDataOf( @@ -189,14 +189,17 @@ val myUploadWork = OneTimeWorkRequestBuilder() .build() // [END android_background_assign_input_data] -internal class MyWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +@SuppressLint("WorkerHasAPublicModifier") +private class MyWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result = Result.success() } -class SyncWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +@SuppressLint("WorkerHasAPublicModifier") +private class SyncWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result = Result.success() } -class SaveImageToFileWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +@SuppressLint("WorkerHasAPublicModifier") +private 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 index a3202b62c..1fb5873b1 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ListenableWorkerThreading.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ListenableWorkerThreading.kt @@ -16,6 +16,7 @@ package com.example.snippets.backgroundwork +import android.annotation.SuppressLint import android.content.ComponentName import android.content.Context import androidx.concurrent.futures.CallbackToFutureAdapter @@ -132,7 +133,8 @@ private fun downloadAsynchronously(url: String, callback: Callback) {} private val cancelDownloadsRunnable = Runnable {} private val executor: Executor = Executors.newSingleThreadExecutor() -class ExampleRemoteListenableWorker( +@SuppressLint("WorkerHasAPublicModifier") +private class ExampleRemoteListenableWorker( context: Context, parameters: WorkerParameters ) : RemoteListenableWorker(context, parameters) { diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt index d241e3033..48c009cd7 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt @@ -47,7 +47,7 @@ class DownloadWorker(context: Context, parameters: WorkerParameters) : ?: return Result.failure() val outputFile = inputData.getString(KEY_OUTPUT_FILE_NAME) ?: return Result.failure() - // Mark the Worker as important + // Mark the Worker as important. val progress = "Starting Download" setForeground(createForegroundInfo(progress)) download(inputUrl, outputFile) @@ -55,9 +55,9 @@ class DownloadWorker(context: Context, parameters: WorkerParameters) : } private fun download(inputUrl: String, outputFile: String) { - // Downloads a file and updates bytes read + // Downloads a file and updates bytes read. // Calls setForeground() periodically when it needs to update - // the ongoing Notification + // the ongoing Notification. } // Creates an instance of ForegroundInfo which can be used to update the // ongoing notification. @@ -65,11 +65,11 @@ class DownloadWorker(context: Context, parameters: WorkerParameters) : 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 + // This PendingIntent can be used to cancel the worker. val intent = WorkManager.getInstance(applicationContext) .createCancelPendingIntent(getId()) - // Create a Notification channel if necessary + // Create a Notification channel if necessary. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createChannel() } @@ -81,7 +81,7 @@ class DownloadWorker(context: Context, parameters: WorkerParameters) : .setSmallIcon(R.drawable.ic_work_notification) .setOngoing(true) // Add the cancel action to the notification which can - // be used to cancel the worker + // be used to cancel the worker. .addAction(android.R.drawable.ic_delete, cancel, intent) .build() @@ -90,7 +90,7 @@ class DownloadWorker(context: Context, parameters: WorkerParameters) : @RequiresApi(Build.VERSION_CODES.O) private fun createChannel() { - // Create a Notification channel + // Create a Notification channel. } companion object { @@ -100,7 +100,8 @@ class DownloadWorker(context: Context, parameters: WorkerParameters) : } // [END android_background_long_running_coroutine_worker] -class ForegroundServiceTypeSnippet( +@SuppressLint("WorkerHasAPublicModifier") +private class ForegroundServiceTypeSnippet( context: Context, parameters: WorkerParameters ) : CoroutineWorker(context, parameters) { diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt index de31251f5..4b9ac11e6 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt @@ -16,6 +16,7 @@ package com.example.snippets.backgroundwork +import android.annotation.SuppressLint import android.content.Context import android.view.View import androidx.work.Constraints @@ -37,10 +38,10 @@ private fun manageWorkEnqueue(requireContext: () -> Context) { // [START_EXCLUDE silent] /* // [END_EXCLUDE] - val myWork: WorkRequest = // ... OneTime or PeriodicWork + val myWork: WorkRequest = // ... OneTime or PeriodicWork. // [START_EXCLUDE silent] */ - val myWork: WorkRequest = OneTimeWorkRequestBuilder().build() + val myWork: WorkRequest = OneTimeWorkRequestBuilder().build() // [END_EXCLUDE] WorkManager.getInstance(requireContext()).enqueue(myWork) // [END android_background_manage_work_enqueue] @@ -66,13 +67,13 @@ private fun Context.uniquePeriodicWork() { private fun observeWorkQuery(workManager: WorkManager, syncWorker: WorkRequest) { // [START android_background_observe_work_query] - // by id + // by id. workManager.getWorkInfoById(syncWorker.id) // ListenableFuture - // by name + // by name. workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture> - // by tag + // by tag. workManager.getWorkInfosByTag("syncTag") // ListenableFuture> // [END android_background_observe_work_query] } @@ -112,17 +113,18 @@ private fun complexWorkQueries(workManager: WorkManager) { private fun cancelWork(workManager: WorkManager, syncWorker: WorkRequest) { // [START android_background_cancel_work] - // by id + // by id. workManager.cancelWorkById(syncWorker.id) - // by name + // by name. workManager.cancelUniqueWork("sync") - // by tag + // by tag. workManager.cancelAllWorkByTag("syncTag") // [END android_background_cancel_work] } -class SendLogsWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +@SuppressLint("WorkerHasAPublicModifier") +private 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 index b455c6656..197a34e97 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt @@ -27,6 +27,12 @@ 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.Data +// import androidx.work.WorkerParameters +// import kotlinx.coroutines.delay + class ProgressWorker(context: Context, parameters: WorkerParameters) : CoroutineWorker(context, parameters) { @@ -49,13 +55,13 @@ class ProgressWorker(context: Context, parameters: WorkerParameters) : private suspend fun observeProgressFlow(applicationContext: Context, requestId: UUID) { // [START android_background_observe_progress_flow] WorkManager.getInstance(applicationContext) - // requestId is the WorkRequest id + // 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 + // Do something with progress information. } } // [END android_background_observe_progress_flow] diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt index f16ea13cc..a828a98fd 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt @@ -75,6 +75,7 @@ private fun trackWorkGeneration(context: Context, oldWorkRequestId: UUID) { // [END android_background_track_work_generation] } -class MyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +@SuppressLint("WorkerHasAPublicModifier") +private class MyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result = Result.success() } From 5befa88506abd7fb8884e0c20aa7178fea3bb49e Mon Sep 17 00:00:00 2001 From: David Jubinville Date: Mon, 28 Sep 2026 19:18:18 +0000 Subject: [PATCH 5/7] Update backgroundwork AndroidManifest.xml to satisfy WorkManager lint checks --- backgroundwork/src/main/AndroidManifest.xml | 23 ++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) 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"> + + + + + - + + + + + + + From 2ef0e721d545320de5fcd55b4a3e05301a0026a5 Mon Sep 17 00:00:00 2001 From: David Jubinville Date: Fri, 2 Oct 2026 16:28:25 +0000 Subject: [PATCH 6/7] Address review feedback on persistent background work snippets --- .../backgroundwork/CoroutineWorkerThreading.kt | 4 +--- .../snippets/backgroundwork/DefineWork.kt | 17 +++-------------- .../backgroundwork/ListenableWorkerThreading.kt | 4 +--- .../backgroundwork/LongRunningWorker.kt | 3 +-- .../snippets/backgroundwork/ManageWork.kt | 4 +--- .../snippets/backgroundwork/ObserveWork.kt | 1 - .../snippets/backgroundwork/UpdateWork.kt | 10 +++------- 7 files changed, 10 insertions(+), 33 deletions(-) diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt index b79871faf..f86c8f31b 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/CoroutineWorkerThreading.kt @@ -16,7 +16,6 @@ package com.example.snippets.backgroundwork -import android.annotation.SuppressLint import android.content.ComponentName import android.content.Context import androidx.work.CoroutineWorker @@ -85,8 +84,7 @@ private fun remoteCoroutineWorkerRequest(): OneTimeWorkRequest { private fun downloadSynchronously(url: String): String = "" private fun saveData(data: String) {} -@SuppressLint("WorkerHasAPublicModifier") -private class ExampleRemoteCoroutineWorker( +class ExampleRemoteCoroutineWorker( context: Context, parameters: WorkerParameters ) : RemoteCoroutineWorker(context, parameters) { diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt index a3db7e798..f8564cfc7 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/DefineWork.kt @@ -16,7 +16,6 @@ package com.example.snippets.backgroundwork -import android.annotation.SuppressLint import android.app.Notification import android.content.Context import androidx.work.BackoffPolicy @@ -37,14 +36,7 @@ import java.util.concurrent.TimeUnit private fun enqueueWorkSample(myContext: Context) { // [START android_background_enqueue_work_request] - // [START_EXCLUDE silent] - /* - // [END_EXCLUDE] - val myWorkRequest = ... - // [START_EXCLUDE silent] - */ val myWorkRequest = OneTimeWorkRequestBuilder().build() - // [END_EXCLUDE] WorkManager.getInstance(myContext).enqueue(myWorkRequest) // [END android_background_enqueue_work_request] } @@ -189,17 +181,14 @@ val myUploadWork = OneTimeWorkRequestBuilder() .build() // [END android_background_assign_input_data] -@SuppressLint("WorkerHasAPublicModifier") -private class MyWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +class MyWork(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result = Result.success() } -@SuppressLint("WorkerHasAPublicModifier") -private class SyncWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +class SyncWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result = Result.success() } -@SuppressLint("WorkerHasAPublicModifier") -private class SaveImageToFileWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +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 index 1fb5873b1..a3202b62c 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ListenableWorkerThreading.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ListenableWorkerThreading.kt @@ -16,7 +16,6 @@ package com.example.snippets.backgroundwork -import android.annotation.SuppressLint import android.content.ComponentName import android.content.Context import androidx.concurrent.futures.CallbackToFutureAdapter @@ -133,8 +132,7 @@ private fun downloadAsynchronously(url: String, callback: Callback) {} private val cancelDownloadsRunnable = Runnable {} private val executor: Executor = Executors.newSingleThreadExecutor() -@SuppressLint("WorkerHasAPublicModifier") -private class ExampleRemoteListenableWorker( +class ExampleRemoteListenableWorker( context: Context, parameters: WorkerParameters ) : RemoteListenableWorker(context, parameters) { diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt index 48c009cd7..78677a9cd 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/LongRunningWorker.kt @@ -100,8 +100,7 @@ class DownloadWorker(context: Context, parameters: WorkerParameters) : } // [END android_background_long_running_coroutine_worker] -@SuppressLint("WorkerHasAPublicModifier") -private class ForegroundServiceTypeSnippet( +class ForegroundServiceTypeSnippet( context: Context, parameters: WorkerParameters ) : CoroutineWorker(context, parameters) { diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt index 4b9ac11e6..a106bed20 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ManageWork.kt @@ -16,7 +16,6 @@ package com.example.snippets.backgroundwork -import android.annotation.SuppressLint import android.content.Context import android.view.View import androidx.work.Constraints @@ -124,7 +123,6 @@ private fun cancelWork(workManager: WorkManager, syncWorker: WorkRequest) { // [END android_background_cancel_work] } -@SuppressLint("WorkerHasAPublicModifier") -private class SendLogsWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +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 index 197a34e97..dbc2748d6 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/ObserveWork.kt @@ -29,7 +29,6 @@ import kotlinx.coroutines.delay // [START android_background_observe_progress_worker] // import android.content.Context // import androidx.work.CoroutineWorker -// import androidx.work.Data // import androidx.work.WorkerParameters // import kotlinx.coroutines.delay diff --git a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt index a828a98fd..265163e5f 100644 --- a/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt +++ b/backgroundwork/src/main/java/com/example/snippets/backgroundwork/UpdateWork.kt @@ -16,7 +16,6 @@ package com.example.snippets.backgroundwork -import android.annotation.SuppressLint import android.content.Context import androidx.concurrent.futures.await import androidx.work.Constraints @@ -28,11 +27,9 @@ import androidx.work.WorkerParameters import java.util.UUID private const val PHOTO_UPLOAD_WORK_NAME = "photo_upload" -@SuppressLint("StaticFieldLeak") -private lateinit var context: Context // [START android_background_update_photo_upload_work] -suspend fun updatePhotoUploadWork() { +suspend fun updatePhotoUploadWork(context: Context) { // Get instance of WorkManager. val workManager = WorkManager.getInstance(context) @@ -70,12 +67,11 @@ private fun trackWorkGeneration(context: Context, oldWorkRequestId: UUID) { // Retrieve WorkInfo instance. val workInfo = workManager.getWorkInfoById(oldWorkRequestId).get() - // Call getGeneration to retrieve the generation. + // Retrieve the generation. val generation = workInfo?.generation // [END android_background_track_work_generation] } -@SuppressLint("WorkerHasAPublicModifier") -private class MyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { +class MyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { override fun doWork(): Result = Result.success() } From 89bf60f15614a0c3aac979eac1182abce0e371a8 Mon Sep 17 00:00:00 2001 From: David Jubinville Date: Tue, 6 Oct 2026 18:09:39 +0000 Subject: [PATCH 7/7] Replace placeholder notification vector icon and remove unused dependency --- backgroundwork/build.gradle.kts | 1 - backgroundwork/src/main/res/drawable/ic_work_notification.xml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/backgroundwork/build.gradle.kts b/backgroundwork/build.gradle.kts index c1cd72de0..0f212b4ea 100644 --- a/backgroundwork/build.gradle.kts +++ b/backgroundwork/build.gradle.kts @@ -34,5 +34,4 @@ dependencies { implementation(libs.androidx.concurrent.futures.ktx) implementation(libs.google.android.material) implementation(libs.guava.android) - implementation(libs.kotlinx.coroutines.guava) } diff --git a/backgroundwork/src/main/res/drawable/ic_work_notification.xml b/backgroundwork/src/main/res/drawable/ic_work_notification.xml index 664af7567..9d6286b2f 100644 --- a/backgroundwork/src/main/res/drawable/ic_work_notification.xml +++ b/backgroundwork/src/main/res/drawable/ic_work_notification.xml @@ -21,5 +21,5 @@ android:viewportHeight="24"> + android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z" />