Skip to content
This repository was archived by the owner on Aug 25, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c06de11
Android Auto: add the app-projected artifact so the host can actually…
alltechdev Jul 23, 2026
f9ead6e
Port upstream Android Auto: full car-side nav, snapshotter map, Robot…
alltechdev Jul 23, 2026
381a3d7
AGENTS: state the app-projected finding at its proven strength
alltechdev Jul 23, 2026
4bbc76d
SPEC: the Android Auto section describes the ported full car-side stack
alltechdev Jul 23, 2026
54a910f
AA: in-car voice search + free-text NAVIGATE geocoding (fork additions)
alltechdev Jul 23, 2026
be9d231
AA: mic on the landing screen, one shared voice flow
alltechdev Jul 23, 2026
4689fd6
AA: full turn-by-turn feature batch (all fork-original)
alltechdev Jul 23, 2026
79a5fee
AA: real dark map at night (the phone's palette, not a tint)
alltechdev Jul 23, 2026
ac2b63f
AA: stop manager, place details, traffic overlay, 2 more categories
alltechdev Jul 23, 2026
12a7cab
Goldens: search + settings phases on the AA feature branch, all legs …
alltechdev Jul 23, 2026
5a50b97
Merge main (Black theme #94) into the AA branch
alltechdev Jul 23, 2026
5879e86
AA: fix all 10 verified review findings
alltechdev Jul 23, 2026
fc1b4bf
AA: mic restored on the landing strip, colorful categories, tap-to-re…
alltechdev Jul 23, 2026
22800d7
AA: no attribution stamp on the car map; ODbL credit anchored in About
alltechdev Jul 23, 2026
1ccaad4
AA: landing mic moved to the strip tail
alltechdev Jul 23, 2026
ca573eb
AA: landing mic fixed for real (handshake race) + no auto keyboard
alltechdev Jul 23, 2026
996af99
AA: voice-to-results + mic icon renamed to dodge stale host icon cache
alltechdev Jul 23, 2026
fdbbd0d
AA: one shared mic - every mic entry opens the search surface
alltechdev Jul 23, 2026
979423a
AA: drop the dead per-screen voice plumbing detekt flagged
alltechdev Jul 23, 2026
469b2e5
AA: the mic obeys the phone's voice-engine preference, no exceptions
alltechdev Jul 23, 2026
459cb9b
AA: Vela-voice path polished - session warm-up + honest listening toast
alltechdev Jul 23, 2026
5243f3d
AA: car-mic silence falls back to the phone mic, still Vela voice
alltechdev Jul 23, 2026
b3231a4
AA: SYSTEM-mode mic tap lands with the voice affordance up
alltechdev Jul 23, 2026
7ee5d14
AA voice: fix all 10 round-2 review findings
alltechdev Jul 23, 2026
e3cdc6c
AA voice: no dead mic buttons - LOCAL-only mic, honest SYSTEM story
alltechdev Jul 23, 2026
d6a705f
docs: head-unit screenshots of the AA experience (user-captured)
alltechdev Jul 23, 2026
6174af9
AA: in-drive search offers the categories under the bar
alltechdev Jul 23, 2026
8537f4e
AA: pause map frame production under covering templates - fixes tearing
alltechdev Jul 23, 2026
772f588
docs: in-drive search with the category rows, tearing-free (user-capt…
alltechdev Jul 23, 2026
298fb56
AA: car activity joins search and place history (port gap)
alltechdev Jul 23, 2026
e5606fe
Merge main into port/android-auto-full-nav
alltechdev Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 62 additions & 17 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1281,23 +1281,68 @@ state - upstream's own 13ac02e8 already made the layers panel a VelaMenu):
- The one seam is `core/data/MapDataSource`. `MockMapDataSource` is the default
and keeps the entire app usable offline; `google/GoogleMapsDataSource` is the
real scraper.
- **Android Auto (`app/car/`).**
- `VelaCarAppService` is a NAVIGATION-category templated `CarAppService` (manifest service +
`xml/automotive_app_desc.xml` + the two `androidx.car.app.*` permissions + application-level
`minCarApiLevel=1`); a sideload appears in the car launcher only with AA developer "Unknown
sources" on, hence `HostValidator.ALLOW_ALL_HOSTS_VALIDATOR`.
- `CarMapScreen` is the whole car UI: NavigationTemplate (Re-center / + / − action strip;
RoutingInfo card with `NavSession.state.maneuverText` + distance while navigating) over a map
surface.
- **The MapLibre-on-car trick: SurfaceCallback surface → `DisplayManager.createVirtualDisplay` →
`Presentation` → plain `MapView`** (MapLibre can't draw to a raw surface). It reuses
`applyDark`/`applyLight` from VelaMapView (made `internal` for this) keyed to
`carContext.isDarkMode`, has its OWN AOSP LocationManager listener for the puck (works with the
phone UI closed), and draws the route from `NavSession.state.route.polyline`.
- Pan/zoom arrive as `onScroll`/`onScale` and move the camera by hand (projection math -
`MapLibreMap.scrollBy` isn't a thing in 11.x).
- The PHONE runs nav (MapViewModel feeds NavSession) and speaks; the car is a display. Car-side
search/route-start is a follow-up; untested on a real head unit yet.
- **Android Auto (`app/car/`) - FULL car-side nav (ported upstream ac487f78 + c409ad21 +
679119d1 + a2510700 + 381cfd49, 2026-07-23).**
- `VelaCarAppService` is a Hilt `@AndroidEntryPoint` NAVIGATION-category `CarAppService`
injecting the same `:core` singletons the phone uses (NavSession, LocationProvider,
MapDataSource, the three place stores, VoiceGuide, RouteEngine), bundled into `CarDeps` for
the screens. **`app-projected` merges the `CarAppMetadataHolderService`
projection glue and is the documented Android Auto artifact** - the first-cut port lacked it,
and this fork's builds were not listed on the reporting head unit (OnePlus 12) until it was
added. NB the causality is not fully isolated (the AA app-list rescan happened in the same
step), and upstream's interim builds may never have had the issue - the artifact is REQUIRED
regardless (NavigationManager / cluster APIs live in it). `HostValidator.ALLOW_ALL_HOSTS_VALIDATOR` stays (sideload-only
distribution).
- Screens (`car/screen/`): `MainCarScreen` (Home/Work/recents/saved rows, capped at the
template's MAX_ROWS, de-duped by location) -> `SearchCarScreen` (car keyboard search via
MapDataSource) -> `RoutePreviewCarScreen` (alternates + live ETAs) -> `ActiveNavCarScreen`
(maneuver card + lanes + trip updates to the cluster via NavigationManager). The PHONE still
runs NavSession and speaks; `VelaCarSession.onNewIntent` handles assistant/`geo:` NAVIGATE
intents (opaque geo URIs must not go through getQueryParameter - upstream's c409ad21 crash).
- **Two fork additions upstream does not have (2026-07-23):** free-text NAVIGATE destinations
(`geo:0,0?q=central park`) geocode via `MapDataSource.search` into the route preview
(upstream returns null and just opens; coord parsing is matchEntire so "Store 12, 34th St"
is a query, not lat 12 lng 34); and **in-car voice search** - `CarVoiceSearch` feeds the
car's mic (`CarAudioRecord`, Car API 5+, 16 kHz) through `WhisperRecognizer.listen`'s
`PcmSource` seam, so the mic actions on `MainCarScreen` and `SearchCarScreen` (shared flow in `CarVoiceSearch.micAction`) run the same on-device VAD +
Whisper pipeline as the phone. THE ENGINE PREFERENCE IS LAW (2026-07-23 rework): mic taps
branch on `VoiceSearch.resolvedMode` - LOCAL runs CarAudioRecord->Whisper with a
silent-car-mic phone-mic retry (peak-level discriminated), SYSTEM opens the search surface
(its in-field host mic IS the system recognizer; template apps cannot start host voice
sessions). Transcript commands dispatch via `VoiceCommandRouter` AT THE CALLER, never from a
pushed screen's init (constructor args evaluate before the caller's push - anything pushed
from init gets buried; review-verified). RECORD_AUDIO via `carContext.requestPermissions`.
- **Fork batch 2 (2026-07-23), all fork-original:** in-drive search-along-route
(`SearchCarScreen(alongRoute=true)` - corridor filter via `RouteCorridor`, pick calls
`NavSession.addStop`), `CategoriesCarScreen` (GridTemplate; labels localize, queries stay the
stable English literals - same rule as the phone chips), `CarCommands` offline voice grammar
in `:core` (unit-tested; parse-don't-guess: only EXACT keywords command, everything else
searches with the nav verb stripped), faster-route head-up `Alert` (API 5+, one per candidate
key), `ArrivalCarScreen` (save parking -> `ParkingStore`, shared with phone Park) + Find-my-car
landing row + live Home/Work ETA subtitles, resume-into-guidance on session create (guarded on
`!arrived` - an arrived-but-undismissed session must not bounce screens), and `DriveAlerts`
(Settings -> Navigation -> Driving alerts; camera heads-up reads the BUNDLED FlockCameras set
along the route - offline, one announce per camera per trip; speeding = red badge at 5% + 2
km/h over `speedLimitKmh`). Settings rows live in the NAVIGATION spoke (not a new spoke).
- **`CarMapRenderer` draws the REAL styled map via MapLibre's `MapSnapshotter`** (off-screen ->
Bitmap -> Canvas onto the template surface; ONE shared renderer per session - a second
instance never receives onSurfaceAvailable, so screens switch its MODE instead). Nav mode is
heading-up with look-ahead camera, speed-tightened zoom, traffic-coloured route and a speed
badge; browse is north-up. A smoothing ticker glides puck/bearing between ~1 Hz fixes.
The old first-cut VirtualDisplay+Presentation+MapView path is GONE with it.
- **`MapFonts` (ported with it) patches Liberty's glyphs to Roboto app-wide** (phone map +
snapshotter): fetches the LIVE style JSON, rewrites `glyphs` to the self-hosted
Roboto-over-Noto set, caches as `file://`; probe-guarded so an unreachable font host degrades
to plain Noto, and stale caches evict so a pinned tile snapshot can't go blank. The glyph
host currently defaults to UPSTREAM's GitHub Pages (`MAP_FONTS_URL`, `-PmapFontsUrl=`
override) - mirroring the `map-fonts` release onto this repo's Pages is on ROADMAP.
- **Recents are ONE chronological timeline** (`RecentPlaceStore`/`RecentSearchStore` reshaped,
upstream 381cfd49): typed `RecentPlace`/`RecentQuery` entries mixed by time in the search
overlay (pin vs clock icon) and reused by `MainCarScreen`. The fork's D-pad rings on those
rows are kept through the merge.
- Verification: host gates + unit tests only - there is still NO device gate for AA (the flip
phone cannot project). Real-car testing via the user's head unit; a DHU-based harness phase
is the standing follow-up.
- **Settings is HUB-AND-SPOKE (2026-07-22), and the hub ORDER is deliberate:** a short category
list (`ui/settings/SettingsHub.kt`) where each row opens its own sub-screen
(`ui/settings/sections/*.kt`), in this order: Appearance (theme/map style/units/language) →
Expand Down
10 changes: 10 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ android {

// Restricted build flavor flag (see productFlavors below). false everywhere except
// the `restricted` flavor, which overrides it to true.
// Self-hosted map-font glyphs (Roboto composited over Noto; ui/map/MapFonts). Defaults to
// UPSTREAM's GitHub Pages glyph host for now (the probe guard degrades safely to Noto if
// it is unreachable); mirroring the map-fonts release onto this repo's Pages is on ROADMAP.
// Same local-test override pattern as the manifests: -PmapFontsUrl=http://127.0.0.1:8099.
buildConfigField(
"String",
"MAP_FONTS_URL",
"\"${(project.findProperty("mapFontsUrl") as String?) ?: "https://pimpinpumpkin.github.io/Vela/fonts"}\"",
)
buildConfigField("boolean", "RESTRICTED", "false")

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down Expand Up @@ -319,6 +328,7 @@ dependencies {
// is the Android Auto artifact). The first-cut port shipped only the base :app template
// library, which is why Vela has never appeared in an AA launcher (device report, OnePlus 12).
implementation(libs.androidx.car.app.projected)
testImplementation(libs.androidx.car.app.testing) // car-template unit tests

debugImplementation(libs.androidx.compose.ui.tooling)
}
12 changes: 12 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- AAOS only: read the vehicle's own speed via CarHardware instead of GPS-derived speed.
No-op on projected Android Auto. (Ported upstream ac487f78.) -->
<uses-permission android:name="android.car.permission.CAR_INFO" />

<!-- Haptic turn cues during navigation (normal permission, auto-granted). -->
<uses-permission android:name="android.permission.VIBRATE" />
Expand Down Expand Up @@ -141,6 +144,15 @@
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.NAVIGATION" />
<!-- Render turn-by-turn on the instrument cluster too (fed by NavigationManager.updateTrip). -->
<category android:name="androidx.car.app.category.FEATURE_CLUSTER" />
</intent-filter>
<!-- Accept a navigation intent (assistant "navigate to X" / another app's geo: URI) and go
straight to the route preview. Handled in VelaCarSession.onNewIntent. -->
<intent-filter>
<action android:name="androidx.car.app.action.NAVIGATE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="geo" />
</intent-filter>
</service>
</application>
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/app/vela/VelaApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ class VelaApp : Application(), coil.ImageLoaderFactory {
app.vela.data.FlockCameras.refresh(this@VelaApp, app.vela.BuildConfig.FLOCK_MANIFEST_URL)
}
app.vela.ui.SimLocation.init(this)
app.vela.ui.VoiceSearch.init(this) // voice-search toggle + engine/provider prefs (mic in the search bar)
app.vela.ui.VoiceSearch.init(this)
app.vela.ui.map.MapFonts.init(this) // Roboto basemap glyphs (cached patched style + async refresh) // voice-search toggle + engine/provider prefs (mic in the search bar)
app.vela.ui.LiveReviews.init(this)
app.vela.ui.ShowReviews.init(this)
app.vela.ui.LoadPhotos.init(this)
Expand Down
Loading
Loading