Files
samplez/rippr-src/README.md
Dylan 3c801e9114 Refresh the native Rippr snapshot to match the claimed commit
RIPPR.md said the native snapshot was at ba57a92, but rippr-src and the bundle
were still at d418920 -- so the superseded notice and the two recorded bugs were
missing from the copy. Re-exported both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 22:38:02 -05:00

7.2 KiB
Raw Permalink Blame History

Rippr — native Android (superseded)

⚠ This app has been ported to Flutter

Active development moved to ~/dojo/rippr-flutter, which runs on Android and iOS. That port is feature-complete and better tested than this app; see its docs/port/PARITY-AUDIT.md for a row-by-row comparison.

This repository is kept as a reference and a fallback, not archived. It is still the only version that has recorded real rides, and the port's validation depends on running both apps side by side — they use different application ids (com.rippr here, com.rippr.port there) specifically so they can be installed together.

Two bugs in this code were found by porting it. Both are fixed in the port and still present here:

  1. TrackingService.restoreAfterProcessDeath measures the dead time as distance. Its comment says it resumes into a new segment; it calls resumeTrip, which adopts the segment a crash left open. RideStatistics.compute then measures straight through the gap — reproduced at 111 km of phantom distance. See TripRepository.resumeIntoNewSegment in the port.
  2. The elevation regression guard passes on seed luck. On a fixture shared between both languages the algorithm yields ~39 m, which would fail this repo's own 35 m bound in RideStatisticsTest. It passes only because kotlin.random.Random(42) draws a benign sequence.

Everything below describes this app as it stands, and remains accurate.


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 Absent in v2; planned for v3
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.

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.

Deliberate absences

No live map while recording — superseded, see docs/v3/BACKLOG.md. v2 shipped without one because the phone rides in a pocket, so a live map would burn battery for something nobody is looking at. That premise no longer holds: handlebar mounting is now a wanted use case. The constraint that survives is architectural — TrackingService holds no reference to any map type, and rendering belongs to a visible screen's Compose lifecycle.

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 Durable design decisions and the reasoning behind them
docs/DEVELOPMENT.md Toolchain setup, build/test commands, emulator harness
docs/TESTING.md What is covered, what is not, and what the emulator cannot verify
docs/v1/ Original spec, what shipped, and how it deviated
docs/v2/ 18-task plan, per-task docs, and the progress log with every bug found
docs/v3/BACKLOG.md Known gaps, deferred work, and ideas for next

Start here for v3: docs/v3/BACKLOG.md, then 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.