Repository navigation
Conversation
Adds a persisted native event queue with retention, a size cap and coalescing, exposed through popEvents/restoreEvents/onEventsAvailable, and a JS dispatcher behind addVoltraListener for the six widget lifecycle event types.
The Dynamic Widget receiver now records widgetAdded, widgetResized, widgetRemoved, widgetRestored, widgetEnabled and widgetDisabled through a placement registry, and getActiveWidgets() reports each placement's launcher sizes.
Adds ADR 0014, a website page for the lifecycle events and the sizes field, and the version plan.
Emulator evidenceTest layout: the Widgets tab of the example app has an Android placements card. It lists Build: a debug build of the example app from this branch @ Android 15 emulator, API 35 (AVD
|
Closes #315. Supersedes the transport proposed in #314.
What is this?
An Android app can now find out when a user places, resizes, removes or restores one of its Dynamic Widgets, and when the first placement of a widget type appears or the last one goes away. It subscribes with the existing
addVoltraListenerto six events:widgetAdded,widgetResized,widgetRemoved,widgetRestored,widgetEnabledandwidgetDisabled. Events that happen while the app is closed are delivered the next time it subscribes.Until now
addVoltraListenerwas a no-op on Android, and the only way to notice a placement was to pollgetActiveWidgets()and diff snapshots. That can't tell "removed" from "never placed" and can't run while the app is dead.getActiveWidgets()also gainssizes, so a placement looks the same whether it is read or received. The design decisions, including where this departs from #314 and #315, are recorded in ADR 0014.How does it work?
When the launcher calls one of the Dynamic Widget receiver's callbacks, the receiver first runs its existing native cleanup, then updates a placement registry, then records the event in a native queue persisted in SharedPreferences. This happens whether or not the app is running. If JavaScript is running, native signals that events are available. JavaScript then removes the events for the types it listens to and receives them in one synchronous call, oldest first. If the app was dead, the same pull happens as soon as a listener subscribes or the app returns to the foreground.
This is the pull design ADR 0011 chose for iOS interactions. Native never tracks whether JavaScript is listening, so #314's listener-active flag and hot delivery are not used.
popEventsremoves what it returns, so a JavaScript reload never redelivers. Events whose listeners all unsubscribe mid-dispatch go back to the queue.widgetResizedevents for one placement collapse to the latest.widgetAddedandwidgetEnabledfire only for real changes. The system's re-announcements at boot and after a package update add or enable nothing, though a launcher that reports a new size at boot produces awidgetResized. On first use the registry is seeded silently from the existing placements.Why is this useful?
Apps can seed a new placement's configuration or initial props when it appears, stop their own sockets, subscriptions or analytics when it goes away, and send props suited to a new size. They can also count placements without diffing snapshots across launches. The channel is generic, so the widget interactions in #314 and the notification actions in #316 can add producers later without a new transport.