Introduce FrontendTarget and use it instead of WebViewActivity - #7087
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a typed FrontendTarget destination model to replace raw/nullable “path” usage across launch/navigation flows, and completes more-info deep link handling by supporting the HA 2025.6+ query parameter with a JavaScript fallback for older servers. It also migrates many callers away from WebViewActivity.newInstance toward LaunchActivity-based intents.
Changes:
- Added
FrontendTargetsealed interface and updatedLaunchActivity/FrontendRoute/ URL resolution APIs to use it. - Implemented
EntityMoreInfohandling with HA-version gating: URL query param on 2025.6+, JShass-more-infodispatch fallback otherwise. - Updated multiple entry points (widgets, notifications, controls, settings, link handling) and expanded unit test coverage.
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| app/src/test/kotlin/io/homeassistant/companion/android/launch/link/LinkViewModelTest.kt | Uses main dispatcher JUnit5 extension for coroutine correctness |
| app/src/test/kotlin/io/homeassistant/companion/android/launch/LaunchViewModelTest.kt | Updates expected routes to use FrontendTarget |
| app/src/test/kotlin/io/homeassistant/companion/android/frontend/WebViewActionTest.kt | Adds coverage for OpenMoreInfo JS generation and escaping |
| app/src/test/kotlin/io/homeassistant/companion/android/frontend/url/FrontendUrlManagerTest.kt | Updates API to target and adds HA-version more-info tests |
| app/src/test/kotlin/io/homeassistant/companion/android/frontend/navigation/FrontendTargetTest.kt | New tests for raw-path parsing/round-tripping |
| app/src/test/kotlin/io/homeassistant/companion/android/frontend/FrontendViewModelTest.kt | Updates URL manager mocks + adds tests for JS more-info fallback behavior |
| app/src/main/kotlin/io/homeassistant/companion/android/widgets/camera/CameraWidget.kt | Migrates widget tap intent to LaunchActivity via FrontendTarget |
| app/src/main/kotlin/io/homeassistant/companion/android/webview/WebViewActivity.kt | Deprecates newInstance to steer callers to LaunchActivity |
| app/src/main/kotlin/io/homeassistant/companion/android/websocket/WebsocketManager.kt | Uses LaunchActivity instead of WebViewActivity for notification intent |
| app/src/main/kotlin/io/homeassistant/companion/android/util/compose/GlanceExt.kt | Switches glance action to build LaunchActivity intent for navigation |
| app/src/main/kotlin/io/homeassistant/companion/android/settings/shortcuts/views/ManageShortcutsView.kt | Uses FrontendTarget parsing/serialization for shortcut entity selection |
| app/src/main/kotlin/io/homeassistant/companion/android/settings/SettingsFragment.kt | Uses FrontendTarget.Default intent for “activate server” CTA |
| app/src/main/kotlin/io/homeassistant/companion/android/settings/server/ServerSettingsFragment.kt | Uses FrontendTarget.Default intent for “activate server” preference |
| app/src/main/kotlin/io/homeassistant/companion/android/qs/TilePreferenceActivity.kt | Routes QS tile setup intent to entity more-info via FrontendTarget |
| app/src/main/kotlin/io/homeassistant/companion/android/notifications/MessagingManager.kt | Migrates notification click routing to FrontendTarget + LaunchActivity |
| app/src/main/kotlin/io/homeassistant/companion/android/launch/link/LinkActivity.kt | Converts raw path deep link to FrontendTarget before launching |
| app/src/main/kotlin/io/homeassistant/companion/android/launch/LaunchViewModel.kt | Switches deep link connect/navigation pipeline to FrontendTarget |
| app/src/main/kotlin/io/homeassistant/companion/android/launch/LaunchActivityExt.kt | Replaces string-path launcher helpers with FrontendTarget variants |
| app/src/main/kotlin/io/homeassistant/companion/android/launch/LaunchActivity.kt | Updates DeepLink.NavigateTo to carry a FrontendTarget |
| app/src/main/kotlin/io/homeassistant/companion/android/frontend/WebViewAction.kt | Adds OpenMoreInfo action for older-server fallback |
| app/src/main/kotlin/io/homeassistant/companion/android/frontend/url/UrlLoadResult.kt | Extends Success with moreInfoEntityId for JS fallback signaling |
| app/src/main/kotlin/io/homeassistant/companion/android/frontend/url/FrontendUrlManager.kt | Introduces target API + HA-version gated more-info URL building |
| app/src/main/kotlin/io/homeassistant/companion/android/frontend/navigation/FrontendTarget.kt | New typed destination model + raw-path mapping helpers |
| app/src/main/kotlin/io/homeassistant/companion/android/frontend/navigation/FrontendNavigation.kt | Stores route as raw path (perf) but exposes typed target |
| app/src/main/kotlin/io/homeassistant/companion/android/frontend/FrontendViewState.kt | Replaces path with FrontendTarget in view states |
| app/src/main/kotlin/io/homeassistant/companion/android/frontend/FrontendViewModel.kt | Tracks pending JS more-info fallback and emits OpenMoreInfo on page finish |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/HaControlsPanelActivity.kt | Removes WebViewActivity path and always routes via LaunchActivity + FrontendTarget |
| app/src/main/kotlin/io/homeassistant/companion/android/controls/HaControl.kt | Updates control PendingIntent to use LaunchActivity + EntityMoreInfo target |
| app/src/main/kotlin/io/homeassistant/companion/android/assist/AssistActivity.kt | Migrates internal navigation to use FrontendTarget.Path via launcher helper |
| // Only consume the target when a URL was actually loaded with it | ||
| if (urlState is UrlState.HasUrl) { | ||
| pathConsumed = true | ||
| targetConsumed = true | ||
| } |
There was a problem hiding this comment.
I think this is already the behavior we have today.
| private fun moreInfoScript(entityId: String): String { | ||
| val entityIdJson = Json.encodeToString(entityId) | ||
| return """document.querySelector("home-assistant")""" + | ||
| """.dispatchEvent(new CustomEvent("hass-more-info", { detail: { entityId: $entityIdJson }}))""" | ||
| } |
There was a problem hiding this comment.
It's a fallback and has been pretty reliable as far as I'm aware, so I wouldn't change it now. It is only a JS error you'd get, not an app crash, right?
| is UrlLoadResult.Success -> { | ||
| pendingMoreInfoEntityId = result.moreInfoEntityId | ||
| _viewState.update { |
bf2969f to
8c88dd9
Compare
| var targetConsumed = false | ||
| serverManager.connectionStateProvider(actualServerId).urlFlow().collect { urlState -> | ||
| val currentPath = if (pathConsumed) null else path | ||
| val currentTarget = if (targetConsumed) FrontendTarget.Default else target | ||
|
|
||
| val result = handleUrlState( | ||
| serverId = actualServerId, | ||
| urlState = urlState, | ||
| path = currentPath, | ||
| target = currentTarget, | ||
| ) | ||
| // Only consume the path when a URL was actually loaded with it | ||
| // Only consume the target when a URL was actually loaded with it | ||
| if (urlState is UrlState.HasUrl) { | ||
| pathConsumed = true | ||
| targetConsumed = true | ||
| } |
jpelgrom
left a comment
There was a problem hiding this comment.
Sorry for the delay. Reviewed again while also considering the next PR and changes almost all look good, just one issue found!
60e840c to
406524a
Compare
Summary
The support for the deep link
more-infowas not complete it was missing the fallback case for old server, in order to make it right I've decided to introduce theFrontendTargetsealed interface. I've also replace most of the call toWebViewActivity.newInstanceit only remains one usage inManageShortcutsViewModelwe need to migrate toLinkActivity#7086.I've also made sure that the given parameter cannot extend more than just the entityID in the URL or JS evaluation.
Checklist