765efe288e
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.
132 lines
6.4 KiB
Markdown
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>`
|