Repository navigation
feat(mobile): show OpenMapX navigation on Android Auto - #776
Merged
Merged
Conversation
Work in progress: native car app, car map page, destinations and car start.
Testing on a phone with the Desktop Head Unit showed that car planning and starting failed and that the car map lacked basic controls. - Planning and starting work with the phone app in the background: start eligibility uses the process importance the car host gives, and expo-location is told the process is foreground while the car shows OpenMapX. - React Native's AbortSignal gets throwIfAborted, which the shared API client needs for every directions request. - Car starts no longer call crypto.randomUUID, which Hermes lacks. - The route preview opens: the screen push moved out of onGetTemplate. - Each planning failure shows its own message; only refusals the phone can fix ask for phone setup. - Route preview rows show time and distance with A/B/C badges matching the map, a row tap re-highlights its route, the selected route is drawn on top, and Start sits in the action strip. Zoom controls also show on the preview. - Map controls (pan, zoom, overview or recenter) and Destinations stay on screen. Android Auto's own X ends the trip, so there is no separate Stop. - Before a trip the map shows the car's position at street zoom. - Current speed sits bottom right beside the speed limit, larger, and is hidden while choosing a route. - Turn arrows, keep-left/right forks and lane images are drawn for the routing card; guidance distances are rounded; turn alerts only pop up while another app is on the car screen. - Search ranks places near the car first. - Ending a trip clears its route from the car map. - Road shields scale with their sign instead of overflowing it.
Google requires navigation apps to simulate a drive when Android Auto calls NavigationManagerCallback.onAutoDriveEnabled (car app quality NF-7). The car's Test drive action only exists in debug builds, so release builds had none. The callback now switches the car session into a test-drive mode. Planning starts from a fixed point south of Alexanderplatz instead of the phone's position, search ranks results around it, and Destinations offers the Brandenburg Gate. The official instance covers Germany only, so a reviewer anywhere still gets a route. A trip started in this mode replays its route through the simulated location driver, installed before the start so no real location service runs. Only the host callback can enable this, and it ends with the car session. Also centre the A badge when the preview has a single route.
The car drew its own arrows: lane arrows bent at a shallow angle, so a left turn looked like a slight left, and they differed from the turn icon beside them. Lane highlighting also used the raw engine data, while the phone recommends lanes from signs when the engine marks none, and the car showed lanes long before the turn. The phone's glyph choice and lane presentation move from the web app to @openmapx/core (maneuverIconName, presentGuidanceLanes). The phone's banner and lane strip now use them unchanged, and the car projection sends the same glyph names and lane cells. The car draws the same Material icon paths in the banner's style and shows lanes only within the phone's approach distance. Also allowlist the car surface in the camera-framing and marker-click gates and move its route-letter layers to free slot orders.
Android Auto's turn notification was a second CATEGORY_NAVIGATION notification that took over the Expo location notification's ID and restored it on disconnect. The trip already has a navigation card from the navigation-surfaces module, so the car module now decorates that card with a CarAppExtender through a small hook, and re-posts it when the turn or the car connection changes. One notification remains; the Expo notification is left alone.
The car page had a simplified copy of the road-conditions layer that refetched about every half second in follow mode, because the follow camera moves the map every frame, and that drew roadworks even before a trip. It now mounts the phone's RoadConditionsLayer, which has the viewport scheduler, zoom gate and overlay visibility: on while driving through ContextualOverlays, or when the user enabled it.
…e navigation card Three rules rounded guidance distances: the phone banner (formatMeasurementDistance), the Android Auto module (50 m steps, in Kotlin) and the navigation card (projectSurface). Core now exposes the banner's rule as measurementDistanceParts, also from the navigation subpath the shell imports. The car projection and route list send its value and unit, Kotlin only wraps them as the host's Distance, and the navigation card formats the same parts in the trip's language.
…lyph The car projection kept its own maneuver list with keep, fork and ramp rules, a second reading of what core's maneuverIconName decides. The host's turn type is now derived from that glyph in one small table; a roundabout's exit number and circling direction travel as their own field.
The car's simulated driver delivered one route vertex per second, so it crawled through curves and leapt along long straights. It now replays core's simulatePositions at 10 m/s, the generator the web's navigation simulator uses, including heading.
…or logic The car's own label placement picked a geographic point per route. It now uses routePillAnchors, which places the directions map's route pills on visible geometry, apart from each other, and is recomputed whenever the car map settles.
The car page had its own bounds walk; core's geoJsonBBox already serves the phone's route overview. Adds a test for the car overview framing only the rest of the trip.
…and the car The car built the geocoding URL by hand with fetch. Core now has fetchGeocode, used by the phone's useGeocoding hook and exported from the navigation/api subpath, so the car search goes through the shared API client with the same parameters, proximity rounding, abort and timeout handling.
In split screen Android Auto keeps the map surface full size and reports the list and the media panel as insets, leaving a strip of about 240 px. A fixed 48 px margin on each side shrank the preview to 140 px. The margin now scales with the free area (8 % of its smaller side, 8 to 48 px).
…navigating During a ground trip the road-conditions overlay showed every incident in view, which covered the map in a city. It now keeps events along the route and up to 1 km beside it (core isNearRoute, built on the route matcher navigation already uses), and shows everything again when the trip ends. The phone and the Android Auto car map both mount this layer, so they behave the same. Outside navigation the overlay is unchanged.
The host's end-trip button was confirmed only on Android Auto 18.1 with touch; rotary access and older hosts need a real car. Head unit restart, process kill and force stop all restore a live car map on the DHU.
- A new car session starts with an empty command mailbox, and the runtime ignores a detach that arrives once the next session is connected, so a leftover detach or stop cannot end the next session or its trip. - Trip updates no longer clear a route preview shown during a trip; a trip start already sends an empty preview. - The car saved-places fetch runs only inside the installed Android app that asked for car destinations, not for every signed-in web user, and only Android advertises car destinations. - The car renderer's recovery budget resets when the page loads again. - Planning cancels a search still in flight, so a late result cannot replace the plan's result. - MapCanvas removes its WebGL-loss listener before tearing the map down, so its own teardown is not reported to the car host as a renderer failure. - A failed simulated start gives a still-running trip its real location back, and a simulated trip ends when the car disconnects. - Tapping the turn notification in the car opens the car app. - The route corridor check samples long lines densely and recognises areas the route runs through.
…geometry Sampling generated interior points before applying its cap, so one very long or malformed line from a feed could allocate millions of points. The spacing is now derived from the total length first, vertices are thinned to a fixed budget, coordinates outside WGS84 ranges are ignored, and the area check is limited in rings and ring size.
…nce surface The registry lists NAVIGATION_TEMPLATES and ACCESS_SURFACE, which the Android Auto config plugin declares; the test still expected ten permissions and only read app.config.ts.
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
What changed and why
The Android app can now be used for ground navigation on Android Auto. Until now, connecting the phone to a car showed nothing from OpenMapX. Now OpenMapX appears as a navigation app. From the car screen you can search or choose a saved place, compare up to three lettered routes (A, B, C), start guidance and end it with the host's X. The car map is the phone's own map. The native module draws a
/carpage of the web app on the host surface, so cartography, the road-conditions layer, route lines and route letters stay identical to the phone. Lists, search, preview, the routing card and the trip card use Android Auto's host templates, which is what Google requires for driving apps.Guidance runs through the trip the phone already manages: the same session, reroutes, notification card and recovery. The car is another view of that trip, not a second navigation engine. The routing card, lane strip and instrument cluster use the shared core helpers (
maneuverIconName,presentGuidanceLanes), so turn arrows and lanes match the phone banner. The trip's existing navigation notification is extended for the car through the navigation-surfaces hook from #773, so no second card is posted. The map controls follow the host's map action strip: pan, zoom and recenter, plus a speed bubble while driving. Rows in the route list are lettered to match the labels on the map.While navigating, the road-conditions overlay now shows only events within 1 km of the route (
isNearRoute), on the phone and on the car alike. Outside navigation the overlay stays complete. Play reviewers can trigger Android Auto's test-drive intent, which replays a fixed drive in Berlin wherever they are, because openmapx.com's routing and search cover Germany only. The Test drive button on the car screen exists in debug builds only.The car path reuses shared code instead of reimplementing it in Kotlin or in the car page: distance rounding, geocoding, simulated positions, route framing and letter placement, and the turn-type mapping. The Android Auto developer page documents the architecture, the car-surface protocol, Desktop Head Unit testing and the release gates.
Related work
Refs #406 (Android Auto part; CarPlay is not included). Builds on #773 (navigation surfaces) and #775 (AbortSignal in React Native).
Verification
pnpm lint(Biome, Prettier, translations),pnpm check-types, commitlint.apps/mobiletests and type check;assert-boundary,mobile:prebuild:check,assert-permissions, inventory andmobile:bundle:check;openmapx-android-auto(car state, mapping, session mailbox) and the navigation-surfaces module;:app:assembleDebug.pnpm test --project web, 626 files and 5,922 tests passed.pnpm test --project nodeand the core navigation tests, including the corridor's bounds for huge or malformed feed geometry.check:policy, production audits, image size, Docker context secrets, ops authority and OpenAPI;pnpm build(including the/carroute), packed packages and the docs build.Not verified: rerouting and the notification tap on this final build; real head units and wireless projection; rotary controllers, including whether the host's end-trip control can be reached with one; hosts older than Android Auto 18.1; Play car review.
Release and upgrade impact
No package release: the only shared-core changes are navigation helpers and formatting exports used by the web app and the mobile shell.
@openmapx/extension-clibuilt fromorigin/mainand from this branch produces byte-identicaldist/files.The Android app gains the Android Auto car-app service and the
androidx.car.apppermissions, which take effect with the next app build. Before store metadata claims Android Auto support, Play car review and real head-unit qualification must both pass (see the developer page). No server, database or operator changes.