Customizable launcher widget#22
Open
jcalado wants to merge 23 commits into
Open
Conversation
…nect button - Status dot ONLINE swaps to brand orange (was emerald green) - Active connect button forces brand orange even under Material You - LIGADO label and power glyph recolor when connected - Battery icon/text gain semantic tinting (red ≤15%, orange ≤40%) - Replace mangled broadcast vector with clean Material campaign icon - Add tonal action button backgrounds with Material You light/dark variants
Theme.AppCompat.DayNight already supplies a window action bar, so calling setSupportActionBar(toolbar) inside the activity raised IllegalStateException at start. Use the NoActionBar variant so the activity-supplied Toolbar is the only one.
- WidgetConfig: store labels: Set<String> alongside buttons; default {sos, connect}
- Provider: derive label visibility from config.labels; localize labels for non-special buttons (advert/chats/map/plan333/telemetry)
- Config screen: add per-row Switch "Etiqueta" visible only on selected rows
- Swatches: replace XML selector + tint dance with programmatic StateListDrawable; selected state now draws a visible ring stroke around the colored circle
Pushes signal_bars (0-4 derived from best SNR over last 5 min) from Dart and renders 4 stepped bars in the widget header. Color and threshold mapping mirrors lib/ui/screens/home_screen.dart::_SignalBarsIcon. Bars are hidden (rendered as 0) when disconnected.
ConnectionNotifier._pushWidget reading bestSignalSnrProvider triggers CircularDependencyError because that provider watches connectionProvider — the same StateNotifier we are. Inline the 5-min best-SNR scan against rxLogProvider instead.
…al bars - Status dot and active connect button now keep their circular drawables and tint via setBackgroundTintList instead of setBackgroundColor (which replaced the drawable with a solid rectangle). - Signal bars now use the configured accent color (filled = accent, unfilled = accent at low alpha) so the indicator matches the rest of the widget's accent.
…ut-aware insets - Switch theme from AppCompat to Theme.Material3.DayNight.NoActionBar - Add com.google.android.material:material dep - Replace AppCompat Button/CheckBox/SwitchCompat/Toolbar with MaterialButton/MaterialCheckBox/MaterialSwitch/MaterialToolbar - Use MaterialAlertDialogBuilder for the RGB picker and validation dialogs - fitsSystemWindows + windowLayoutInDisplayCutoutMode=shortEdges so the toolbar clears the camera cutout - Replace dated drag handle (ic_menu_sort_by_size) with a clean two-line vector
…plugin FlutterActivity.onNewIntent does not call setIntent(intent), so HomeWidget.widgetClicked kept seeing the original launch URI. Result: only the first tap routed correctly; subsequent taps fell through to the /channels redirect. Override onNewIntent to forward.
FlutterActivity.onNewIntent dispatches the intent to plugins (so home_widget can read intent.data) and then unconditionally pushes intent.data to the flutter/navigation channel as a deep-link route. That second step caused GoRouter to receive the meshcore-widget:// URI after our Dart-side dispatcher had already navigated, triggering the scheme redirect to /channels and overwriting the user's intended destination on every warm tap. For meshcore-widget URIs we now dispatch to plugins manually via FlutterEngine.activityControlSurface.onNewIntent and skip Flutter's built-in deep-link push. Other URIs go through the default path unchanged.
There was a problem hiding this comment.
Pull request overview
This PR enhances the Android home-screen launcher widget by adding per-widget-instance customization (buttons, ordering, labels, accent color), visual polish (status dot tinting, header signal/battery indicators), and new widget deep-link actions (Plan 3-3-3, Telemetry), alongside intent-routing fixes for warm-start widget taps.
Changes:
- Added a Material 3 widget configuration activity and per-widget persisted config (buttons/labels/accent).
- Extended widget → app action plumbing (new URI actions, GoRouter redirect hardening, intent routing fix, widget signal bars + accent-driven tinting).
- Introduced/updated Android widget layouts, drawables, and build dependencies; added a Dart unit test for
WidgetAction.fromUri.
Reviewed changes
Copilot reviewed 52 out of 52 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| test/widget_action_uri_test.dart | Adds coverage for widget action URI parsing. |
| lib/ui/router.dart | Hardens redirect handling for meshcore-widget:// URIs. |
| lib/services/widget_service.dart | Adds signal bars persistence + SNR→bars mapping + new widget actions. |
| lib/providers/parts/connection_notifier.dart | Inlines best-SNR scan to avoid provider circular dependency and pushes signal bars to widget. |
| lib/main.dart | Routes new widget actions to app destinations. |
| android/app/src/main/res/xml/widget_info.xml | Enables configure + reconfigure support for the widget. |
| android/app/src/main/res/values/styles.xml | Adds Material 3 theme for widget config activity. |
| android/app/src/main/res/values/strings.xml | Adds widget config UI strings and button labels. |
| android/app/src/main/res/values/colors.xml | Adds widget on-surface color tokens (light). |
| android/app/src/main/res/values-night/colors.xml | Adds widget on-surface color tokens (dark). |
| android/app/src/main/res/layout/widget_meshcore.xml | Updates widget layout (header signal/battery, 5 generic action slots). |
| android/app/src/main/res/layout/item_widget_button_pick.xml | RecyclerView row layout for selecting/reordering widget buttons. |
| android/app/src/main/res/layout/activity_widget_config.xml | Widget configuration activity UI layout (toolbar, list, swatches). |
| android/app/src/main/res/drawable/widget_swatch_bg.xml | Adds swatch background selector drawable. |
| android/app/src/main/res/drawable/widget_status_dot_online.xml | Online dot drawable. |
| android/app/src/main/res/drawable/widget_status_dot_offline.xml | Offline dot drawable. |
| android/app/src/main/res/drawable/widget_status_dot_neutral.xml | Neutral dot drawable (tinted at runtime). |
| android/app/src/main/res/drawable/widget_status_chip_online.xml | Online chip background drawable. |
| android/app/src/main/res/drawable/widget_status_chip_offline.xml | Offline chip background drawable. |
| android/app/src/main/res/drawable/widget_background.xml | Widget background styling for pre-v31. |
| android/app/src/main/res/drawable/widget_action_btn_sos_bg.xml | SOS action button background. |
| android/app/src/main/res/drawable/widget_action_btn_bg.xml | Neutral action button background. |
| android/app/src/main/res/drawable/widget_action_btn_active_neutral.xml | Neutral “active” button background (tinted at runtime). |
| android/app/src/main/res/drawable/widget_action_btn_active_bg.xml | Active button background (static fallback). |
| android/app/src/main/res/drawable/ic_widget_warning.xml | SOS/warning vector icon. |
| android/app/src/main/res/drawable/ic_widget_telemetry.xml | Telemetry vector icon. |
| android/app/src/main/res/drawable/ic_widget_tag.xml | Channels/tag vector icon. |
| android/app/src/main/res/drawable/ic_widget_power.xml | Connect/power vector icon. |
| android/app/src/main/res/drawable/ic_widget_plan333.xml | Plan 3-3-3 vector icon. |
| android/app/src/main/res/drawable/ic_widget_people.xml | People/contacts vector icon. |
| android/app/src/main/res/drawable/ic_widget_map.xml | Map vector icon. |
| android/app/src/main/res/drawable/ic_widget_location.xml | GPS/location vector icon. |
| android/app/src/main/res/drawable/ic_widget_drag_handle.xml | Drag handle vector icon. |
| android/app/src/main/res/drawable/ic_widget_chat.xml | Chat vector icon. |
| android/app/src/main/res/drawable/ic_widget_broadcast.xml | Broadcast/advert vector icon. |
| android/app/src/main/res/drawable/ic_widget_battery_mid.xml | Battery mid vector icon. |
| android/app/src/main/res/drawable/ic_widget_battery_low.xml | Battery low vector icon. |
| android/app/src/main/res/drawable/ic_widget_battery_high.xml | Battery high vector icon. |
| android/app/src/main/res/drawable/ic_widget_battery_full.xml | Battery full vector icon. |
| android/app/src/main/res/drawable/ic_widget_battery_alert.xml | Battery alert vector icon. |
| android/app/src/main/res/drawable-v31/widget_background.xml | Widget background leveraging system dynamic colors on v31+. |
| android/app/src/main/res/drawable-v31/widget_action_btn_bg.xml | Action button background using system dynamic colors on v31+. |
| android/app/src/main/res/drawable-v31/widget_action_btn_active_bg.xml | v31+ active background resource. |
| android/app/src/main/res/drawable-night-v31/widget_background.xml | Night v31+ widget background. |
| android/app/src/main/res/drawable-night-v31/widget_action_btn_sos_bg.xml | Night v31+ SOS background. |
| android/app/src/main/res/drawable-night-v31/widget_action_btn_bg.xml | Night v31+ action button background. |
| android/app/src/main/kotlin/pt/meshcore/lusoapp/WidgetConfigActivity.kt | New widget configuration activity (button picking, swatches, custom color dialog). |
| android/app/src/main/kotlin/pt/meshcore/lusoapp/WidgetConfig.kt | New per-widget persisted config model + prefs helpers. |
| android/app/src/main/kotlin/pt/meshcore/lusoapp/MeshCoreWidgetProvider.kt | Updates widget rendering to use per-widget config, accent tinting, signal bars, cleanup on delete. |
| android/app/src/main/kotlin/pt/meshcore/lusoapp/MainActivity.kt | Fixes intent routing for warm-start widget taps (skip Flutter deep-link push for widget URIs). |
| android/app/src/main/AndroidManifest.xml | Registers widget configure activity with Material theme. |
| android/app/build.gradle.kts | Adds AndroidX/Material/Flexbox dependencies for config UI. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+51
to
53
| if (signalBars != null) | ||
| HomeWidget.saveWidgetData<int>('signal_bars', signalBars.clamp(0, 4)), | ||
| ]); |
Comment on lines
160
to
+173
| switch (action) { | ||
| case WidgetAction.open: | ||
| // Just bring the app to the foreground — no navigation change. | ||
| break; | ||
| case WidgetAction.openChats: | ||
| router.go('/channels'); | ||
| case WidgetAction.openMap: | ||
| router.go('/map'); | ||
| case WidgetAction.openConnect: | ||
| router.go('/connect'); | ||
| case WidgetAction.openPlan333: | ||
| router.go('/apps/plan333'); | ||
| case WidgetAction.openTelemetry: | ||
| router.go('/apps/telemetry'); |
Comment on lines
+117
to
+121
| views.setColorStateList( | ||
| R.id.widget_status_dot, | ||
| "setBackgroundTintList", | ||
| if (connected) ColorStateList.valueOf(accent) else null, | ||
| ) |
Comment on lines
+248
to
252
| views.setColorStateList( | ||
| frame, | ||
| "setBackgroundTintList", | ||
| if (bgTint != null) ColorStateList.valueOf(bgTint) else null, | ||
| ) |
Comment on lines
+203
to
+209
| val container = LinearLayout(ctx).apply { | ||
| orientation = LinearLayout.VERTICAL | ||
| setPadding(48, 32, 48, 16) | ||
| } | ||
| val preview = View(ctx).apply { | ||
| layoutParams = LinearLayout.LayoutParams( | ||
| LinearLayout.LayoutParams.MATCH_PARENT, 80, |
Comment on lines
+259
to
+260
| "Escolhe entre ${WidgetConfig.MIN_BUTTONS} e " + | ||
| "${WidgetConfig.MAX_BUTTONS} botões.", |
Comment on lines
+131
to
+136
| private fun makeSwatch(color: Int, isCustom: Boolean): View { | ||
| val density = resources.displayMetrics.density | ||
| val size = (density * 40).toInt() | ||
| val margin = (density * 6).toInt() | ||
| val ringPx = (density * 3).toInt() | ||
|
|
Comment on lines
+33
to
41
| <ImageView | ||
| android:id="@+id/widget_gps_badge" | ||
| android:layout_width="wrap_content" | ||
| android:layout_height="wrap_content" | ||
| android:layout_marginEnd="6dp" | ||
| android:text="📍" | ||
| android:textSize="12sp" | ||
| android:layout_width="14dp" | ||
| android:layout_height="14dp" | ||
| android:layout_marginEnd="10dp" | ||
| android:src="@drawable/ic_widget_location" | ||
| android:tint="@color/widget_on_surface_variant" | ||
| android:contentDescription="GPS" | ||
| android:visibility="gone" /> |
Comment on lines
+60
to
+67
| <LinearLayout | ||
| android:id="@+id/widget_signal_bars" | ||
| android:layout_width="wrap_content" | ||
| android:layout_height="14dp" | ||
| android:layout_marginStart="8dp" | ||
| android:orientation="horizontal" | ||
| android:gravity="bottom" | ||
| android:contentDescription="Sinal"> |
Comment on lines
+1
to
+23
| <?xml version="1.0" encoding="utf-8"?> | ||
| <selector xmlns:android="http://schemas.android.com/apk/res/android"> | ||
| <item android:state_selected="true"> | ||
| <layer-list> | ||
| <item> | ||
| <shape android:shape="oval"> | ||
| <solid android:color="@android:color/white" /> | ||
| </shape> | ||
| </item> | ||
| <item android:left="3dp" android:top="3dp" | ||
| android:right="3dp" android:bottom="3dp"> | ||
| <shape android:shape="oval"> | ||
| <solid android:color="@android:color/white" /> | ||
| </shape> | ||
| </item> | ||
| </layer-list> | ||
| </item> | ||
| <item> | ||
| <shape android:shape="oval"> | ||
| <solid android:color="@android:color/white" /> | ||
| </shape> | ||
| </item> | ||
| </selector> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds per-instance widget customization plus a few visual upgrades:
setBackgroundTintListinstead ofsetBackgroundColor).meshcore-widget://nav/apps/plan333and…/telemetrymapped throughWidgetActionand_handleWidgetAction.Bug fixes shipped along the way
FlutterActivity.onNewIntentwas pushing the widget URI intoflutter/navigation, which triggered GoRouter's scheme redirect to/channelsafter our dispatcher had already navigated.MainActivity.onNewIntentnow manually routes meshcore-widget URIs to plugins only and skips the deep-link push.bestSignalSnrProviderwatchesconnectionProvider, which is the StateNotifier that calls_pushWidget, so reading it from there raisedCircularDependencyErroron every channel-info packet. Inlined the 5-minute best-SNR scan inConnectionNotifier.Theme.AppCompat.DayNightsupplying an action bar while the activity also calledsetSupportActionBar(toolbar); switched toTheme.Material3.DayNight.NoActionBar.Files
WidgetConfigActivity.kt,WidgetConfig.ktdata model, configuration layouts, neutral tintable drawables, plan333 / telemetry / drag-handle vector icons.MeshCoreWidgetProvider.kt(5 generic slots, accent tinting, signal bars,onDeletedcleanup),widget_meshcore.xml,widget_info.xml(configure + reconfigurable),AndroidManifest.xml(Material theme, configure activity),MainActivity.kt(intent routing fix),lib/services/widget_service.dart(signalBars+ new actions),lib/main.dart(action dispatcher cases),lib/providers/parts/connection_notifier.dart(inline SNR),build.gradle.kts(recyclerview, flexbox, appcompat, material).Test plan
widget_<id>_*keys; new widget starts at defaultssetBackgroundTintListso the dot, active button, low-battery icon and signal bars all recolor together