Skip to content

fix(location): persist self-marker across screen-off + instant fix on foreground (#75)#87

Merged
jfuginay merged 2 commits into
mainfrom
fix/75-self-marker-persistence
Jun 11, 2026
Merged

fix(location): persist self-marker across screen-off + instant fix on foreground (#75)#87
jfuginay merged 2 commits into
mainfrom
fix/75-self-marker-persistence

Conversation

@jfuginay

Copy link
Copy Markdown
Contributor

Closes #75 — self-marker disappears after screen-off; slow to reappear on resume.

Root cause

TacticalMap.kt's lifecycle observer disables the MapLibre LocationComponent on ON_PAUSE (added to kill the compass animator that crashed against a detached style on Android 16) — but nothing ever re-enabled it on ON_RESUME. After screen-off/on, the puck stayed hidden until the composable happened to be rebuilt (nav away + back, basemap change). Two aggravators made the reacquire slow and the cold-start case worse:

  • LocationProvider's fix was memory-only — process death (Doze kill) meant no position at all until fused delivered, and the cold-start lastLocation path suppresses anything older than 5 min, so the marker sat empty while GPS reacquired.
  • UserPrefs.selfLat/selfLon (the broadcaster's designed persisted-position fallback since GAP-030b) had no writer anywhere — the persistence half of the design was never wired.

iOS parity note (issue comment): iOS holds the puck through screen-off via its background-location entitlement. Android mirrors the intent with zero new permissions: persist the last fix, render it immediately on resume (stale-marked), and force a refresh on foreground.

What changed

Piece Change
TacticalMap.kt (root cause) ON_RESUME re-enables the LocationComponent, restoring follow-me's camera mode. New selfFix param feeds the puck via forceLocationUpdate (verified unconditional in MapLibre 11.8.0 bytecode), so a restored fix renders the instant the map is up. Puck renders dimmed (45% alpha, no pulse) while the fix is >30 s old, and staleStateTimeout(30s) + a registered stale image make live-GPS-loss dim identically. Puck options re-applied after basemap swaps so the style-bound bitmaps survive reloads.
UserPrefs.kt selfHae + selfFixTimeMs join selfLat/selfLon; setLastSelfFix() writer.
SelfFixPersistence.kt (new) Pure-JVM policy: 30 s staleness, 15 s persist throttle, restore mapping (restored fixes carry NaN accuracy → ce=9999999, zero speed — never masquerade as live GPS), newer-wins merge.
LocationProvider.kt seedFromPersisted(), requestImmediateFix() (fused cache + single-shot getCurrentLocation), and a newer-wins write gate so the seed can never clobber a live fix (and a fresher fused cache upgrades a stale seed).
OmniTAKApp.kt Cold-start seed from DataStore, then throttled persist of real fixes (seed-then-collect in one coroutine — no race, no self-re-persist).
MainActivity.kt Existing issue-#6 ON_RESUME observer also fires requestImmediateFix().
SelfPositionBroadcaster.kt Prefs fallback now carries persisted hae (and is finally reachable, since the prefs have a writer).

No manifest or permission changes — foreground lifecycle + DataStore only (Play review window respected).

Test evidence

  • ./gradlew assembleDebug — green
  • ./gradlew testDebugUnitTest300 tests, 0 failures (JDK 21)
    • new SelfFixPersistenceTest — 19 cases: restore round-trip (incl. NaN sentinels + never-claims-live-confidence), 30 s staleness boundary, persist throttle, newer-wins merge
    • SelfPositionBroadcasterFixSourcingTest — new broadcasts_persisted_position_when_fix_null locks the persisted-prefs PPLI fallback (lat/lon/hae + honest ce=9999999); the GAP-030b no-SF regressions still pass

Remaining verification

Per repo convention, on-device side-by-side verification (screen-off → resume on hardware, plus an iOS/Android composite for the parity check) remains before this ships in a release build — flagging for the next device session.

🤖 Generated with Claude Code

jfuginay and others added 2 commits June 11, 2026 14:44
… foreground (#75)

Root cause: TacticalMap's ON_PAUSE handler disables the MapLibre
LocationComponent to silence its compass animator (Android 16 crash),
but nothing ever re-enabled it on ON_RESUME — after screen-off/on the
self-marker stayed hidden until the composable was rebuilt, and after
process death there was no persisted fix to render while GPS reacquired.

- TacticalMap ON_RESUME now re-enables the puck (follow-me-aware camera
  mode) so the marker survives screen-off in-process
- Persist the last real fix (lat/lon/hae/time) to DataStore, throttled
  to one write per 15 s — UserPrefs selfHae/selfFixTimeMs join the
  existing selfLat/selfLon, which finally gain a writer
- Seed LocationProvider from the persisted fix on cold start so the 2D
  puck, Cesium self entity, HUD card, and PPLI prefs-fallback all render
  immediately; the puck renders dimmed (45% alpha, no pulse) while the
  fix is older than 30 s, matching MapLibre's own stale-state timeout
- Force an immediate fused refresh (cache + single-shot
  getCurrentLocation) on every foreground ON_RESUME instead of waiting
  for the next passive interval tick
- Newer-wins gate in LocationProvider so a restored fix can never
  clobber live GPS
- No manifest/permission changes — foreground lifecycle + DataStore only

Tests: 19-case SelfFixPersistenceTest (restore round-trip, staleness
boundary, persist throttle, newer-wins) + persisted-prefs PPLI fallback
case in SelfPositionBroadcasterFixSourcingTest. assembleDebug +
testDebugUnitTest green (300 tests, 0 failures).

Closes #75

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jfuginay jfuginay merged commit 42dd101 into main Jun 11, 2026
jfuginay added a commit that referenced this pull request Jun 11, 2026
… Cesium viewport handoff

- versionName 0.35.2 -> 0.35.3, versionCode 90 -> 91
- carries #86 (Cesium camera inherits 2D viewport, #78) and #87
  (self-marker survives screen-off + instant fix on resume, #75) —
  both from r/ATAK tester feedback, same-day turnaround

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

Self-marker disappears after screen-off; slow to reappear on resume

1 participant