Files
bincio-rec/CLAUDE.md
T
2026-06-03 00:05:35 +02:00

128 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# bincio-rec
GPS activity recorder for the bincio ecosystem.
Companion to **bincio-autarchive** (archive/sync app): rec makes the data, autarchive stores and syncs it.
---
## Concept
A focused, minimal recording app for cycling, running, hiking, and walking.
Records GPS track + BLE sensor data, saves to GPX, optionally uploads directly to a bincio-activity server.
Future: live navigation / turn-by-turn instructions.
The app does one thing well: **record**. No browsing, no analysis — that's autarchive's job.
---
## Ecosystem
```
bincio-rec → writes GPX files to shared storage
bincio-autarchive → imports GPX, stores locally, syncs to server
bincio-activity → server: parses GPX, stores activities, serves API
bincio-auth → auth service: issues JWTs for all bincio apps
```
bincio-rec can also upload directly to bincio-activity via `/api/upload/raw` (same endpoint autarchive uses), with instance URL + API token configured in settings.
---
## Tech Stack
**Expo Prebuild** (not managed Expo, not bare RN).
- Managed Expo ruled out: BLE requires native modules
- Bare RN ruled out: Expo library ecosystem is valuable (location, notifications, file system)
- Prebuild gives full native access while keeping Expo tooling and EAS Build
Key libraries:
- `expo-location` + `expo-task-manager` — background GPS recording
- `react-native-ble-plx` — BLE sensors (HR, power, cadence)
- `@maplibre/maplibre-react-native` — live track map, future navigation
- `expo-notifications` — km milestone alerts, future navigation cues
- `expo-keep-awake` — keep screen on during recording (user-toggleable)
- `expo-file-system` — write GPX/JSON to shared storage
- `expo-sqlite` — local DB for past recordings list
Target: **Android first**, iOS second.
---
## v1 Screens
1. **Sensor pairing** — scan BLE, pair HR monitor / power meter / cadence sensor; persisted
2. **Recording** — start / pause / resume / stop
- Live stats: elapsed time, distance, current+avg speed, HR, power, cadence, elevation gain
- Map with track drawn in real time
- Keep-awake toggle
3. **Post-recording summary** — stats overview, title field, save / discard
4. **Saved recordings** — list of past sessions; tap to export GPX or upload to server
5. **Settings** — bincio instance URL + API token; km notification toggle; upload format
---
## Output Format
**GPX with Garmin trackpoint extensions** — one `<trkpt>` per second with:
- lat/lon/elevation from GPS
- `<gpxtpx:hr>` — heart rate
- `<gpxtpx:power>` — power (watts)
- `<gpxtpx:cad>` — cadence (rpm)
bincio-activity already parses this format via `bincio.extract.parsers`.
Files saved to a user-accessible location (iOS Files app / Android shared storage) so bincio-autarchive can import them manually.
---
## Known Platform Concerns
- **Android battery optimization**: aggressive OEMs (Xiaomi, Samsung, Huawei) kill background tasks. Prompt user to whitelist bincio-rec in battery settings on first launch. Every recording app has this problem — no framework-level fix.
- **iOS background location**: requires `location` background mode in `Info.plist` and "Always" location permission. Fine for sideloading/TestFlight; App Store submission requires clear justification.
- **iOS foreground notifications**: notification sounds are suppressed when app is in foreground — use `expo-av` audio cue instead for km alerts while screen is on.
---
## Out of Scope (v1)
- Navigation / turn-by-turn routing
- Auto-pause (stop lights, etc.)
- Structured workouts / intervals
- Offline maps
- Live tracking / sharing
---
## v1 Completion Plan
Scaffold is done (all screens, navigation, store, services, GPX, DB, upload).
Items below are what remains before v1 is shippable.
### 1 — Quick fixes (each < 30 min)
- [ ] **Keep-awake toggle**`useKeepAwake()` in `RecordingScreen` is unconditional; wire it to `keepAwake` from the store so the toggle actually works
- [ ] **Sensor button on Recording screen** — no entry point to the SensorPairing modal; add a BLE icon button in the header or stats area that calls `nav.navigate('SensorPairing')`
- [ ] **GPS pause/resume**`pause()` / `resume()` only change store state; also call `stopGpsRecording()` / `startGpsRecording()` so the background task actually pauses
### 2 — BLE (half day)
- [ ] **Android runtime permissions**`BLUETOOTH_SCAN` + `BLUETOOTH_CONNECT` require explicit permission requests on Android 12+; add a `requestBlePermissions()` call in `SensorPairingScreen` before scanning
- [ ] **Cadence CSC parsing**`subscribeCadence()` in `ble.ts` is stubbed; implement stateful CSC Measurement parsing (track previous crank revolution count + timestamp, compute RPM from delta)
- [ ] **BLE persistence** — save paired device IDs + types to AsyncStorage on connect; on app start, attempt to reconnect to previously paired devices automatically
### 3 — Km notifications (half day)
- [ ] **Permission request** — call `Notifications.requestPermissionsAsync()` on first launch
- [ ] **Milestone tracker** — track last notified km in the GPS background task (or store); fire a notification each time `distanceMeters` crosses a new km boundary, gated on the `kmNotifications` setting from AsyncStorage
### 4 — Android battery optimization prompt (12 hours)
- [ ] On first launch, detect if the app is affected by battery optimization (`expo-intent-launcher`) and show a one-time prompt directing the user to whitelist bincio-rec; persist dismissal in AsyncStorage so it only shows once
### 5 — Map (12 days)
- [ ] **Initialize MapLibre** — call `MapLibreGL.setAccessToken(null)` at app start (or configure a tile provider in Settings); add a tile source (e.g. OpenFreeMap / self-hosted)
- [ ] **Replace placeholder** — swap the grey `<View>` in `RecordingScreen` with a `<MapLibreGL.MapView>` + `<MapLibreGL.Camera>` that follows current location
- [ ] **Live track polyline** — subscribe to `trackPoints` from the store and render a `<MapLibreGL.ShapeSource>` + `<MapLibreGL.LineLayer>` that updates as points arrive