Skip to content

Migrate Persistent Background Work snippets - #1133

Open
djubinville wants to merge 6 commits into
android:mainfrom
StellarElements:bg-work-persistent
Open

djubinville wants to merge 6 commits into
android:mainfrom
StellarElements:bg-work-persistent

Conversation

@djubinville

@djubinville djubinville commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Extracts Kotlin code snippets for 8 persistent background work (WorkManager) documentation guides into :backgroundwork (backgroundwork/src/main/java/com/example/snippets/backgroundwork/) as region-tagged source code.

Pages covered:

List of modifications

  1. Custom-configuration page (CustomConfiguration.kt — android_background_custom_configuration_on_demand): Updated override fun getWorkManagerConfiguration() to override val workManagerConfiguration: Configuration get() = ... because Configuration.Provider defines workManagerConfiguration as a Kotlin property in WorkManager 2.9+.
  2. Define-work page (DefineWork.kt — android_background_enqueue_work_request, android_background_expedited_work_request, android_background_assign_input_data):
    • Replaced the uncompilable placeholder val myWorkRequest = ... in android_background_enqueue_work_request with val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>().build().
    • Stripped inline HTML formatting tags (<b>, </b>, <var>, </var>), wrapped the hidden uploadFile(uri: String) stub in UploadWork inside // [START_EXCLUDE] ... // [END_EXCLUDE] so DevSite renders the ellipsis automatically, and kept val myUploadWork in android_background_assign_input_data at top-level scope so both class UploadWork and val myUploadWork share column-0 indentation.
  3. Long-running page (LongRunningWorker.kt — android_background_long_running_foreground_service_type): Stripped inline HTML <b> / </b> tags around FOREGROUND_SERVICE_TYPE_LOCATION or FOREGROUND_SERVICE_TYPE_MICROPHONE.
  4. Observe page (ObserveWork.kt — android_background_observe_progress_worker): Removed the unused androidx.work.Data import (ProgressWorker uses workDataOf) and preserved the remaining 4 instructional imports (Context, CoroutineWorker, WorkerParameters, delay) inside the region tag as commented imports (// import ...) while placing the compiling imports at the file header.
  5. Update-work page (UpdateWork.kt — android_background_update_photo_upload_work, android_background_track_work_generation):
    • Added context: Context parameter to suspend fun updatePhotoUploadWork(context: Context) so WorkManager.getInstance(context) compiles without a file-level Context variable.
    • Changed workManager.getWorkInfoById(oldWorkRequestId) to workManager.getWorkInfoById(oldWorkRequestId).get(), updated workInfo.getGeneration() to workInfo?.generation (since getWorkInfoById() returns ListenableFuture<WorkInfo?> and generation is a Kotlin property), and updated the preceding comment from // Call getGeneration to retrieve the generation. to // Retrieve the generation..
  6. Coroutineworker page (CoroutineWorkerThreading.kt — android_background_coroutine_download_worker_with_context): Changed withContext(Dispatchers.IO) { ... return Result.success() } to return withContext(Dispatchers.IO) { ... Result.success() } because non-local return inside withContext is prohibited in Kotlin.
  7. Comment punctuation (CustomConfiguration.kt, DefineWork.kt, LongRunningWorker.kt, ManageWork.kt, ObserveWork.kt, UpdateWork.kt): Added trailing periods (.) to natural-language inline comments carried over from DAC.
  8. Supporting resources (ic_work_notification.xml, strings.xml, AndroidManifest.xml — no region tags): Added Material work notification vector icon (ic_work_notification.xml), string resources (strings.xml), and WorkManager manifest declarations (AndroidManifest.xml) required for the Kotlin snippets to compile and pass :backgroundwork:lintDebug.

Snippets not migrated

  • Java code snippets across all 8 pages cataloged for removal under Kotlin-first policy.
  • AndroidManifest.xml configuration snippets (custom-configuration.md, long-running.md, coroutineworker.md, listenableworker.md) remain inline on the DAC pages with {# disableFinding(SNIPPET_GITHUB) #}.

Verification

  • ./gradlew :backgroundwork:build - passes (0 errors)
  • ./gradlew :backgroundwork:lintDebug - passes (0 errors)
  • ./gradlew :backgroundwork:compileDebugKotlin - passes (0 errors)
  • ./gradlew :backgroundwork:spotlessApply - formatted cleanly

@google-cla

google-cla Bot commented Sep 22, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@djubinville

djubinville commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor Author

This commit is to address linter issue as a standalone. The following were resolved:

# File:line Lint ID Fix
1 CoroutineWorkerThreading.kt:87 ExampleRemoteCoroutineWorker WorkerHasAPublicModifier Remove private. The class is outside any region tag, so the docs don't change.
2 DefineWork.kt:198 SyncWorker WorkerHasAPublicModifier Same fix
3 DefineWork.kt:202 SaveImageToFileWorker WorkerHasAPublicModifier Same fix
4 ListenableWorkerThreading.kt:135 ExampleRemoteListenableWorker WorkerHasAPublicModifier Same fix
5 LongRunningWorker.kt:101 ForegroundServiceTypeSnippet WorkerHasAPublicModifier Same fix
6 ManageWork.kt:126 SendLogsWorker WorkerHasAPublicModifier Same fix
7 UpdateWork.kt:75 MyWorker WorkerHasAPublicModifier Same fix

Two lint errors are left, and they still fail build. Both happen because the Kotlin snippets rely on manifest entries that backgroundwork/src/main/AndroidManifest.xml doesn't have yet. The DAC pages show those entries as their own snippets, so I suggest adding them as region-tagged XML snippets rather than suppressing the lint checks.

It appears that these errors may be due to the snippet extraction skill leaving the AndroidManifest.xml file hardcoded. How should this be addressed?

cc: @kkuan2011 @erikrodriguez-se


Proposed Fix

1. RemoveWorkManagerInitializer (AndroidManifest.xml:22)

Error: Remove androidx.work.WorkManagerInitializer from your AndroidManifest.xml when using on-demand initialization. [RemoveWorkManagerInitializer from androidx.work]

Cause: MyApplication in CustomConfiguration.kt implements Configuration.Provider (android_background_custom_configuration_on_demand). But the manifest still lets androidx.startup run the default WorkManagerInitializer. On-demand initialization needs the default one removed.

Suggested fix: add the manifest block from the Custom WorkManager configuration page. It removes only the WorkManager initializer, so androidx.startup keeps working for other components:

<!-- [START android_background_custom_configuration_remove_initializer] -->
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <!-- If you are using androidx.startup to initialize other components -->
    <meta-data
        android:name="androidx.work.WorkManagerInitializer"
        android:value="androidx.startup"
        tools:node="remove" />
</provider>
<!-- [END android_background_custom_configuration_remove_initializer] -->

The same page also shows a variant that disables androidx.startup entirely (<provider … tools:node="remove">). If we want both variants on DAC, it could go in under a second tag, such as android_background_custom_configuration_disable_startup.

2. SpecifyForegroundServiceType (LongRunningWorker.kt:114)

Error: Missing location foregroundServiceType in the AndroidManifest.xml [SpecifyForegroundServiceType from androidx.work]

Cause: android_background_long_running_foreground_service_type passes FOREGROUND_SERVICE_TYPE_LOCATION or FOREGROUND_SERVICE_TYPE_MICROPHONE to ForegroundInfo. WorkManager's SystemForegroundService doesn't declare those types in the merged manifest.

Suggested fix: add the service declaration from the Support for long-running workers page:

<!-- [START android_background_long_running_foreground_service_type_manifest] -->
<service
    android:name="androidx.work.impl.foreground.SystemForegroundService"
    android:foregroundServiceType="location|microphone"
    tools:node="merge" />
<!-- [END android_background_long_running_foreground_service_type_manifest] -->

This alone isn't enough. With only the <service> block, lint swaps error 9 for two ForegroundServicePermission errors on the same element:

Error: foregroundServiceType:location requires permission:[android.permission.FOREGROUND_SERVICE_LOCATION] AND any permission in list:[android.permission.ACCESS_COARSE_LOCATION, android.permission.ACCESS_FINE_LOCATION] [ForegroundServicePermission]
Error: foregroundServiceType:microphone requires permission:[android.permission.FOREGROUND_SERVICE_MICROPHONE] AND any permission in list:[..., android.permission.RECORD_AUDIO] [ForegroundServicePermission]

So the manifest also needs these permissions, placed outside the region tag. Declare foreground services and request permissions covers them:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

ACCESS_COARSE_LOCATION is the minimum that satisfies the location check. ACCESS_FINE_LOCATION also works.

Notes

  • Both blocks go inside the existing <application> element. That means turning <application tools:ignore="MissingApplicationIcon" /> into an open/close pair. The tools namespace is already declared.
  • The XML spotless rule already skips <!-- [START …] --> lines when it checks license headers. spotlessXmlCheck passes with the region tags in place.
  • Tested: with all of the above applied, :backgroundwork:lintDebug, :backgroundwork:spotlessCheck (including spotlessXmlCheck), :backgroundwork:compileDebugKotlin and the full :backgroundwork:build pass, and lint reports no errors. The full manifest I tested is below.
  • The two ObsoleteSdkInt warnings (LongRunningWorker.kt:71, :89) are only warnings and don't fail the build. They sit inside the rendered long_running_coroutine_worker region, so if we want to silence them, a lint { disable += "ObsoleteSdkInt" } in backgroundwork/build.gradle.kts would do it without changing the snippet.
Tested AndroidManifest.xml body
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <application tools:ignore="MissingApplicationIcon">
        <!-- [START android_background_custom_configuration_remove_initializer] -->
        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">
            <!-- If you are using androidx.startup to initialize other components -->
            <meta-data
                android:name="androidx.work.WorkManagerInitializer"
                android:value="androidx.startup"
                tools:node="remove" />
        </provider>
        <!-- [END android_background_custom_configuration_remove_initializer] -->

        <!-- [START android_background_long_running_foreground_service_type_manifest] -->
        <service
            android:name="androidx.work.impl.foreground.SystemForegroundService"
            android:foregroundServiceType="location|microphone"
            tools:node="merge" />
        <!-- [END android_background_long_running_foreground_service_type_manifest] -->
    </application>

</manifest>

- 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.
@djubinville

Copy link
Copy Markdown
Contributor Author

Code Review Resolution Summary (30aa6be & cl/986196893)

All 8 blocking findings from the audit review have been addressed and validated against the rendered snippets, :backgroundwork:compileDebugKotlin, :backgroundwork:spotlessCheck, and :backgroundwork:lintDebug:

1. GitHub PR (android/snippets#1133, commit 30aa6be)

  1. ObserveWork.kt (android_background_observe_progress_worker):
    • Removed the duplicate [START]/[END] region tag around the import block (L19–25), deleted the unused import androidx.work.Data, sorted imports alphabetically (spotlessCheck passing), and documented the omitted file-level imports in the PR description.
  2. LongRunningWorker.kt (android_background_long_running_foreground_service_type):
    • Removed the stray // [START_EXCLUDE silent] and // [END_EXCLUDE] comments that were outside the active region tag (L107, L110).
    • Added @SuppressLint("ObsoleteSdkInt") outside [START android_background_long_running_coroutine_worker] to clear the two ObsoleteSdkInt lint warnings without altering the rendered snippet.
  3. CustomConfiguration.kt (android_background_custom_configuration_manual_init):
    • Changed private fun manualInitialization(context: Context) to an extension function private fun Context.manualInitialization() and restored WorkManager.initialize(this, myConfig) so the rendered snippet is 100% character-for-character identical to DAC.
  4. DefineWork.kt (android_background_assign_input_data):
    • Removed the private fun createUploadWork() { ... } wrapper inside [START android_background_assign_input_data] and kept val myUploadWork at top-level file scope so both class UploadWork and val myUploadWork sit at column 0 for DevSite rendering while satisfying Spotless (ktlint).
  5. UpdateWork.kt (android_background_update_photo_upload_work):
    • Restored suspend fun updatePhotoUploadWork() to match DAC verbatim by declaring @SuppressLint("StaticFieldLeak") private lateinit var context: Context at file scope outside the region tag.
  6. PR Migrate Persistent Background Work snippets #1133 Description:
    • Restored all backticked code spans stripped by shell expansion and updated List of modifications: to explicitly document all 5 required compilation/rendering adjustments.

2. Critique CL (cl/986196893)

  1. how-to/long-running.md:
    • Restored the ### Kotlin {:#long-running-kotlin} and ### Java {:#long-running-java} headings, deep-link anchor IDs, and narrative paragraphs ("You'll use a slightly different approach...", "Here is a simple example of a long running worker...", and "Developers using a ListenableWorker or a Worker can call the setForegroundAsync() API..."), while rendering only the Kotlin github.code_snippet.

Outstanding Items (Awaiting Guidance)

  1. Consolidating PR #1119 (bg-work-snippet-migration) into This Superseding PR (Migrate Persistent Background Work snippets #1133):

  2. 2 androidx.work Lint Errors in backgroundwork/src/main/AndroidManifest.xml (RemoveWorkManagerInitializer & SpecifyForegroundServiceType):

    • As detailed in PR #1133 comment, :backgroundwork:lintDebug currently reports 0 warnings and only these 2 manifest errors because AndroidManifest.xml was left untouched per the snippet migration skill.
    • A tested AndroidManifest.xml patch (adding the InitializationProvider removal block, SystemForegroundService location|microphone service declaration, and foreground service permissions) is ready to commit once confirmed whether we should include those manifest entries (or suppress the two lint checks).

cc: @kkuan2011 @erikrodriguez-se

- 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.
@hamen

hamen commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

@djubinville thanks for tracing both lint errors, the analysis is right.

On the manifest: the docs team asked us to leave manifest snippets hardcoded on the page (android-dac-snippets#18), so the page's two XML blocks stay as they are, with no region tags.

The module's own backgroundwork/src/main/AndroidManifest.xml is a separate thing. It can get whatever the build needs, outside any region: the InitializationProvider block that removes WorkManagerInitializer, the SystemForegroundService merge with location|microphone, and the permissions. That is your tested manifest without the [START]/[END] lines.

The skill wording only covered components a snippet declares, which is why it read as "leave the manifest alone". It now covers entries a library's lint check needs too (snippet-extraction 1.7.2), so a pull of the toolkit picks it up.

@djubinville
djubinville marked this pull request as ready for review September 28, 2026 19:25
@snippet-bot

snippet-bot Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Here is the summary of changes.

You are about to add 33 region tags.

This comment is generated by snippet-bot.
If you find problems with this result, please file an issue at:
https://github.com/googleapis/repo-automation-bots/issues.
To update this comment, add snippet-bot:force-run label or use the checkbox below:

  • Refresh this comment

@kkuan2011 kkuan2011 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thank you! Some comments around removing the suppression of lint errors, let me know if that's possible.

val myWorkRequest = ...
// [START_EXCLUDE silent]
*/
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>().build()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can simplify lines 40-47 and just fill in the ... even though that would be a visible diff

val myWorkRequest = OneTimeWorkRequestBuilder().build()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the [START_EXCLUDE silent] D23 Twin (// val myWorkRequest = ...) and parameter binding here to comply with our snippet migration audit rules (D5 Smallest Delta and D23 Same-Line Placeholders), which prohibit inventing an unruled visible RHS when the original DAC snippet used val myWorkRequest = ... (see audit review pass in commit 30aa6be / abf5efb6).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we should make the code change because line 43 won't be compiled. So I prefer to reduce the amount of code in comments.

@djubinville djubinville Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplified android_background_enqueue_work_request in DefineWork.kt to use val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>().build() directly (removing the [START_EXCLUDE silent] block-comment twin) in commit 270d61d1, and documented the visible diff in the PR description.

Skill review note for @hamen (android-snippets-experimental):

  • Rules impacted: D23 (Same-Line Placeholders Ordered 3-Step Test) (SKILL.md lines 150–164) and D5 (Permitted Diff Justifications) (SKILL.md lines 175–179).
  • What needs review in the skill: Under D23, same-line placeholders (= ...) must follow a strict 3-step test:
    1. Use the D23 Twin (/* val x: Type = ... */ inside [START_EXCLUDE silent] followed by a hidden compiling binding) when a type or provenance comment is present.
    2. Bind visibly in the open only if the placeholder matches the closed D23 Literal Table (currently restricted solely to File(...) -> File("photo.jpg")).
    3. Otherwise stop and ask; never invent an unruled visible RHS.
      Here, DRE review prefers replacing val myWorkRequest = ... directly in the visible snippet with val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>().build() to reduce uncompiled code inside /* ... */ comments and [START_EXCLUDE silent] blocks. D23 should be reviewed to clarify when concrete builder/initializer calls (like OneTimeWorkRequestBuilder<MyWork>().build()) are preferred as visible replacements over the /* ... */ D23 Twin pattern.

}
// [END android_background_long_running_coroutine_worker]

@SuppressLint("WorkerHasAPublicModifier")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try to remove this suppress lint annotations throughout the entire PR? Does removing "private" from the Worker classes fix the issue?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing private from the Worker subclasses does resolve the WorkerHasAPublicModifier lint check, and we originally did that in commit d53aa88. However, our snippet migration audit rule D26a requires all helper classes outside region tags that aren't cross-file dependencies to be marked private (which was enforced in commit abf5efb6). Because AndroidX WorkManager's WorkerHasAPublicModifier detector fails :backgroundwork:lintDebug when a ListenableWorker/Worker/CoroutineWorker subclass is private, @SuppressLint("WorkerHasAPublicModifier") is required alongside private to satisfy both D26a and ./gradlew :backgroundwork:lintDebug.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we should try doing this (as the private modifier is more of a preference, if it doesn't have any other side effects)

@djubinville djubinville Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed private and @SuppressLint("WorkerHasAPublicModifier") (along with the unused SuppressLint imports) from all 8 helper Worker / CoroutineWorker / ListenableWorker subclasses across CoroutineWorkerThreading.kt, DefineWork.kt, ListenableWorkerThreading.kt, LongRunningWorker.kt, ManageWork.kt, and UpdateWork.kt in commit 270d61d1. Verified that ./gradlew :backgroundwork:compileDebugKotlin :backgroundwork:lintDebug passes with 0 errors and no naming collisions across the package.

Skill review note for @hamen (android-snippets-experimental):

  • Rules impacted: D26a (Self-Contained Constants & Private Top-Level Scoping) (SKILL.md lines 273–281).
  • What needs review in the skill: Under D26a, "Mark top-level helper constants, functions, and wrapper classes private across sibling snippet files in the same package, EXCEPT Android framework components (Activity, Service, BroadcastReceiver, TileService) registered in AndroidManifest.xml, which the system instantiates by class name and cannot be private." AndroidX WorkManager includes an Android Lint detector (WorkerHasAPublicModifier) that fails :backgroundwork:lintDebug whenever a ListenableWorker (Worker, CoroutineWorker, RemoteListenableWorker, RemoteCoroutineWorker) subclass is marked private, because WorkManager's default WorkerFactory instantiates workers reflectively and requires a public class. D26a should be updated to add ListenableWorker / Worker / CoroutineWorker subclasses to the exception list alongside Activity, Service, BroadcastReceiver, and TileService so they stay public without requiring @SuppressLint("WorkerHasAPublicModifier").

import java.util.UUID

private const val PHOTO_UPLOAD_WORK_NAME = "photo_upload"
@SuppressLint("StaticFieldLeak")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having a context variable and suppressing this lint error, can we pass in context to the method in line 35 (even though it results in a visible code change)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We originally had suspend fun updatePhotoUploadWork(context: Context) in commit 13030b9, but audit rule D5 (Smallest Delta / Zero Visible Diff) required reverting the visible function signature to suspend fun updatePhotoUploadWork() and moving context to an out-of-band declaration outside the region tag (commit 30aa6be). Keeping @SuppressLint("StaticFieldLeak") private lateinit var context: Context outside the region tag preserves zero visible diff against the original DAC snippet.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the original way of having context as a parameter looks better in the code, having a Context variable feels a bit hack-y

@djubinville djubinville Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed @SuppressLint("StaticFieldLeak") private lateinit var context: Context and restored context: Context as a parameter on suspend fun updatePhotoUploadWork(context: Context) in commit 270d61d1, and recorded the signature change in the PR description.

Skill review note for @hamen (android-snippets-experimental):

  • Rules impacted: D5 (Permitted Diff Justifications / Minimize Diffs) (SKILL.md lines 175–179, 269–271) and code_review.md Rules 1 & 6 (keeping supporting variables outside region tags to preserve visible function signatures verbatim).
  • What needs review in the skill: When an original hardcoded DAC snippet includes a function signature inside the code block (suspend fun updatePhotoUploadWork()) and references an undeclared variable like context in its body (WorkManager.getInstance(context)), D5 and Rule 6 require keeping the visible function signature unchanged and declaring supporting variables outside the region tag. However, declaring private lateinit var context: Context at file scope triggers Android Lint's StaticFieldLeak check (requiring @SuppressLint("StaticFieldLeak")) and feels hacky in Kotlin compared to passing context: Context as a function parameter. D5 and Rule 6 should be updated to allow adding missing contextual parameters (such as context: Context) to a visible function signature when declaring them as file-level properties would require @SuppressLint("StaticFieldLeak") or introduce an Android anti-pattern.

// [START android_background_observe_progress_worker]
// import android.content.Context
// import androidx.work.CoroutineWorker
// import androidx.work.Data

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this import is not needed in the snippet right?

@djubinville djubinville Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed // import androidx.work.Data in commit 270d61d1 since ProgressWorker uses workDataOf(...) (androidx.work.workDataOf) and never references Data directly.

Skill review note for @hamen (android-snippets-experimental):

  • Rules impacted: D18 (Instructional Imports Inside Region Tags) (SKILL.md lines 188–199) and D5 (Permitted Diff Justifications) (SKILL.md lines 175–179).
  • What needs review in the skill: Under D18, "If an import statement appears inside the page's code block, keep it at the exact same relative position inside the region tag as a commented-out import (// import ...), while placing the real compiling import at the top of the file." In the original hardcoded DAC block on observe.md, import androidx.work.Data was present at the top of the snippet block even though Data is unused in ProgressWorker (which calls workDataOf(...)). Keeping // import androidx.work.Data verbatim under D18 / D5 preserves an unused import in the snippet. D18 and D5 should be updated to explicitly state that unused instructional imports carried over from legacy DAC blocks (such as import androidx.work.Data when workDataOf is used) should be removed rather than commented out with // import ....

// Retrieve WorkInfo instance.
val workInfo = workManager.getWorkInfoById(oldWorkRequestId).get()

// Call getGeneration to retrieve the generation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update this comment since you changed the code?

@djubinville djubinville Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the comment on line 69 to // Retrieve the generation. in commit 270d61d1 (and updated the corresponding prose in update-work.md on cl/986196893).

Skill review note for @hamen (android-snippets-experimental):

  • Rules impacted: D9 & D29 (Comment Copying, Closed Fill-in List, & Full Stops) (SKILL.md lines 165–170) and D5 (Permitted Diff Justifications) (SKILL.md lines 175–179).
  • What needs review in the skill: Under D9 / D29, comments must be copied verbatim with only two permitted adjustments: the closed fill-in list (insert your code here. -> Insert your code here) and trailing full stops (.). Because line 70 had to be updated from workInfo.getGeneration() to workInfo?.generation under D17 so the Kotlin snippet compiles against androidx.work.WorkInfo, keeping the original comment // Call getGeneration to retrieve the generation. verbatim under D9 left the comment out of sync with the updated code. D9 and D5 should be updated to add an explicit exception allowing inline comments that name a specific method/API to be updated when the adjacent code line is modified under D2 / D17 / D24.

val myWorkRequest = ...
// [START_EXCLUDE silent]
*/
val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>().build()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we should make the code change because line 43 won't be compiled. So I prefer to reduce the amount of code in comments.

}
// [END android_background_long_running_coroutine_worker]

@SuppressLint("WorkerHasAPublicModifier")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we should try doing this (as the private modifier is more of a preference, if it doesn't have any other side effects)

import java.util.UUID

private const val PHOTO_UPLOAD_WORK_NAME = "photo_upload"
@SuppressLint("StaticFieldLeak")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the original way of having context as a parameter looks better in the code, having a Context variable feels a bit hack-y

@kkuan2011 kkuan2011 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed PR descriptions too. To make them more concise for DRE reviewers, could you omit the "Files & Region Tags Added" and "Compile-driven resources" sections so they can dive immediately into the list of diffs?

@djubinville

djubinville commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Updated the PR description to omit the "Files & Region Tags Added" and "Compile-Driven Supporting Resources" sections so reviewers can jump straight to the List of modifications.

Skill review note for @hamen (android-snippets-experimental):

  • Rules impacted: D11 (Asset Naming and Origin) (SKILL.md lines 230–237), D27 (Compile-driven resources in PR description) (SKILL.md lines 201–210), and create_github_pr.md (lines 42–63).
  • What needs review in the skill: D27 states: "When a non-Kotlin resource (layout, drawable, XML) is moved into android/snippets because a tagged Kotlin region references it and will not build without it, the PR description MUST state that compile dependency and name the forcing symbol ... and list any untagged supporting resources." However, DRE reviewers (and create_github_pr.md) prefer a concise PR description that goes directly from the DAC page URLs into the List of modifications and Snippets not migrated, without separate "Files & Region Tags Added" or "Compile-Driven Supporting Resources" sections. D27 / D11 should be aligned with create_github_pr.md so that any required asset/resource provenance is folded concisely into List of modifications (or kept in the Google Doc audit report) rather than adding separate inventory sections to the GitHub PR description.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants