feat: Android home-screen widgets (Jetpack Glance) - #255
Conversation
1167693 to
deed3b4
Compare
|
Pushed a follow-up commit (d9a9d77) to get this review-ready:
Verified locally: |
|
The error was on my side. Did a complete reinstall of the repo. Now I can build the app. |
|
I was able to test the widgets. |
As it is time consuming, I'm waiting to see if you agree to go (CC: @naltatis ) and then I can continue the work. |
|
To me, this looks like a good approach. The implementation of Android widgets is definitely wanted. |
d9a9d77 to
52f7788
Compare
|
Pushed a follow-up addressing both points from @Maschga's feedback above:
Still not attempted: widget size variants (iOS's medium-size mode-selector column) and localization (still hardcoded English) - both already called out as follow-ups. Verified with |
|
Pushed both remaining follow-ups called out earlier:
Verified with |
|
Just to make sure this topic isn't forgotten: The widgets will need to support Smart Mode once they're ready. (see #246) The current widgets already look promising! |
Adds loadpoint and forecast home-screen widgets built with Jetpack Glance: a widget configuration activity for picking server + loadpoint, real chart rendering for forecast data, per-instance widget config with immediate refresh on server change, and widget package name derived from app config so fork builds can coexist with the official app install. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
lint was failing on 3 @typescript-eslint/no-explicit-any errors in withAndroidWidget.ts; typed the receiver/activity manifest nodes against AndroidConfig.Manifest instead. Also brings the README status section up to date - it still described the pipeline-spike state (one widget, no config Activity, no instant refresh) even though all 5 widgets, per-instance config, and instant refresh are implemented.
Ports LoadpointVM's full status/metric logic (heating, finished/ waitForVehicle, kWh fallback), status dot + color coding, a canvas-rendered progress bar (Glance has no fractional-width modifier), chip-style mode buttons, a two-column forecast header, a chart Y-axis + step-vs-area modes + per-type color (previously always a flat green line), bold/colored footer stats, and day/night theming throughout - all mirrored from LoadpointViews.swift / Views.swift / Theme.swift. Both widget config Activities now fetch real data for the tapped choice and show an actual preview of the widget (plain Views reusing the same chart/ progress-bar bitmaps, since a live Glance render would need pulling in the full Compose UI stack) before committing via a new "Use this" button, instead of committing immediately on tap with no preview. Verified with expo prebuild + local assembleDebug/assembleRelease builds.
The Android widgets and their config Activities had hardcoded English text. Extend build-widget-strings.mts (already generating the iOS .xcstrings catalog from evcc's + this app's Weblate translations) to also emit Android string resources under targets/android-widget/res/values(-b+<locale>)/, and have withAndroidWidget.ts merge them into the prebuilt project's values/strings.xml (which already has app_name etc.) instead of overwriting it. Replaced every hardcoded string in the widget/config-Activity Kotlin with R.string.* lookups. The config Activities' picker/live-preview flow has no iOS equivalent, so those strings are new additions to this app's own i18n (en.json source + de.json, matching this repo's i18n conventions) rather than reuses of existing evcc/app keys. Also generalized the config plugin's package-rewrite from a single `package` line replace to a whole-file regex, since the widget Kotlin now needs `import io.evcc.android.R` to resolve correctly for the LOCAL-ONLY dev fork build too (io.evcc.android.dev). Verified with expo prebuild + local assembleDebug (confirmed the manifest merge keeps app_name, and R resolves for the fork package).
Adds the last open follow-up from the parity work: a second widget size,
mirroring LoadpointCard's HStack { left; modeSelector } on iOS. Declares
SizeMode.Responsive(setOf(SMALL_SIZE, WIDE_SIZE)) and reads LocalSize to
branch layout - compact keeps the inline mode-chip row (deliberately kept
interactive, unlike iOS's compact size which drops to a plain-text mode
label), wide adds a vertical mode-selector column alongside. No manifest
change needed since the widget was already resizable; this just makes the
wider size render differently once a user resizes it.
Forecast widgets have no iOS size-variant precedent, so they stay single-size.
Verified with expo prebuild + local assembleDebug.
pairs with evcc-io/evcc#32490 and mirrors targets/widget/Loadpoint.swift's handling from evcc-io#246, which this branch picked up via a rebase onto main. Detects smart-mode servers via loadpoints[].alwaysCharge, switching the selector to off/smart/now (with device-class labels for continuous heat pumps and switchable devices) while old servers keep off/pv/minpv/now unchanged. A read-only "∞" marks the Smart chip when Always charge is on/once, no toggle in the widget yet, matching iOS. Also fixes a bug the rebase's auto-merge introduced: the frozen pv/minpv legacy-label loop wrote straight into `strings`, a variable that in this branch is now built later from an intermediate `translations` map (added here to share resolved strings between the iOS and Android generators) - it was referencing `strings` before its declaration.
1aac0fb to
223ae76
Compare
I've implemented it, thanks for noticing it. Can you do a check again and provide a feedback? |
The buttons, text, and graphics are all on the widget up there. Now it's just a matter of arranging them properly so they look like the iOS widgets. |
Addresses evcc-io#255#issuecomment-5317470240 (Maschga's PR review): - LoadpointWidget gets a reload button in the title row (res/drawable/ic_reload.xml tinted via Glance's ColorFilter.tint(), wired to a new ReloadAction), mirroring iOS's ReloadIntent. - Both widget families are now tappable end-to-end, deep-linking to the right app screen (evcc://loadpoint?server=&lp=, evcc://forecast?server=, evcc://server when unconfigured) via a shared deepLinkAction() helper - mirrors widgetURL in LoadpointViews.swift/Views.swift, including the same query param semantics the app's own router (AppContext.tsx) expects. - loadpoint_widget_info.xml's resize bounds are now pinned to SizeMode.Responsive's two declared breakpoints (180-340dp wide, height locked at 110dp, resizeMode="horizontal" only) instead of open-ended horizontal|vertical; forecast_widget_info.xml drops resizeMode entirely since those widgets have no size-variant layout. Closes the gap where a launcher could hand the widget a real container bigger than any size Glance was told to lay content out for, leaving unfillable blank space - the likely cause of the "strange spacing" in Maschga's screenshot, pending on-device confirmation. The mode-button highlight bug and widget-reconfigure check from the same review are held for a live-device pass.
If charting is non-trivial for Android (iOS has builtin charting for widgets), we could also simplify and start with just the loadpoint widgets. |
Per naltatis's review (evcc-io#255#issuecomment-5327087002): Android has no first-party charting API comparable to iOS's Swift Charts, and even third-party Compose chart libraries generally can't render inside a Glance widget's RemoteViews surface - the four forecast widgets carry real hand-rolled-canvas risk this PR doesn't need to also resolve. Splits them out: - Removes ForecastWidget.kt, ForecastWidgetConfigActivity.kt, ChartRenderer.kt and their manifest receiver/config-activity/widget-info-xml/preview registration in the config plugin. - Trims the now-unused forecast-only pieces from the files shared between both widget families: WidgetConfig.kt (saveForecast/forecastServerId/ forecastAdjust), Theme.kt (per-forecast-type Palette + header/footer styles), WidgetPreview.kt (forecast()/footerSide()). - build-widget-strings.mts is untouched - it also feeds iOS's already-shipped forecast widgets, so its KEYS table still has forecast entries; only unused on the Android side now. - README rewritten for loadpoint-only scope, pointing at the feat/android-widgets-forecast branch (a snapshot of this branch pre-split) for the deferred forecast-widget follow-up. Verified with expo prebuild + a full local Android build (assembleDebug) and npm run lint.


Adds loadpoint and forecast home-screen widgets built with Jetpack Glance: a widget configuration activity for picking server + loadpoint, real chart rendering for forecast data, per-instance widget config with immediate refresh on server change, and widget package name derived from app config so fork builds can coexist with the official app install.