Refresh Rippr snapshot and bundle with full project documentation
Re-exported at 46a0726, which adds README.md plus docs/ARCHITECTURE, DEVELOPMENT, TESTING, v1 history including the original brief, and a v3 backlog. 112 files, and the bundle now carries 19 commits. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
116
rippr-src/README.md
Normal file
116
rippr-src/README.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# 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 `<trkseg>`.
|
||||
|
||||
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`.
|
||||
Reference in New Issue
Block a user