Skip to content

feat(mobile): run native ground and transit navigation in the installed app - #758

Merged
Medformatik merged 1 commit into
mainfrom
feat/mobile-native-navigation
Nov 3, 2026
Merged

Medformatik merged 1 commit into
mainfrom
feat/mobile-native-navigation

Conversation

@Medformatik

Copy link
Copy Markdown
Collaborator

What changed and why

The shell already had processors, durable sessions, effect ports, permissions and recovery, but nothing composed them, so the installed app reported every navigation mode as unavailable and turn-by-turn guidance never started. createForegroundNavigation now builds that graph in the foreground app, and App.tsx sends every page command to its NavigationCoordinator. The one-shot location, settings and sign-in requests still reach the shell services. The handshake reports what the shell can actually do: ground and transit navigation, background location, local notifications once that permission is granted, and speech when the native audio module is present.

  • One coordinator. The foreground coordinator is registered, so the background location task feeds the same instance instead of opening a second, headless one beside it.
  • Start flow. session.start runs the permission controller with native screens (PermissionPrompts.tsx), because the page may be suspended during OS prompts. On Android 11 and later, the background step uses the system's background-location request, which opens the app's own page with "Allow all the time". The first start also asks once for notification permission, for transit alight alerts.
  • Reroute. A reroute effect only starts the request. The answer comes back to the coordinator as a queued applyReroute, which applies it only while the session and its pending request id still match. A newer request aborts the one in flight.

Running it on a phone turned up four more failures:

  • Crash on the first location update. expo-task-manager always schedules a persisted JobScheduler job, and Android refuses that without RECEIVE_BOOT_COMPLETED, so the process crashed. The permission is now declared and reviewed in the compliance list. It registers no boot receiver, and the app starts nothing at boot.
  • "database is locked" on every location batch. expo-sqlite runs each exclusive transaction on its own connection, so a diagnostic write collided with the batch commit. Writes are now queued on the one handle, with a busy timeout.
  • No speech on a cold start. The first cue arrived before Android's TTS engine had initialised and was reported as failed. speak now waits for initialisation, up to 3 s.
  • Native screens under the status bar. Their titles sat under the status bar.

Native location disclosure shown before the OS prompt when starting navigation

Transit refresh and replan effects are still no-ops: a transit session keeps running on its captured itinerary.

Related work

Part of #398. This covers Android only: iOS has not run on a device, and transit refresh and replan are not wired. Third of four stacked PRs. It is based on #757 (shell requests), which is based on #756.

Verification

  • Pixel (Android 17) over USB against openmapx.com, with a short driving route:
    • The disclosure, the "Allow all the time" page and the notification prompt appear in order.
    • Native guidance renders from native snapshots. Ending navigation sends session.stopped and takes the location service out of the foreground.
    • A killed process resumes the session on relaunch.
    • Speech reported spoken on three consecutive cold launches. One earlier launch in the same Metro session reported failed, and I could not tell whether it ran the previously installed build, so cold-start speech needs more device runs.
  • apps/mobile: jest (new tests for reroute answers, remote work, permission prompts and App composition), tsc, biome, test:native (Swift and Kotlin), assert-permissions, assert-generated, assert-boundary, inventory and pnpm mobile:bundle:check.
  • Not checked: a reroute triggered on the road, transit navigation on a device, and iOS.

Release and upgrade impact

Product impact: ordinary

@Medformatik Medformatik added documentation Documentation content, tooling or workspace dependencies javascript Pull requests that update javascript code labels Nov 3, 2026
@Medformatik Medformatik self-assigned this Nov 3, 2026
@Medformatik
Medformatik force-pushed the feat/mobile-shell-requests branch from 904ca65 to 6f60e3e Compare November 3, 2026 21:51
Base automatically changed from feat/mobile-shell-requests to main November 3, 2026 22:11
…ed app

Compose the navigation coordinator into the foreground app and dispatch the page's session commands to it. The foreground coordinator is registered so the background location task feeds the same instance, and the handshake now reports the navigation capabilities the shell actually has.

The start flow shows the native permission screens, uses Android's background-location request to reach "Allow all the time", and asks once for notification permission. Reroute answers re-enter the coordinator as queued mutations bound to the request they answer. Transit refresh and replan are still no-ops.

Fixes found on a device: expo-task-manager persists its job, so the app now holds RECEIVE_BOOT_COMPLETED (without it the first location update crashed the process); writes are serialised on the SQLite handle (separate exclusive-transaction connections hit "database is locked"); speech waits for the TTS engine to initialise (a cold launch reported every cue as failed); and the native screens clear the status bar.
@Medformatik
Medformatik force-pushed the feat/mobile-native-navigation branch from 8a75857 to ad03646 Compare November 3, 2026 22:11
@Medformatik
Medformatik merged commit ae28df7 into main Nov 3, 2026
23 checks passed
@Medformatik
Medformatik deleted the feat/mobile-native-navigation branch November 3, 2026 22:25
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 3, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

documentation Documentation content, tooling or workspace dependencies javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant