feat: Configurable widgets for Android#396
Conversation
|
To view this pull requests documentation preview, visit the following URL: docs.page/abausg/home_widget~396 Documentation is deployed and generated using docs.page. |
WalkthroughAdds Android widget-configuration support: new Dart APIs to detect configure-time launches and finish configuration, Android plugin handlers for APPWIDGET_CONFIGURE intents, updated (duplicated) documentation, and tests exercising the configure pathway. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Flutter App
participant Plugin as Android Plugin
participant Activity as Android Activity
participant System as Android Home Launcher
Note over System,Activity: Launcher starts Activity for widget configuration
System->>Activity: start (action=APPWIDGET_CONFIGURE, EXTRA_APPWIDGET_ID)
Activity->>Plugin: activity attached / intent available
App->>Plugin: invokeMethod("initiallyLaunchedFromHomeWidgetConfigure")
Plugin->>Activity: read intent.action and EXTRA_APPWIDGET_ID
Plugin-->>App: return widgetId (string) or null
App->>Plugin: invokeMethod("finishHomeWidgetConfigure")
Plugin->>Activity: setResult(RESULT_OK, EXTRA_APPWIDGET_ID) and finish()
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetPlugin.kt`:
- Around line 139-147: The handler for
"initiallyLaunchedFromHomeWidgetConfigure" in HomeWidgetPlugin returns "-1" when
the EXTRA_APPWIDGET_ID is missing because it converts the int directly to
string; change the logic to read the int via activity?.intent?.getIntExtra(...),
check if it equals AppWidgetManager.INVALID_APPWIDGET_ID and call
result.success(null) in that case, otherwise call result.success(id.toString())
so Dart receives null for invalid/missing widget IDs.
🧹 Nitpick comments (1)
docs/features/configurable-widgets.mdx (1)
440-462: Add documentation aboutandroid:exportedattribute and configure activity return requirements.The current snippet omits two critical implementation details. On Android 12+, the configure activity must declare
android:exported="true"(otherwise the app cannot be installed). Additionally, the configure activity must returnRESULT_OKwithEXTRA_APPWIDGET_IDfor the widget to actually be added; without this, the widget won't appear even if configuration completes successfully.📌 Suggested doc addition
<intent-filter> <action android:name="android.intent.action.APPWIDGET_CONFIGURE"/> </intent-filter> +> **Note:** On Android 12+ the configure activity must include `android:exported="true"`. +> The activity must also return `RESULT_OK` with an Intent containing `EXTRA_APPWIDGET_ID` +> set to the widget ID received from the launch intent; without this, the widget won't be added.
packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetPlugin.kt
Show resolved
Hide resolved
|
Thanks for this! Can you maybe add some more links to official Android Documentation to home_widget's docs? Will try to find some time to review this proper by end of week |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #396 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 3 3
Lines 115 117 +2
=========================================
+ Hits 115 117 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@ABausG I've added a link to the Android docs page on this feature. I also made a minor change to the code based on the CodeRabbit suggestion in this thread. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetPlugin.kt`:
- Around line 151-160: The finishHomeWidgetConfigure branch should validate the
AppWidget ID and default the activity result to RESULT_CANCELED early; read the
ID from activity!!.intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID), if the ID is INVALID_APPWIDGET_ID then
call activity!!.setResult(Activity.RESULT_CANCELED) (or leave the default) and
finish, otherwise set activity!!.setResult(Activity.RESULT_OK,
Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)) and finish;
update the finishHomeWidgetConfigure flow to perform this check before returning
result.success(null).
| "finishHomeWidgetConfigure" -> { | ||
| if ( | ||
| activity != null | ||
| && activity!!.intent?.action?.equals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) == | ||
| true | ||
| ) { | ||
| activity!!.setResult(Activity.RESULT_OK, Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, activity!!.intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID))); | ||
| activity!!.finish() | ||
| } | ||
| return result.success(null) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Android AppWidget configuration activity RESULT_OK EXTRA_APPWIDGET_ID requirements
💡 Result:
For an AppWidget configuration Activity (the one launched via ACTION_APPWIDGET_CONFIGURE / android:configure=...), the host/system expects:
- You must always return a result to the caller (the widget host). (developer.android.com)
- The result must include the same AppWidget ID you were launched with, in an intent extra named
AppWidgetManager.EXTRA_APPWIDGET_ID. (developer.android.com) - To actually add the widget, you must call:
setResult(Activity.RESULT_OK, Intent().putExtra(EXTRA_APPWIDGET_ID, appWidgetId))- then
finish()(developer.android.com)
- Best practice: set a default result of
RESULT_CANCELEDearly (with the extra), so if the user backs out, the host knows to cancel and not add the widget. (developer.android.com) - When a widget has a configuration activity,
onUpdate()is not called for the initial add; your config activity should perform the initialAppWidgetManager.updateAppWidget(...)before returningRESULT_OK. (developer.android.com)
Citations:
- 1: https://developer.android.com/develop/ui/views/appwidgets/configuration?utm_source=openai
- 2: https://developer.android.com/develop/ui/views/appwidgets/configuration?utm_source=openai
- 3: https://developer.android.com/develop/ui/views/appwidgets/configuration?utm_source=openai
- 4: https://developer.android.com/develop/ui/views/appwidgets/configuration?utm_source=openai
- 5: https://developer.android.com/develop/ui/views/appwidgets/configuration?utm_source=openai
🌐 Web query:
Android AppWidget host behavior INVALID_APPWIDGET_ID configuration activity
💡 Result:
AppWidgetManager.INVALID_APPWIDGET_ID (-1) in a widget configuration Activity almost always means the host did not pass a real widget ID in Intent extra AppWidgetManager.EXTRA_APPWIDGET_ID (or you didn’t read it from the same launching intent). Per the platform contract, the config activity is started with ACTION_APPWIDGET_CONFIGURE and must receive EXTRA_APPWIDGET_ID identifying the instance being configured. (developer.android.com)
Correct host flow (what the host must do)
- Allocate an ID:
val appWidgetId = appWidgetHost.allocateAppWidgetId(). (developer.android.com) - Bind the provider to that ID (often via
ACTION_APPWIDGET_BIND, orbindAppWidgetIdIfAllowed(...)when permitted). (developer.android.com) - If the provider has a config activity, start it for that same ID, e.g.:
AppWidgetHost.startAppWidgetConfigureActivityForResult(activity, appWidgetId, ...)(recommended; also handles cross-profile). (developer.android.com)
If the host skips (1) or doesn’t pass the ID through, the config activity will read INVALID_APPWIDGET_ID.
Correct configuration Activity behavior (what your config Activity must do)
- Read the ID from the launch intent extras, defaulting to
INVALID_APPWIDGET_ID. (developer.android.com) - If it’s invalid, finish (usually
RESULT_CANCELED), because you can’t configure/update without a real ID. - When the user completes configuration, return
RESULT_OKand include the sameEXTRA_APPWIDGET_IDin the result intent; otherwise many hosts will treat it as failure and won’t add the widget. (developer.android.com)
Minimal pattern:
class WidgetConfigActivity : Activity() {
private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
appWidgetId = intent?.getIntExtra(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID
) ?: AppWidgetManager.INVALID_APPWIDGET_ID
// Default to canceled so Back = not added
setResult(RESULT_CANCELED, Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId))
if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
finish()
return
}
// ... UI ...
}
private fun done() {
// ... update widget via AppWidgetManager.updateAppWidget(appWidgetId, views) ...
setResult(RESULT_OK, Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId))
finish()
}
}Common causes of INVALID_APPWIDGET_ID
- Host launches your config activity with a plain intent (e.g.,
startActivity(...)) and forgetsputExtra(EXTRA_APPWIDGET_ID, id). - Host launches config before allocating/binding an ID.
- Config activity reads from the wrong intent (e.g., uses
getIntent()after being relaunched without propagating extras).
If you share your host code that launches the config activity (and your manifest/provider XML android:configure=...), I can point out exactly where the ID gets lost.
Citations:
- 1: https://developer.android.com/reference/android/appwidget/AppWidgetManager?utm_source=openai
- 2: https://developer.android.com/develop/ui/views/appwidgets/host?utm_source=openai
- 3: https://developer.android.com/reference/android/appwidget/AppWidgetManager?utm_source=openai
- 4: https://developer.android.com/reference/android/appwidget/AppWidgetHost?utm_source=openai
- 5: https://developer.android.com/develop/ui/views/appwidgets/configuration?utm_source=openai
- 6: https://developer.android.com/develop/ui/views/appwidgets/configuration?utm_source=openai
Validate widget ID before returning RESULT_OK.
The Android AppWidget configuration activity contract requires returning RESULT_OK only with a valid widget ID. If getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID) returns -1, the activity must return RESULT_CANCELED instead. Hosts treat RESULT_OK with an invalid ID as a configuration failure and will not add the widget. Additionally, per best practices, set a default RESULT_CANCELED result early to ensure the widget isn't added if the user backs out.
🔧 Suggested hardening
"finishHomeWidgetConfigure" -> {
- if (
- activity != null
- && activity!!.intent?.action?.equals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) ==
- true
- ) {
- activity!!.setResult(Activity.RESULT_OK, Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, activity!!.intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID)));
- activity!!.finish()
- }
- return result.success(null)
+ val act = activity
+ if (
+ act?.intent?.action?.equals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) ==
+ true
+ ) {
+ val widgetId =
+ act.intent?.getIntExtra(
+ AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID,
+ )
+ if (widgetId != null && widgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+ act.setResult(
+ Activity.RESULT_OK,
+ Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId),
+ )
+ act.finish()
+ } else {
+ act.setResult(Activity.RESULT_CANCELED)
+ }
+ }
+ return result.success(null)
}🤖 Prompt for AI Agents
In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetPlugin.kt`
around lines 151 - 160, The finishHomeWidgetConfigure branch should validate the
AppWidget ID and default the activity result to RESULT_CANCELED early; read the
ID from activity!!.intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID), if the ID is INVALID_APPWIDGET_ID then
call activity!!.setResult(Activity.RESULT_CANCELED) (or leave the default) and
finish, otherwise set activity!!.setResult(Activity.RESULT_OK,
Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)) and finish;
update the finishHomeWidgetConfigure flow to perform this check before returning
result.success(null).
|
@ABausG just checking in on this PR. If you need any more info, let me know! |
|
Hi sorry. Super busy both with work and a new (really big, really awesome) home_widget feature! To understand the full flow better would you be able to add the android implementation and the suggested usage of these new apis to the configurable_widget? This would help me (but I guess also all users of this) to directly see how they can use it to add configuration to their widgets! |
|
Hey @ABausG, unfortunately I'm swamped and I don't know if or when I would get to that since it looks like that project doesn't have an Android widget implementation at all yet. However, I'd be happy to answer questions and/or expand on the documentation if that would be helpful in understanding how to use this feature. We've implemented it in our own app and it seems to work pretty well. |
Description
Android supports "reconfigurable" widgets via a Settings item in the widget menu (opened via long-press on the widget), and also on initial widget creation. This PR adds a method,
initiallyLaunchedFromHomeWidgetConfigure(), which can be used to detect if the app was launched from a widget configuration requests, and returns the corresponding widget ID if so. This can then be handled in the app to load a widget settings UI.Checklist
exampleor documentation.Breaking Change?
Related Issues
N/A