Files
bincio-rec/CLAUDE.md
T
Davide Scaini 765efe288e feat: section 4 — Android battery optimization prompt
One-time prompt on first launch (Android only) directing the user to
exclude bincio-rec from battery optimization. Uses the direct
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS system dialog with a fallback to
the general settings page for OEMs that block the direct intent.
Dismissal persisted in AsyncStorage so prompt never repeats.
2026-06-03 09:09:51 +02:00

132 lines
6.4 KiB
Markdown

# 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 ✅
- [x] **Keep-awake toggle** — wired to `keepAwake` store state via `activateKeepAwakeAsync` / `deactivateKeepAwake`; toggle button visible in controls bar
- [x] **Sensor button on Recording screen** — "⚡ Sensors" button overlaid on map area navigates to SensorPairing modal
- [x] **GPS pause/resume**`handlePause` calls `stopGpsRecording()`, `handleResume` calls `startGpsRecording()`
- [x] **Track view**`TrackView` component renders recorded lat/lon points as a scaled SVG polyline on a dark background (no tile server needed); replaces the grey placeholder
### 2 — BLE ✅
- [x] **Android runtime permissions**`requestBlePermissions()` requests `BLUETOOTH_SCAN` + `BLUETOOTH_CONNECT` on Android 12+ (API 31+) before scanning
- [x] **Cadence CSC parsing** — stateful parsing in `parseCscMeasurement()`: tracks previous crank revolution count + event time (uint16, 1/1024 s), computes RPM from delta with uint16 rollover handling
- [x] **BLE persistence**`savePairedDevice` / `loadPairedDevices` / `removePairedDevice` in `ble.ts` via AsyncStorage; SensorPairingScreen shows saved sensors at top, auto-attempts reconnect on mount, Forget button removes a device
### 3 — Km notifications ✅
- [x] **Permission request**`requestNotificationPermissions()` called in `App.tsx` on mount; foreground notification handler set at module level so iOS shows banners while app is active
- [x] **Milestone tracker** — module-level `runningDistanceMeters` / `lastNotifiedKm` / `prevPoint` in `gps.ts`, reset on each `startGpsRecording()`; each incoming location adds haversine distance and fires a notification when a new km is crossed, gated on the `kmNotifications` AsyncStorage setting
### 4 — Android battery optimization prompt ✅
- [x] `src/services/batteryOptimization.ts` — Android-only, one-time prompt (dismissed flag in AsyncStorage); uses `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` intent to open the system dialog for bincio-rec directly; falls back to `IGNORE_BATTERY_OPTIMIZATION_SETTINGS` (general page) on OEMs that block the direct intent; called from `App.tsx` on mount alongside notification permission request
### 5 — Map (optional upgrade)
The track view from section 1 already shows the GPX polyline scaled to fit the screen.
MapLibre can be added later for a real basemap (streets/terrain), but is not required for v1.
- [ ] **MapLibre basemap** — replace `TrackView` SVG with `<MapLibreGL.MapView>` + a tile source (e.g. OpenFreeMap); call `MapLibreGL.setAccessToken(null)` for raster-free usage
- [ ] **Camera follow** — add `<MapLibreGL.Camera>` that follows `trackPoints[last]` during recording
- [ ] **Line layer** — replace SVG polyline with `<MapLibreGL.ShapeSource>` + `<MapLibreGL.LineLayer>`