Skip to content

Android Auto: live surface QSO map (zoom, borders, status banners, partner location) - #456

Merged
patrickrb merged 5 commits into
devfrom
feat/car-surface-map-pota
Jul 7, 2026
Merged

Android Auto: live surface QSO map (zoom, borders, status banners, partner location)#456
patrickrb merged 5 commits into
devfrom
feat/car-surface-map-pota

Conversation

@patrickrb

@patrickrb patrickrb commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Reworks the Android Auto car screen into a live, Surface-rendered QSO map. Supersedes the earlier static-image approach on this branch.

What it does

  • Live surface map (CarMapSurfaceRenderer) on a NavigationTemplate, drawn each 1 Hz tick directly to the Surface — countdown updates never trigger a template rebuild, so the display no longer dims/flashes. Category is NAVIGATION so the map stays up while driving.
  • Zoom-to-fit (CarMapProjection): world view when idle; zooms and re-centres around the operator + QSO partner when in a QSO so the connection is legible. Clamped [1, 12x] with padding.
  • Basemap detail: world land + national borders (new res/raw/world_countries.json) + US state borders, layered under the markers; US state abbreviations label in once zoomed.
  • Two full-width status banners (CarPanelLayout): live QSO state (headline + slot timer) on top, band + POTA on the bottom, flush against the car's system bars (heights from framework dimens, safe-area fallback), leaving the map centre clear.
  • Partner location next to the slot timer: US state (point-in-polygon over us_states.json, UsStateLocator) when the partner is stateside, otherwise DXCC country (from the callsign database), e.g. RX slot · 7 s · Massachusetts / · Reunion Island.

Testing

  • DEBUG-ONLY DebugInject broadcast receiver + app-automotive host so the car map can be exercised on an Automotive OS emulator without a phone/DHU. Never ships in release.
  • Unit tests: projection framing, panel/banner geometry, GeoJSON label + region parsing, and point-in-polygon lookup. Full testDebugUnitTest passes.

🤖 Generated with Claude Code

patrickrb and others added 4 commits July 7, 2026 08:36
Switch the car screen from PaneTemplate-only to direct Surface rendering
via MapWithContentTemplate (car-app API level 5+). The 1 Hz tick now
redraws the Canvas directly instead of calling invalidate(), which
eliminates the host dimming/flashing on every countdown update.

The surface draws a world map (land outlines from WorldOutlines) with
decoded-station markers and an operator dot, covered by a 75% black
overlay, then status text (headline, TX message, sequence step, slot
countdown, band/mode, POTA activation line) and a TX indicator dot.

Hosts below API level 5 fall back to the original PaneTemplate path
(minCarApiLevel stays at 1 for backward compat).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The POI category added in the previous commit triggers Android Auto's
driving restrictions, blocking the entire screen while in motion. The
IOT category has no such restriction.

MapWithContentTemplate (surface-based rendering) requires POI or
Navigation category, so it can't be used with IOT while driving.
Remove the surface rendering code and fix dimming differently: the tick
now invalidates only at slot boundaries (every 15s for FT8) instead of
every second. LiveData observers still trigger immediate refreshes for
real state changes.

POTA activation line is kept — added as a 4th PaneTemplate row.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Render a 480x240 equirectangular world map as the PaneTemplate hero
image. Shows the operator's position (orange dot) and, during an active
QSO, a line to the partner station (cyan dot). The bitmap is built with
Canvas from WorldOutlines land data — no map templates, no POI category,
works while driving.

The image is cached by (opGrid, partnerCall, partnerGrid) so
onGetTemplate() skips re-rendering when nothing moved. The partner's
grid is looked up from the current decoded message list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ners

Replaces the static QSO map image with a Surface-rendered, full-bleed map on
the Android Auto NavigationTemplate (CarMapSurfaceRenderer), drawn directly each
1 Hz tick so countdown updates never trigger a template refresh (no dimming).

Map:
- Zoom-to-fit framing of the operator and QSO partner (CarMapProjection):
  world view when idle, zoomed in around the two stations when in a QSO, so the
  connection is actually legible. Clamped [1, 12x] with padding.
- World land + national borders (new res/raw/world_countries.json) + US state
  borders, layered under markers; US state abbreviations labelled once zoomed in.

Status:
- Two full-width status banners (CarPanelLayout) — live QSO state (headline +
  slot) on top, band + POTA on the bottom — flush against the car's system bars
  (heights read from framework dimens; safe-area fallback), leaving the map
  centre clear.
- The QSO partner's location rides next to the slot timer: US state (via
  point-in-polygon over us_states.json, UsStateLocator) or DXCC country (from the
  callsign database), e.g. "RX slot 7 s - Massachusetts" / "- Reunion Island".

Testing:
- DEBUG-ONLY DebugInject broadcast receiver (src/debug) + app-automotive host so
  the car map can be exercised on an Automotive OS emulator without a phone/DHU.
- Unit tests for the projection, panel/banner geometry, GeoJSON label + region
  parsing, and point-in-polygon lookup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@patrickrb
patrickrb changed the base branch from main to dev July 7, 2026 17:43
@patrickrb patrickrb changed the title Android Auto: surface-based map, fix dimming, show POTA count Android Auto: live surface QSO map (zoom, borders, status banners, partner location) Jul 7, 2026
@patrickrb
patrickrb requested a review from Copilot July 7, 2026 18:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reworks the Android Auto experience from a template-driven status pane to a Surface-rendered, navigation-category live map screen, adding map projection/zoom framing, richer basemap borders/labels, and partner-location resolution (US state via point-in-polygon or DXCC country via callsign DB). It also adds a debug-only injection path to exercise the car map in an AAOS emulator and introduces unit tests for the new geometry/parsing/lookup utilities.

Changes:

  • Switch Android Auto to a NavigationTemplate + SurfaceCallback rendering path with a 1 Hz direct redraw loop (fallback to PaneTemplate on older hosts).
  • Add map rendering infrastructure (projection, renderer, panel layout, surface state) plus GeoJSON parsing for country/state borders, labels, and US state point-in-polygon lookup.
  • Add debug-only emulator harness (receiver + AAOS host activity) and expand unit test coverage for the new pure components.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
ft8af/app/src/testDebug/kotlin/radio/ks3ckc/ft8af/car/DebugInjectTest.kt Unit tests for debug-inject extras parsing.
ft8af/app/src/test/kotlin/radio/ks3ckc/ft8af/ui/map/StateLabelsParseTest.kt Tests for state label parsing/centroid logic.
ft8af/app/src/test/kotlin/radio/ks3ckc/ft8af/ui/map/NamedRegionLookupTest.kt Tests for point-in-polygon and named-region lookup.
ft8af/app/src/test/kotlin/radio/ks3ckc/ft8af/car/CarQsoStatusTest.kt Adds coverage for POTA line formatting and PSK spot marker mapping.
ft8af/app/src/test/kotlin/radio/ks3ckc/ft8af/car/CarPanelLayoutTest.kt Tests banner geometry for the surface-rendered status bars.
ft8af/app/src/test/kotlin/radio/ks3ckc/ft8af/car/CarMapProjectionTest.kt Tests for world and fit-to-points projection behavior/clamping.
ft8af/app/src/test/kotlin/radio/ks3ckc/ft8af/car/CarAppManifestWiringTest.kt Updates manifest expectation to NAVIGATION category.
ft8af/app/src/main/res/values/strings_compose.xml Adds a POTA line string resource for car UI.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/ui/map/WorldOutlines.kt Adds country borders loading, state labels parsing, and US state locator utilities.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/pskreporter/WhoHeardMeCache.kt Introduces a process-wide PSKReporter “who heard me” spot cache.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/QsoStatusScreen.kt Implements surface-based navigation screen, PSK fetch loop, and frame building.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/CarSurfaceState.kt Defines immutable per-frame state snapshot for the renderer.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/CarQsoStatus.kt Adds POTA formatting and PSK spot-to-marker mapping helpers.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/CarPanelLayout.kt Adds pure banner rectangle calculation helpers.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/CarMapSurfaceRenderer.kt New Surface renderer drawing basemap, borders, labels, markers, and banners.
ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/CarMapProjection.kt New pure projection math for world and fit-to-points views.
ft8af/app/src/main/AndroidManifest.xml Adds car surface permissions and switches car app category to NAVIGATION.
ft8af/app/src/debug/kotlin/radio/ks3ckc/ft8af/car/DebugInject.kt Debug-only broadcast receiver to inject synthetic car-map engine state.
ft8af/app/src/debug/AndroidManifest.xml Debug manifest overlay registering injection receiver and AAOS host activity.
ft8af/app/build.gradle Bumps androidx.car.app dependency and adds debug-only app-automotive.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/QsoStatusScreen.kt Outdated
Comment thread ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/QsoStatusScreen.kt Outdated
Comment thread ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/QsoStatusScreen.kt Outdated
Comment thread ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/QsoStatusScreen.kt Outdated
Comment thread ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/CarSurfaceState.kt Outdated
Comment thread ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/CarMapSurfaceRenderer.kt Outdated
Comment thread ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/QsoStatusScreen.kt Outdated
Comment thread ft8af/app/src/main/kotlin/radio/ks3ckc/ft8af/car/CarQsoStatus.kt Outdated
Fixes eight review findings on the Android Auto surface map path:

