Skip to content

feat(map): mesh view pictures, track direction and time fade, barometric height - #7719

Merged
jamesarich merged 12 commits into
mainfrom
feat/mesh-view
Oct 21, 2026
Merged

jamesarich merged 12 commits into
mainfrom
feat/mesh-view

Conversation

@jamesarich

@jamesarich jamesarich commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

The mesh view (#7659) shows where a node's track went but not which way, fades it by point order rather than time, and its own height bobs with every GPS fix. This makes tracks and traceroutes read at a glance on the camera, steadies the phone's height with the barometer, and lets the view be saved and opened from a link.

🌟 New Features

  • Camera button in the mesh view's top bar saves the view as shown (camera and overlay) to Pictures/Meshtastic. The viewfinder renders embedded so a window copy holds the feed.
  • Sparks run along tracks (oldest to newest) and traceroute legs (hop by hop), spaced by angle in the world so they stay on the path while the phone turns. They keep moving at a few ticks a second on a hot phone.
  • Track summary in the bottom panel (12 points over 3h 20m · newest 5 min. ago), also what TalkBack reads, plus an empty-window message and age tags on the oldest fix and one near the middle of a long track.
  • Tracks fade by the time of each fix across the track's own span, falling back to order when a time is missing, on the Google map, the MapLibre map and the mesh view.
  • Deep links: /nodes/{n}/mesh-view, /nodes/{n}/position/mesh-view and /map/mesh-view.
  • Phone height averages like the position, weighted by vertical accuracy, and fuses with the barometer where there is one: pressure carries each change, GPS settles the level over a minute.

🐛 Bug Fixes

  • Track legs ride an outline in the opposite shade, so a dark node color shows against a dark scene.
  • Long track legs step along the ground instead of cutting a chord, and legs at the viewer rise from the bottom of the view like route links.

🛠️ Refactoring & Architecture

  • One TrackFade in feature:map replaces four copies of the fade (both maps, the mesh view and its radar).
  • saveJpegToPictures in core:ui androidMain is shared by the AR device viewer and the mesh view.
  • PhonePressureProvider with an Android barometer source and a desktop no-op.

Testing Performed

  • TrackFadeTest, TrackLineStopsTest, AltitudeFuserTest: new.
  • HudPathsTest: summary, label picks, viewer legs, sparks by angle, ground stepping.
  • FixAveragerTest: height averaging. DeepLinkRouterTest: the three mesh view links.
  • On a Pixel 6a in Demo Mode with a seeded track: picture saves from the map and track views, time fade on the MapLibre track map, sparks on a hot phone, deep links cold and warm, barometer registered while the view is open. Not seen on device: traceroute sparks (Demo Mode has no traceroute response) and the Google flavor's track gradient.

Summary by CodeRabbit

  • New Features

    • Added deep links for opening the mesh HUD from maps, nodes, and position views.
    • Added mesh HUD track summaries, localized labels, animated route effects, and camera capture on Android 10+.
    • Added barometer-assisted altitude estimates for improved location accuracy.
    • Added desktop support and graceful handling on platforms without camera or barometer access.
  • Improvements

    • Track fading now reflects position recency and distance for more consistent visualization.
    • Improved altitude averaging using vertical accuracy.
  • Bug Fixes

    • Improved JPEG capture cleanup and save reliability.

The viewfinder renders embedded so a window copy holds the camera feed and the overlay together.
… leaves the viewer from the bottom

The bottom panel sums the track up and says when the window holds no positions; legs at the viewer draw from the bottom of the view, as route links do.
…mesh view

One fade in feature:map, measured across the track's own span and falling back to order when a time is missing, replaces four copies that faded by order.
…, or the whole mesh

`/nodes/{n}/mesh-view`, `/nodes/{n}/position/mesh-view` and `/map/mesh-view`, each over the screens its button lives on.
A dark node color vanished against a dark scene, worst on the faint old end of a track.
… direction of travel

As in the Chirpy Hop backdrop: sparks spaced by length on screen, flowing oldest to newest along a track and hop by hop along each traceroute leg. They hold still while the phone is hot.
… follow the ground

Sparks were spaced by length on screen, so turning the phone slid them along the path; they now sit by angle along it, fewer and slower. A long track leg is stepped along the ground instead of cutting a chord between its ends.
… a second

Frozen sparks only shifted as the view turned, which read as motion tied to the camera.
GPS height now averages like the position, weighted by vertical accuracy. Where the phone has a barometer, pressure carries every change in height and GPS settles the level over a minute.
riseM moves next to its only caller to keep HudGeometry under the function cap, and the track gradient notes why its stops are spread.
@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

📝 Walkthrough

This change updates timestamp-based track fading and HUD path rendering, adds phone barometer altitude fusion, enables Android HUD capture, centralizes JPEG saving, and adds mesh-view deep-link routes.

Changes: Track rendering

Layer / File(s) Summary
Shared track recency and map fading
feature/map/.../TrackFade.kt, feature/map/.../TrackFadeTest.kt, androidApp/.../MapView.kt, feature/map-maplibre/.../NodeTrackMap.kt, feature/map/.../HudRadarPaths.kt, feature/map-maplibre/.../TrackLineStopsTest.kt
Shared timestamp-based recency drives track alpha across Android map, MapLibre, and HUD radar rendering.
HUD track data and path geometry
feature/map/.../HudPaths.kt, feature/map/.../HudPathSampling.kt, feature/map/.../HudGeometry.kt, feature/map/.../HudPathsTest.kt
HUD tracks retain fix timestamps and provide summaries, labels, viewer-leg selection, ground-path sampling, and spark positions.
HUD track and route display
feature/map/.../HudPathsDraw.kt, feature/map/.../MeshHudScreen.kt, feature/map/.../HudPanels.kt, core/resources/.../strings.xml, .skills/compose-ui/strings-index.txt
HUD paths use recency fades, casings, animated sparks, and fix-age labels. The bottom panel displays track summaries.

Phone pressure and altitude fusion

Layer / File(s) Summary
Pressure provider contract and platform bindings
feature/map/.../PhonePressureProvider.kt, feature/map/.../AndroidPhonePressureProvider.kt, desktopApp/.../CompassStubs.kt, desktopApp/.../DesktopStubsModule.kt, feature/map/.../MeshHudViewModelTest.kt
The pressure-flow interface has Android sensor and desktop no-op implementations.
Altitude fusion and location updates
feature/map/.../AltitudeFuser.kt, feature/map/.../FixAverager.kt, feature/map/.../MeshHudViewModel.kt, feature/map/.../AltitudeFuserTest.kt, feature/map/.../FixAveragerTest.kt
AltitudeFuser combines pressure-derived altitude with GPS altitude. FixAverager weights reported heights by vertical accuracy, and MeshHudViewModel applies pressure readings.

HUD and AR image capture

Layer / File(s) Summary
Shared JPEG saving
core/ui/.../PicturesStore.kt, androidApp/.../ArCapture.kt
PicturesStore saves JPEGs to Pictures and removes pending rows when writing or publishing fails. AR scene capture uses the shared utility.
HUD capture API and platform wiring
feature/map/.../HudCapture.kt, feature/map/.../HudCapture.android.kt, feature/map/.../HudCapture.ios.kt, feature/map/.../HudCapture.jvm.kt, feature/map/.../HudCameraPreview.android.kt, feature/map/.../HudPanels.kt, feature/map/.../MeshHudScreen.kt
Android HUD capture copies the activity window with PixelCopy and saves the image. The iOS and JVM implementations return no capture action.

Mesh-view deep links

Layer / File(s) Summary
Mesh-view route matching and validation
core/navigation/.../DeepLinkRouter.kt, core/navigation/.../DeepLinkRouterTest.kt
Deep-link routing supports whole-mesh, node-focused, and node-position mesh views. Tests cover route stacks and invalid node identifiers.

Priority: ⬇️ Low

Merge Risk: 🔵 Low · up to 08072

In a narrow location-data case, the mesh view can report inconsistent heights. Clear the MSL height when it cannot be recalculated; this is a bounded issue rather than a demonstrated merge blocker.

Pre-merge checks: Passed 6 · Failed 2

Failed Failed checks: 2 warnings

Check name Status Explanation Resolution
Tests Prove The Path, Not The End State Warning Several added tests are non-discriminating. HudPathsTest.a short leg stays a straight chord between its ends asserts only groundDirections(a, b).size == 2 (line 228), so arbitrary two directions p… Strengthen the short-leg test by asserting both returned directions equal the computed endpoints. In the phase test, assert non-empty output and the expected spark bearings for both phases. In the sea-level test, assert the expected pressur…
Regression Coverage For Changed Behavior Warning Regression coverage remains incomplete. The changed production paths have helper tests, but the observable integrations and failure branches remain untested: - PicturesStore.saveJpegToPictures (39-6… Add the Android host, sensor, common, map-consumer, drawing/frame-clock, and Compose UI tests above. Keep assertions for every listed failure branch, timestamp fallback, stale-fix branch, platform branch, and Google/MapLibre renderer integr…
Passed Passed checks: 6 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title accurately summarizes the main changes: mesh-view picture capture, track direction and time-based fading, and barometric height support.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Sibling Call Sites And Presence Semantics Passed PASS. The diff does not touch NodeItem or NodeItemCompact, and it does not change ambient-temperature presence semantics. TrackPoint changes ageSec to nullable fixTimeSec without changing nullability;…
Moved Code Diffed Against Its Original Passed The only clear code move is saveToPictures from ArCapture.kt to PicturesStore.kt. Its @RequiresApi contract, MediaStore setup, JPEG quality, title sanitization, pending-row cleanup, exception …
Full details: Sibling Call Sites And Presence Semantics

Explanation

PASS. The diff does not touch NodeItem or NodeItemCompact, and it does not change ambient-temperature presence semantics. TrackPoint changes ageSec to nullable fixTimeSec without changing nullability; all TrackPoint consumers use fixTimeSec. PhoneLocation altitude and mslAltitude were already nullable. No new RSSI, temperature, current, voltage, particulate-concentration, or app-level nullable SNR field defaults to 0.

Full details: Moved Code Diffed Against Its Original

Explanation

The only clear code move is saveToPictures from ArCapture.kt to PicturesStore.kt. Its @RequiresApi contract, MediaStore setup, JPEG quality, title sanitization, pending-row cleanup, exception handling, and nullable return behavior are preserved line-for-line. captureArScene still calls it on the I/O dispatcher, and the new HUD caller also uses the I/O dispatcher. The renamed public utility is an explicit part of the shared core:ui design, not a silent move defect. No old caller remains on the removed declaration, and no listed annotation, validation, nullability, coroutine-policy, or interface change was introduced by the move.

Full details: Tests Prove The Path, Not The End State

Explanation

Several added tests are non-discriminating. HudPathsTest.a short leg stays a straight chord between its ends asserts only groundDirections(a, b).size == 2 (line 228), so arbitrary two directions pass. a phase one spacing on puts every spark back where it was compares two results but never asserts that either result contains sparks (lines 209-215), so an empty-output implementation passes. AltitudeFuserTest.the sea level height moves with the fused one checks only mslAltitude == altitude - 25 (line 86); a fuser that ignores the pressure change still satisfies this relation. No changed test asserts emission order under UnconfinedTestDispatcher, and no added test seeds a backing store and only reads the value back.

Resolution

Strengthen the short-leg test by asserting both returned directions equal the computed endpoints. In the phase test, assert non-empty output and the expected spark bearings for both phases. In the sea-level test, assert the expected pressure-shifted altitude and MSL altitude (approximately 155 m and 130 m), in addition to their relationship.

Full details: Regression Coverage For Changed Behavior

Explanation

Regression coverage remains incomplete. The changed production paths have helper tests, but the observable integrations and failure branches remain untested: - PicturesStore.saveJpegToPictures (39-62), captureWindow (58-106), and captureArScene (41-52) can regress in MediaStore metadata, JPEG compression, publish, cleanup, PixelCopy failure, zero-size windows, API-level gating, context unwrapping, and single-flight capture. No matching Android tests exist. Add host tests with a fake ContentResolver for success, insert/open/compress/update failures, and pending-row deletion. Add capture tests for API < Q, invalid windows, PixelCopy failure, ContextWrapper, and repeated taps. - AndroidPhonePressureProvider.pressureUpdates (30-49) and MeshHudViewModel pressure integration (234-247) have no sensor or controllable-flow tests. The current ViewModel test only supplies NoBarometer (86-88). Add Android sensor-fake tests for missing sensors, registration, emitted values, cancellation, and unregister. Add ViewModel tests for pressure updates, fused-height propagation, and suppression of altitude-only changes below 0.25 m. - AltitudeFuser.calibrated has an explicit stale/out-of-order branch (101-108), but AltitudeFuserTest does not exercise it. Add a newer-fix then older-fix test and assert that the offset and fused height remain unchanged. FixAverager.meanHeight uses the new missing-vertical-accuracy fallback (95-105), but the tests cover only known accuracy and missing height. Add two height-bearing fixes with one missing verticalAccuracyM and assert the fallback weighting. - Google NodeTrackOverlay (1456-1530) and MapLibre pointFeatures/TrackLineLayer (209-278) now consume timestamp recency. TrackFadeTest and TrackLineStopsTest test helpers only. Add consumer tests for old, widely separated, newest, missing, zero, and invalid timestamps, including Google marker opacity/z-index and segment gradients and MapLibre point opacity and line-gradient stops. - drawTrack, drawRoute, and MeshHudScreen now render casings, alpha, viewer-origin paths, directional sparks, and normal/hot frame scheduling (HudPathsDraw 119-311; MeshHudScreen 187-199 and 262-280). HudPathsTest covers path-sampling helpers only. Add recording draw-command tests for direction, casing, per-leg alpha, viewer paths, and route/track sparks. Add a Compose frame-clock test for path open/close and normal versus hot tick cadence. - HudTopBar, HudBottomPanel, TrackLegend, and their screen wiring (HudPanels 138-180, 204-243, 349-377; MeshHudScreen 335-351) have no Compose UI tests. Add tests for the conditional camera button and callback, empty and summarized tracks, accessibility text, and suppression of the no-nodes message while a track is active.

Resolution

Add the Android host, sensor, common, map-consumer, drawing/frame-clock, and Compose UI tests above. Keep assertions for every listed failure branch, timestamp fallback, stale-fix branch, platform branch, and Google/MapLibre renderer integration.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added desktop Desktop target enhancement New feature or request labels Oct 21, 2026
@jamesarich
jamesarich marked this pull request as ready for review October 21, 2026 20:06
@jamesarich

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: meshtastic/Meshtastic-Android/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d6de7680-9330-41db-af12-cd66b4106a3d
📥 Commits

Reviewing files that changed from the base of the PR and between e70ed95 and ebd7496.

📒 Files selected for processing: 34
  • .skills/compose-ui/strings-index.txt
  • androidApp/src/google/kotlin/org/meshtastic/app/map/MapView.kt
  • androidApp/src/google/kotlin/org/meshtastic/app/node/ArCapture.kt
  • core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/DeepLinkRouter.kt
  • core/navigation/src/commonTest/kotlin/org/meshtastic/core/navigation/DeepLinkRouterTest.kt
  • core/resources/src/commonMain/composeResources/values/strings.xml
  • core/ui/src/androidMain/kotlin/org/meshtastic/core/ui/util/PicturesStore.kt
  • desktopApp/src/main/kotlin/org/meshtastic/desktop/di/DesktopStubsModule.kt
  • desktopApp/src/main/kotlin/org/meshtastic/desktop/stub/CompassStubs.kt
  • feature/map-maplibre/src/commonMain/kotlin/org/meshtastic/feature/map/maplibre/NodeTrackMap.kt
  • feature/map-maplibre/src/commonTest/kotlin/org/meshtastic/feature/map/maplibre/TrackLineStopsTest.kt
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/hud/AndroidPhonePressureProvider.kt
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/hud/HudCameraPreview.android.kt
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/hud/HudCapture.android.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/TrackFade.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/AltitudeFuser.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/FixAverager.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/HudCapture.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/HudGeometry.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/HudPanels.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/HudPathSampling.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/HudPaths.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/HudPathsDraw.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/HudRadarPaths.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/MeshHudScreen.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/MeshHudViewModel.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/PhonePressureProvider.kt
  • feature/map/src/commonTest/kotlin/org/meshtastic/feature/map/TrackFadeTest.kt
  • feature/map/src/commonTest/kotlin/org/meshtastic/feature/map/hud/AltitudeFuserTest.kt
  • feature/map/src/commonTest/kotlin/org/meshtastic/feature/map/hud/FixAveragerTest.kt
  • feature/map/src/commonTest/kotlin/org/meshtastic/feature/map/hud/HudPathsTest.kt
  • feature/map/src/commonTest/kotlin/org/meshtastic/feature/map/hud/MeshHudViewModelTest.kt
  • feature/map/src/iosMain/kotlin/org/meshtastic/feature/map/hud/HudCapture.ios.kt
  • feature/map/src/jvmMain/kotlin/org/meshtastic/feature/map/hud/HudCapture.jvm.kt

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

Comment thread feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/AltitudeFuser.kt Outdated
…ix has none

A tap during a save is ignored, a window that loses its surface reports a failed save instead of crashing, and a fix without a GPS height keeps the barometer height on the last level.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository: meshtastic/Meshtastic-Android/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 95142259-2f8d-4546-8e71-b815860ae7d4
📥 Commits

Reviewing files that changed from the base of the PR and between ebd7496 and 08072c6.

📒 Files selected for processing: 3
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/hud/HudCapture.android.kt
  • feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/hud/AltitudeFuser.kt
  • feature/map/src/commonTest/kotlin/org/meshtastic/feature/map/hud/AltitudeFuserTest.kt
🚧 Files skipped from review as they are similar to previous changes: 2
  • feature/map/src/commonTest/kotlin/org/meshtastic/feature/map/hud/AltitudeFuserTest.kt
  • feature/map/src/androidMain/kotlin/org/meshtastic/feature/map/hud/HudCapture.android.kt

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

@jamesarich
jamesarich added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit 986ed80 Oct 21, 2026
17 checks passed
@jamesarich
jamesarich deleted the feat/mesh-view branch October 21, 2026 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

desktop Desktop target enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant