1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
# Map Interaction Design
_2026-04-03_
## Overview
Enable free map pan/zoom while keeping GPS auto-follow as the default. When the user gestures on the map, the UI hides and a Recenter button appears. Tapping Recenter restores auto-follow and the full UI.
## State
A single `isFollowing: Boolean` flag owned by `MapHandler`. Starts `true`. This is the only piece of new state.
## Entering Manual Mode
`MapLibreMap.addOnCameraMoveStartedListener` fires with `REASON_API_GESTURE` when the user initiates a pan, pinch-zoom, or rotate. On that event:
- `MapHandler.isFollowing` → `false`
- `MapHandler` emits via a new `StateFlow<Boolean> isFollowing` exposed to MainActivity
- `MapHandler.centerOnLocation()` becomes a no-op while `!isFollowing` — GPS updates still arrive, last position is stored
## Returning to Auto-Follow
`fab_recenter` click handler:
- Calls `MapHandler.recenter()` — sets `isFollowing = true`, calls `centerOnLocation(lastLat, lastLon)`
- `isFollowing` StateFlow emits `true` → MainActivity restores UI
## UI Changes
### New element: `fab_recenter`
- Pill-shaped button (`wrap_content` width, 40dp height, 20dp corner radius)
- Label: "⊙ Recenter"
- Position: centered horizontally, `24dp` above the bottom of the map `ConstraintLayout`
- `android:visibility="gone"` by default
- Elevation: 20dp (above the instrument sheet)
### Visibility toggling (MainActivity)
When `isFollowing` → `false`:
- Fade out (alpha 0, 150ms): `instrument_bottom_sheet`, `bottom_navigation`, `fab_mob`, `fab_record_track`
- Show `fab_recenter` (visibility VISIBLE, fade in 150ms)
When `isFollowing` → `true`:
- Hide `fab_recenter` (fade out 150ms, then GONE)
- Fade in (alpha 1, 150ms): `instrument_bottom_sheet`, `bottom_navigation`, `fab_mob`, `fab_record_track`
## Files to Change
| File | Change |
|------|--------|
| `MapHandler.kt` | Add `isFollowing` StateFlow, `lastLat`/`lastLon` storage, `recenter()`, guard in `centerOnLocation()`, register `OnCameraMoveStartedListener` |
| `activity_main.xml` | Add `fab_recenter` pill button |
| `MainActivity.kt` | Observe `mapHandler.isFollowing`, animate UI in/out, wire `fab_recenter` click |
## Out of Scope
- Tapping the map (without gesture) to restore UI — not requested
- Timeout to auto-restore UI — not requested
- Zoom-level persistence across recenter — not requested
|