Skip to content

feat: Add direct background execution mode bypassing WorkManager - #406

Open
armandsLa wants to merge 3 commits into
ABausG:mainfrom
armandsLa:features/direct
Open

feat: Add direct background execution mode bypassing WorkManager#406
armandsLa wants to merge 3 commits into
ABausG:mainfrom
armandsLa:features/direct

Conversation

@armandsLa

Copy link
Copy Markdown
Contributor

Description

Adds a new direct execution mode for Android background callbacks that runs Dart code directly in the BroadcastReceiver using goAsync(), bypassing WorkManager entirely.

On some devices, WorkManager tasks are delayed between being queued and executed, and on others, they don’t run at all. I’m not sure whether this is an existing issue or something that started happening more recently. #361

HomeWidgetBackgroundIntent.getBroadcast(context, uri, direct = true)

Checklist

  • I have updated/added tests for ALL new/updated/fixed functionality.
  • I have updated/added relevant documentation and added code (documentation) comments where necessary.
  • I have updated/added relevant examples in example or documentation.

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

@docs-page

docs-page Bot commented Mar 19, 2026

Copy link
Copy Markdown

To view this pull requests documentation preview, visit the following URL:

docs.page/abausg/home_widget~406

Documentation is deployed and generated using docs.page.

@coderabbitai

coderabbitai Bot commented Mar 19, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Android background broadcasts can now run directly through a Flutter engine or continue using WorkManager. The broadcast intent API exposes a direct flag, a background runner handles engine initialization and queued callbacks, and Android compilation targets and selected dependency versions are updated.

Changes

Background execution flow

Layer / File(s) Summary
Direct receiver dispatch and Flutter background runner
packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundReceiver.kt, packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt
The receiver routes direct intents to HomeWidgetBackgroundRunner.execute(...) and retains WorkManager fallback behavior. The runner initializes the Flutter engine and method channel, queues work until background initialization, and dispatches callbacks.
Broadcast intent API
packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetIntent.kt
getBroadcast(...) accepts direct: Boolean = false and stores it in the receiver intent extra.
Android build targets and dependency versions
packages/home_widget/android/build.gradle
Java and Kotlin compilation targets are upgraded to 11, and Glance, WorkManager, and coroutines dependencies are pinned to fixed versions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
    participant Broadcast as BroadcastReceiver
    participant Runner as HomeWidgetBackgroundRunner
    participant Engine as FlutterEngine
    participant Channel as MethodChannel
    participant Main as MainThread

    Broadcast->>Runner: execute(context, intent, pendingResult)
    Runner->>Runner: Launch background coroutine
    Runner->>Engine: Initialize using dispatcher handle
    Engine->>Channel: Create "home_widget/background" channel
    Runner->>Runner: Queue or dispatch prepared work
    Runner->>Main: Post method invocation
    Main->>Channel: invokeMethod("", args, callback)
    Channel->>Runner: Finish pending result
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, uses a conventional prefix, and clearly states the direct background execution mode change.
Description check ✅ Passed The description covers the behavior change, motivation, checklist, and breaking-change status; only the Related Issues section is missing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch features/direct

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Mar 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (fa7453a) to head (8fda7f5).

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #406   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            3         3           
  Lines          115       115           
=========================================
  Hits           115       115           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt (2)

48-48: Empty method name in invokeMethod("") appears intentional but should be documented.

Using an empty string as the method name is unconventional. If this is the expected protocol for the Dart side's "home_widget/background" channel, please add a brief comment explaining this design choice for maintainability.

📝 Suggested documentation
+        // The Dart background channel expects an empty method name with [callbackHandle, data] as arguments
         mainHandler.post { channel?.invokeMethod("", args) }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt`
at line 48, The call mainHandler.post { channel?.invokeMethod("", args) } in
HomeWidgetBackgroundRunner.kt uses an empty string for the method name which is
unconventional; add a brief inline comment immediately above the
mainHandler.post (or next to channel?.invokeMethod) explaining that an empty
string is the expected protocol for the Dart-side "home_widget/background"
channel and why (e.g., used as a signal/message-only invocation), so future
maintainers understand the intentional design choice; keep the comment short and
reference the channel name "home_widget/background" and the invokeMethod call.

78-100: Engine initialization exceptions will crash the coroutine but pendingResult.finish() is still called - verify exception propagation.

The initializeFlutterEngine method throws IllegalStateException if the callback handle is missing or lookup fails. These exceptions will propagate up and be caught by the finally block, ensuring pendingResult.finish() is called. This is correct behavior.

However, if FlutterEngine(context) or executeDartCallback throws on the main thread (line 91, 98), the exception will propagate through withContext(Dispatchers.Main) and terminate the coroutine. Consider whether you want to log these errors or handle them more gracefully.

📝 Suggested error handling
   private suspend fun initializeFlutterEngine(context: Context) {
     val callbackHandle = HomeWidgetPlugin.getDispatcherHandle(context)
     if (callbackHandle == 0L) {
-      throw IllegalStateException(
-          "No callbackHandle saved. Did you call HomeWidget.registerBackgroundCallback?"
-      )
+      android.util.Log.e("HomeWidgetBackgroundRunner", 
+          "No callbackHandle saved. Did you call HomeWidget.registerBackgroundCallback?")
+      return
     }
 
     val callbackInfo =
         FlutterCallbackInformation.lookupCallbackInformation(callbackHandle)
-            ?: throw IllegalStateException("Failed to lookup callback information")
+    if (callbackInfo == null) {
+      android.util.Log.e("HomeWidgetBackgroundRunner", "Failed to lookup callback information")
+      return
+    }
 
     withContext(Dispatchers.Main) {
+      try {
         engine = FlutterEngine(context)
         // ...
+      } catch (e: Exception) {
+        android.util.Log.e("HomeWidgetBackgroundRunner", "Failed to initialize FlutterEngine", e)
+      }
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt`
around lines 78 - 100, The initializeFlutterEngine function can throw on the
main thread when constructing FlutterEngine or executing the Dart callback; wrap
the critical part inside withContext(Dispatchers.Main) in a try/catch that
catches Throwable, log the error (including the exception and context such as
the callbackHandle and callbackInfo) and handle it gracefully (e.g., leave
engine null, avoid rethrowing if you want pendingResult.finish() to run
normally, or rethrow after logging if preferred). Specifically, surround the
FlutterEngine(context) creation and
engine?.dartExecutor?.executeDartCallback(callback) calls with a catch block,
use your module logger or Android Log.e to record the exception and relevant
identifiers (callbackHandle, callbackInfo), and ensure the rest of the
coroutine/ finally block (pendingResult.finish()) behaves according to your
chosen recovery strategy.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt`:
- Around line 51-54: The current finally block calls pendingResult.finish()
before the Dart callback completes; modify HomeWidgetBackgroundRunner.kt so
pendingResult is not finished in the finally block but passed through to the
posted Runnable and finished from the MethodChannel.Result callback of
channel?.invokeMethod (or an equivalent callback) after
success/error/notImplemented, e.g., post the invokeMethod on mainHandler and
call pendingResult.finish() inside each Result override; remove the premature
finish in finally and ensure pendingResult is accessible to the invoked method
call so Dart completion confirms before finishing.
- Around line 33-40: The code has a race where concurrent execute() calls can
both see engine == null and call initializeFlutterEngine(), and channel is
written on Dispatchers.Default but read on main thread causing visibility
issues; fix by adding a dedicated init lock (e.g., private val initLock = Any())
and an ensureEngineInitialized(context) helper (called from execute()) that does
a double-checked pattern: if (engine != null) return; synchronized(initLock) {
if (engine != null) return; initializeFlutterEngine(context) } and mark shared
vars like engine and channel as `@Volatile` to ensure visibility across threads
(references: HomeWidgetBackgroundRunner, execute, initializeFlutterEngine,
engine, channel, initLock, ensureEngineInitialized).

---

Nitpick comments:
In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt`:
- Line 48: The call mainHandler.post { channel?.invokeMethod("", args) } in
HomeWidgetBackgroundRunner.kt uses an empty string for the method name which is
unconventional; add a brief inline comment immediately above the
mainHandler.post (or next to channel?.invokeMethod) explaining that an empty
string is the expected protocol for the Dart-side "home_widget/background"
channel and why (e.g., used as a signal/message-only invocation), so future
maintainers understand the intentional design choice; keep the comment short and
reference the channel name "home_widget/background" and the invokeMethod call.
- Around line 78-100: The initializeFlutterEngine function can throw on the main
thread when constructing FlutterEngine or executing the Dart callback; wrap the
critical part inside withContext(Dispatchers.Main) in a try/catch that catches
Throwable, log the error (including the exception and context such as the
callbackHandle and callbackInfo) and handle it gracefully (e.g., leave engine
null, avoid rethrowing if you want pendingResult.finish() to run normally, or
rethrow after logging if preferred). Specifically, surround the
FlutterEngine(context) creation and
engine?.dartExecutor?.executeDartCallback(callback) calls with a catch block,
use your module logger or Android Log.e to record the exception and relevant
identifiers (callbackHandle, callbackInfo), and ensure the rest of the
coroutine/ finally block (pendingResult.finish()) behaves according to your
chosen recovery strategy.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f2cb7aea-a98d-4f85-bbe8-d0fec9b8c712

📥 Commits

Reviewing files that changed from the base of the PR and between fa7453a and e8910d0.

📒 Files selected for processing (3)
  • packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundReceiver.kt
  • packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt
  • packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetIntent.kt

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt (1)

111-148: Engine lifecycle consideration.

The FlutterEngine is created once and never destroyed. While this provides faster response for subsequent broadcasts, it may consume memory if no further widget interactions occur. Consider whether adding a cleanup mechanism (e.g., destroy engine after a timeout of inactivity) would be beneficial for resource-constrained devices.

This is not a blocking concern for the current implementation—keeping the engine warm is a reasonable tradeoff for widget responsiveness.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt`
around lines 111 - 148, initializeFlutterEngine currently creates a
FlutterEngine and never releases it (engine and channel stay alive), which can
leak memory on idle devices; add a cleanup mechanism by implementing a
destroy/cleanup method (e.g., destroyEngine or shutdownEngine) that calls
engine.destroy() and clears channel and engine references, and wire it to a
timeout/inactivity scheduler (use a CoroutineScope with a delayed job or
Handler) started after engine initialization in initializeFlutterEngine and
cancelled when the engine is reused; ensure the timeout duration is configurable
and that destroyEngine is also called from any explicit lifecycle teardown
points to avoid leaks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt`:
- Around line 45-46: The code uses HomeWidgetPlugin.getHandle(context) without
validating its return value; update HomeWidgetBackgroundRunner (before building
args/listOf(...)) to check the handle returned by
HomeWidgetPlugin.getHandle(context) and if it equals 0L, log or abort the
background invocation and return early instead of constructing args and invoking
Dart; ensure you reference the handle variable (e.g., val handle =
HomeWidgetPlugin.getHandle(context)) and only proceed to create args =
listOf(handle, data) and call the dispatcher when handle != 0L.

---

Nitpick comments:
In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt`:
- Around line 111-148: initializeFlutterEngine currently creates a FlutterEngine
and never releases it (engine and channel stay alive), which can leak memory on
idle devices; add a cleanup mechanism by implementing a destroy/cleanup method
(e.g., destroyEngine or shutdownEngine) that calls engine.destroy() and clears
channel and engine references, and wire it to a timeout/inactivity scheduler
(use a CoroutineScope with a delayed job or Handler) started after engine
initialization in initializeFlutterEngine and cancelled when the engine is
reused; ensure the timeout duration is configurable and that destroyEngine is
also called from any explicit lifecycle teardown points to avoid leaks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6bd0a9f9-b91b-4b81-9c96-e5427226cb43

📥 Commits

Reviewing files that changed from the base of the PR and between e8910d0 and 8fda7f5.

📒 Files selected for processing (1)
  • packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt

Comment on lines +45 to +46
val data = intent.data?.toString() ?: ""
val args = listOf(HomeWidgetPlugin.getHandle(context), data)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing validation of getHandle() return value.

Per the relevant code snippet, HomeWidgetPlugin.getHandle(context) returns 0L if the callback was never registered or after app data is cleared. While getDispatcherHandle() is validated in initializeFlutterEngine(), getHandle() is not. Passing 0L to the Dart side will cause the callback lookup to fail silently.

Consider adding validation:

🛡️ Proposed fix
       val data = intent.data?.toString() ?: ""
+      val handle = HomeWidgetPlugin.getHandle(context)
+      if (handle == 0L) {
+        Log.w(TAG, "No callback handle registered. Ignoring background event.")
+        pendingResult.finish()
+        return@launch
+      }
-      val args = listOf(HomeWidgetPlugin.getHandle(context), data)
+      val args = listOf(handle, data)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetBackgroundRunner.kt`
around lines 45 - 46, The code uses HomeWidgetPlugin.getHandle(context) without
validating its return value; update HomeWidgetBackgroundRunner (before building
args/listOf(...)) to check the handle returned by
HomeWidgetPlugin.getHandle(context) and if it equals 0L, log or abort the
background invocation and return early instead of constructing args and invoking
Dart; ensure you reference the handle variable (e.g., val handle =
HomeWidgetPlugin.getHandle(context)) and only proceed to create args =
listOf(handle, data) and call the dispatcher when handle != 0L.

@ABausG

ABausG commented Mar 25, 2026

Copy link
Copy Markdown
Owner

Can you say a bit more on what the functionality of the change is?

Also did you check that this mode works in all scenarios of the app? E.g App is running, App is fully closed, App is in background mode.

Also could this potentially be combined with the existing work manager and then skipping the queue there and do the work directly?

Wondering if this could be combined for two reasons:

  1. Reduce Code Duplication (e.g seeing quite a bit of duplication between this and the Background Worker)
  2. Is there some conditions where we could generally say that this direct approach is better in certain scenarios and only if not met fall back to work manager. Once again for this it would be great to hear your input on what use case you have for this change.

Thanks for your continued contributions!

@armandsLa

Copy link
Copy Markdown
Contributor Author

Recently, some of my users reported slow or unresponsive widget actions.

After investigating, I found the following:

  • On certain devices, WorkManager tasks may be postponed, which prevents immediate widget execution.
  • Even when executed, WorkManager consistently introduces additional latency

In my testing, when the app is already “warm” (not the first action after a longer idle period):

  • Direct execution completes in ~300 ms
  • WorkManager execution takes ~1200 ms

I have ~50k active users in a project that relies heavily on widgets. I recently released an update using direct execution, and so far the results have been very positive. Users are reporting significantly faster and more responsive widget interactions.

Regarding your questions:

  1. Yes, I tested this across all app states: foreground, background, and inactive
  2. I agree there is some code duplication. I initially considered merging the WorkManager and direct execution paths, but that increases the risk of regressions. Keeping them separate made the changes easier to review and safer to release. This can be refactored if needed.

To be honest, I struggle to see the benefit of using WorkManager at all. Widget actions are explicitly user-initiated and should execute immediately. Relying on a system like WorkManager, which is subject to Doze mode, OEM restrictions, and scheduling delays, feels counterintuitive for this use case. Am I missing something?

@ABausG

ABausG commented Apr 9, 2026

Copy link
Copy Markdown
Owner

Thanks for the reasoning and the work on this!
All makes a lot of sense and I like that this also allows a potential quite smooth transition to shift to this.

Agree with also potentially always using this going forward but with this approach I think it leaves the option open as well!

@ABausG ABausG left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Overall looks good.

Apart from the formatting failure can you please also add a brief paragraph to the docs to indicate about this option 🙏

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/home_widget/android/build.gradle`:
- Line 55: Resolve the WorkManager compatibility mismatch by either raising the
module’s minSdkVersion from 16 to 23 and updating the compatibility
documentation, or replacing the 2.10.2 dependency with a WorkManager version
that supports API 16. Keep the selected dependency and documented minimum SDK
consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 993d05c0-2a69-4a24-ac75-4aa8b9406dc9

📥 Commits

Reviewing files that changed from the base of the PR and between 8fda7f5 and 8c8f3e9.

📒 Files selected for processing (1)
  • packages/home_widget/android/build.gradle

implementation "androidx.work:work-runtime-ktx:2.+"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.+"
implementation "androidx.glance:glance-appwidget:1.1.1"
implementation "androidx.work:work-runtime-ktx:2.10.2"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

build="packages/home_widget/android/build.gradle"
rg -n 'minSdkVersion|work-runtime-ktx|glance-appwidget' "$build"

curl -fsSL https://developer.android.com/jetpack/androidx/releases/work |
  rg -n 'minSdk|API 21|API 23|2\.10\.2'

Repository: ABausG/home_widget

Length of output: 2198


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

build="packages/home_widget/android/build.gradle"
echo "== build.gradle relevant sections =="
sed -n '1,120p' "$build"
echo

echo "== gradle dependency metadata declarations =="
rg -n 'androidTestImplementation|minSdkVersion|targetSdkVersion|compileSdkVersion|kotlin-compiler|dependencyGuard|detached|work-runtime-ktx|glance-appwidget' "$build" || true
echo

echo "== module path/package declarations =="
rg -n 'package:|sdkVersion:|androidApplication|androidLibrary|flutter|name:|version:|minSdkVersion' packages/home_widget -g 'pubspec.yaml' -g 'manifest.yaml' -g 'android/build.gradle' || true
echo

echo "== release page WorkManager 2.10.0 section =="
curl -fsSL https://developer.android.com/jetpack/androidx/releases/work | sed -n '9400,9490p'

Repository: ABausG/home_widget

Length of output: 11677


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

url="https://storage.googleapis.com/android-gradle-dsl/android-worklibrary.html?_ga=2.237260338.1106646879.1721574459-1845660164.1721574459&_gac=1.147547559.1721574459.CjwKCAjwx8O6BhBxEiwAjNjPvJj2fQ7bZg4Gp9Vq2N9Xv9N9Xv9N9Xv9N9Xv9N9Xv9N9Xv9N9&version=7.6.3&search=minSdk"
echo "== Android Gradle plugin manifest-merger rule docs? =="
for v in 7.6.3 8.1.2 8.5.0; do
  echo "-- AGP $v manifestMerger --"
  curl -fsSL "https://android-gradle-dsl.appspot.com/android-gradle-dsl/reference/android/worklibrary/options_manifestmerger/m1253656c9a962f9b69959222e5b0c7b6b6b6b6b.html?version=$v" \
    2>/dev/null | sed -n '1,220p' | rg -n -C 3 'manifestMerger|rules|minSdk|minSdkVersion|tools:' || true
done

echo
echo "== search repository for androidManifest/manifestMerger usage =="
rg -n 'androidManifest|manifest_mergers|mergeDebugManifest|mergeReleaseManifest|minSdkVersion|manifestMerger|minSdkOverride|tools:override|tools:maxSdk|minSdk' .

Repository: ABausG/home_widget

Length of output: 540


🌐 Web query:

Android WorkManager 2.10.2 minSdk baseline API 21

💡 Result:

As of the release of WorkManager version 2.10.2, the minimum SDK (minSdk) requirement has been updated from API level 21 to API level 23 [1][2][3]. This change reflects a shift in the library's base support, moving away from older Android versions (API 21 and 22) to focus on API 23 (Android 6.0 Marshmallow) and higher [1][2]. Applications integrating WorkManager 2.10.2 must have a minSdk of at least 23 in their build configuration [1][2].

Citations:


🌐 Web query:

Android WorkManager 2.9.0 minSdk 21

💡 Result:

Android WorkManager version 2.9.0 maintains a minimum SDK requirement (minSdk) of 21 [1][2]. While some AndroidX libraries have transitioned to a minimum SDK of 23 in more recent releases [3], version 2.9.0 of the WorkManager library is compatible with API level 21 and above [1][2]. WorkManager is designed to handle background tasks seamlessly across different Android versions by automatically selecting the most appropriate scheduling mechanism—such as JobScheduler for API 23+ or a combination of AlarmManager and BroadcastReceiver for API 21-22 [4].

Citations:


Resolve the WorkManager/minSdk mismatch.

packages/home_widget/android/build.gradle still declares minSdkVersion 16, and WorkManager 2.10.x minimum SDK became API 23. APIs 16–22 clients may fail manifest merging; raise this module to minSdkVersion 23 and update the compatibility docs, or use an older WorkManager baseline that supports API 16.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/home_widget/android/build.gradle` at line 55, Resolve the
WorkManager compatibility mismatch by either raising the module’s minSdkVersion
from 16 to 23 and updating the compatibility documentation, or replacing the
2.10.2 dependency with a WorkManager version that supports API 16. Keep the
selected dependency and documented minimum SDK consistent.

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.

2 participants