- Guard checkIsCQ() against a null callsignTo: it dereferences
  callsignTo unconditionally, so a decode with a null To field would
  crash the 1 Hz surface tick. buildStationMarkers now gates the call.
- Dedupe station markers by the normalized getCallsignFrom() (strips the
  < > a hashed decode carries) instead of the raw callsignFrom field, so
  a hashed decode no longer produces a duplicate marker.
- Match the current QSO partner on getCallsignFrom() too, so a hashed
  partner decode still renders its line/location.
- Cache the empty fallback when a geo asset fails to load, so a
  missing/corrupt asset does not re-throw the parse on every frame.
- Localize the POTA line: buildCarPotaLine now returns a CarStringSpec
  resolved against the (previously unused) @string/car_pota_line.
- Correct stale KDoc/comments that still referenced MapWithContentTemplate
  and host-rendered status cards; the screen uses NavigationTemplate and
  draws the status banners on the surface itself.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014B1egpdNWafvAksJCn1tMA
@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 27.95139% with 415 lines in your changes missing coverage. Please review.
✅ Project coverage is 21.65%. Comparing base (635ce27) to head (08a6d94).

Files with missing lines Patch % Lines
...n/kotlin/radio/ks3ckc/ft8af/car/QsoStatusScreen.kt 0.00% 171 Missing ⚠️
...in/radio/ks3ckc/ft8af/car/CarMapSurfaceRenderer.kt 0.00% 157 Missing ⚠️
.../kotlin/radio/ks3ckc/ft8af/ui/map/WorldOutlines.kt 69.04% 27 Missing and 12 partials ⚠️
...debug/kotlin/radio/ks3ckc/ft8af/car/DebugInject.kt 40.74% 29 Missing and 3 partials ⚠️
...n/kotlin/radio/ks3ckc/ft8af/car/CarSurfaceState.kt 26.66% 11 Missing ⚠️
.../radio/ks3ckc/ft8af/pskreporter/WhoHeardMeCache.kt 0.00% 3 Missing ⚠️
.../kotlin/radio/ks3ckc/ft8af/car/CarMapProjection.kt 97.56% 0 Missing and 1 partial ⚠️
...main/kotlin/radio/ks3ckc/ft8af/car/CarQsoStatus.kt 83.33% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##                dev     #456      +/-   ##
============================================
+ Coverage     21.45%   21.65%   +0.19%     
- Complexity      142      147       +5     
============================================
  Files           156      162       +6     
  Lines         20310    20868     +558     
  Branches       2990     3126     +136     
============================================
+ Hits           4357     4518     +161     
- Misses        15785    16165     +380     
- Partials        168      185      +17     
Flag Coverage Δ
android 13.11% <27.95%> (+0.62%) ⬆️
native 9.93% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...in/kotlin/radio/ks3ckc/ft8af/car/CarPanelLayout.kt 100.00% <100.00%> (ø)
.../kotlin/radio/ks3ckc/ft8af/car/CarMapProjection.kt 97.56% <97.56%> (ø)
...main/kotlin/radio/ks3ckc/ft8af/car/CarQsoStatus.kt 94.20% <83.33%> (-1.04%) ⬇️
.../radio/ks3ckc/ft8af/pskreporter/WhoHeardMeCache.kt 0.00% <0.00%> (ø)
...n/kotlin/radio/ks3ckc/ft8af/car/CarSurfaceState.kt 26.66% <26.66%> (ø)
...debug/kotlin/radio/ks3ckc/ft8af/car/DebugInject.kt 40.74% <40.74%> (ø)
.../kotlin/radio/ks3ckc/ft8af/ui/map/WorldOutlines.kt 67.51% <69.04%> (+6.22%) ⬆️
...in/radio/ks3ckc/ft8af/car/CarMapSurfaceRenderer.kt 0.00% <0.00%> (ø)
...n/kotlin/radio/ks3ckc/ft8af/car/QsoStatusScreen.kt 0.00% <0.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@patrickrb
patrickrb merged commit 2540a24 into dev Jul 7, 2026
17 checks passed
@patrickrb
patrickrb deleted the feat/car-surface-map-pota branch July 7, 2026 19:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants