# Rippr A native Android GPS ride recorder for motorcycles. Records telemetry from an un-killable foreground service into a local SQLite database, renders the traversed path on a map afterwards, and exports to GPX/GeoJSON. Built for one specific use: **hit start, put the phone in a pocket, ride, hit stop.** Every design decision below follows from that. ``` Package com.rippr minSdk 26 (Android 8.0) targetSdk 35 Kotlin 2.0.21 AGP 8.7.3 Gradle 8.11.1 ~5,800 lines Kotlin 84 unit tests 46 instrumented tests ``` ## Current state — v2.0.1 | Feature | Status | |---|---| | GPS recording via foreground service | Working, validated on real rides | | Trips with pause/resume/stop/discard | Working | | Live speed + elapsed clock | Working (fixed in 2.0.1) | | Path rendered on OpenStreetMap | Working (fixed in 2.0.1) | | Ride statistics + charts | Working; elevation gain has known drift | | Rename / delete / merge rides | Working | | GPX + GeoJSON export | Working, validated against an external parser | | Upload to a REST endpoint | Implemented, **no UI** — see below | | Live map during recording | Deliberately absent — see below | | Group ride view | Deferred to v3 | ## Quick start Requires **JDK 17–21** (AGP does not support 25) and the Android SDK with platform 35 and build-tools 35. Full setup in [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md). ```bash echo "sdk.dir=$HOME/Library/Android/sdk" > local.properties ./gradlew assembleDebug testDebugUnitTest lintDebug ./gradlew connectedDebugAndroidTest # needs a device or emulator ``` ## Architecture in one page ``` TrackingService (foreground) └─ FusedLocation callback ──trySend──► unbounded Channel │ single writer coroutine (batches of 25, ~2s) │ ┌───────────────────┴────────────────────┐ ▼ ▼ Room database Accumulator Trip → Segment → TrackPoint distance / moving time / │ elevation, folded per batch ▼ TripRepository (all lifecycle transitions) │ ┌─────────────────┼──────────────────┐ ▼ ▼ ▼ RecordScreen TripsScreen TripDetailScreen └─ RideMap (osmdroid) ``` Three decisions carry most of the weight: **The location callback never blocks.** Fixes go into an unbounded `Channel`; a single writer coroutine drains it in batches. Slow disk stalls the writer, never GPS, and no fix is dropped under back pressure. **Recording state lives in the database, not memory.** A row in `trips` with `endedAt IS NULL` *is* the fact of an in-progress ride. It survives process death, which an in-memory flag cannot — `START_STICKY` restarts the service with a null intent and a flag would come back `false` mid-ride. **Segments make pause correct rather than cosmetic.** Without them, pausing at a gas station and resuming across town draws a straight line through terrain never ridden, and counts it as distance. Segments propagate all the way through: distance accumulation, map polylines, and GPX ``. Full reasoning in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). ## Two deliberate absences **No live map while recording.** The phone is in a pocket; nobody is looking at it. A live map would burn battery on top of GPS and a wake lock for nothing. The map is a post-ride artifact, gated behind a toggle, and `TrackingService` holds no reference to any map type. **No UI for the upload endpoint.** The REST uploader works and is tested, but is only reachable via `Config.setUploadEndpoint()`. It has no server to talk to yet; the UI arrives when group-ride streaming does. ## Documentation | Document | Contents | |---|---| | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Durable design decisions and the reasoning behind them | | [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) | Toolchain setup, build/test commands, emulator harness | | [docs/TESTING.md](docs/TESTING.md) | What is covered, what is not, and what the emulator cannot verify | | [docs/v1/](docs/v1/) | Original spec, what shipped, and how it deviated | | [docs/v2/](docs/v2/) | 18-task plan, per-task docs, and the progress log with every bug found | | [docs/v3/BACKLOG.md](docs/v3/BACKLOG.md) | Known gaps, deferred work, and ideas for next | **Start here for v3:** [docs/v3/BACKLOG.md](docs/v3/BACKLOG.md), then [docs/v2/PROGRESS.md](docs/v2/PROGRESS.md) for the bugs that were found and why. ## Known gaps 1. **Elevation gain drifts** ~30 m per ten stationary minutes against synthetic noise. Real GPS error is correlated rather than uniform, so the true figure needs a real ride. Flat ground should read near zero. 2. **No Compose UI tests** for any of the six screens. Verification was manual. 3. **Speed colouring on the map is unvalidated** — the emulator reports zero velocity, so the path renders uniformly there. 4. **Migrations are now mandatory.** `fallbackToDestructiveMigration()` was removed in v2; any schema change must ship a `Migration` against `app/schemas/com.rippr.data.AppDatabase/2.json`.