Add Rippr source snapshot and full-history bundle
Two copies for two jobs. rippr-src/ is a browsable git archive export of the tracked tree at 2f76983 - no build outputs, no local.properties, no nested .git - which is convenient to read in gitea but carries no history and will drift. rippr-full-history.bundle is the real backup: all 18 commits, verified as "records a complete history" and test-cloned before committing. This matters because ~/dojo/rippr has no git remote and otherwise exists only on one machine. rippr-src/SNAPSHOT.md explains the difference and how to restore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
112
rippr-src/docs/v2/03-repository.md
Normal file
112
rippr-src/docs/v2/03-repository.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# T03 — Trip repository + reactive state
|
||||
|
||||
**Phase** 1 · **Depends on** T02 · **Status** Done
|
||||
|
||||
## Goal
|
||||
|
||||
Own trip lifecycle transitions in one place and make recording state a function of the
|
||||
database rather than an in-memory flag. Done when `RecordingState` is deleted, the UI
|
||||
observes trip state through a Flow, and killing the app process no longer loses track of
|
||||
whether a ride is in progress.
|
||||
|
||||
## State as of T02
|
||||
|
||||
`TripDao`/`SegmentDao` already expose every query this task needs (`observeActive`, `getActive`, `openSegment`, `close`, `setState`, `deleteById`).
|
||||
|
||||
T02 left **temporary scaffolding** that this task and T06 replace:
|
||||
`TrackingService.openTripAndSegment()` opens a trip and segment inline via the DAOs, and
|
||||
`@Volatile currentTripId/currentSegmentId` are read by the location callback. The callback
|
||||
drops fixes while those are 0 so a point can never violate the FK — **preserve that guard**.
|
||||
|
||||
## Context
|
||||
|
||||
v1 keeps state in a process-wide singleton in
|
||||
`app/src/main/java/com/rippr/Telemetry.kt`:
|
||||
|
||||
```kotlin
|
||||
object RecordingState {
|
||||
private val _isRecording = MutableStateFlow(false)
|
||||
…
|
||||
}
|
||||
```
|
||||
|
||||
This was already an improvement over the Activity-local `isRecording` the original spec
|
||||
used, but it still lies after process death: `START_STICKY` restarts the service with a
|
||||
null intent and the flag resets to `false` while a ride is genuinely underway.
|
||||
|
||||
The fix is that the database already knows. A row in `trips` with `endedAt IS NULL` *is*
|
||||
the fact of an in-progress ride, and it survives anything short of uninstall.
|
||||
|
||||
**Keep the rest of `Telemetry.kt` exactly as it is.** `msToKmh`, `sanitizeSpeedKmh`,
|
||||
`isUsableFix`, `formatDuration`, and `encodeBatch` are pure, already unit-tested by
|
||||
`TelemetryTest`, and have no reason to change. Only `RecordingState` goes.
|
||||
|
||||
`RecordingState.lastUploadError` is also used by `TelemetryUploader` and surfaced on the
|
||||
Record screen — that one is genuinely ephemeral UI state and can stay as a small
|
||||
`UploadStatus` object, or move onto the repository. Do not silently drop it.
|
||||
|
||||
## Design
|
||||
|
||||
```kotlin
|
||||
class TripRepository(
|
||||
private val tripDao: TripDao,
|
||||
private val segmentDao: SegmentDao,
|
||||
private val pointDao: TrackPointDao,
|
||||
) {
|
||||
fun observeActiveTrip(): Flow<Trip?>
|
||||
fun observeTrip(id: Long): Flow<Trip?>
|
||||
fun observeCompletedTrips(): Flow<List<Trip>>
|
||||
|
||||
suspend fun startTrip(now: Long): Trip // creates trip + first segment
|
||||
suspend fun pauseTrip(now: Long) // closes open segment, state=PAUSED
|
||||
suspend fun resumeTrip(now: Long) // opens new segment, state=RECORDING
|
||||
suspend fun completeTrip(now: Long) // closes segment, endedAt, COMPLETED
|
||||
suspend fun discardTrip() // deletes active trip (CASCADE)
|
||||
}
|
||||
```
|
||||
|
||||
Every transition is idempotent and safe to call from an unexpected state — the service
|
||||
can be restarted by the OS at any moment, so `resumeTrip` on an already-recording trip
|
||||
must be a no-op rather than opening a duplicate segment.
|
||||
|
||||
**Single instance.** v1 uses a `@Volatile` double-checked singleton for `AppDatabase`;
|
||||
follow the same pattern for the repository rather than introducing a DI framework. The
|
||||
app is small and Hilt would be more ceremony than it earns.
|
||||
|
||||
## Implementation
|
||||
|
||||
1. Create `data/TripRepository.kt` with the interface above.
|
||||
2. Implement each transition as a single Room `@Transaction` so a crash mid-transition
|
||||
cannot leave a trip with two open segments.
|
||||
3. Guard each transition on current state; log and no-op on nonsensical transitions.
|
||||
4. Delete `RecordingState` from `Telemetry.kt`.
|
||||
5. Relocate upload-error state so the Record screen keeps its indicator.
|
||||
6. Update `TelemetryUploader` and `TrackingService` references.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] `RecordingState` no longer exists
|
||||
- [x] `observeActiveTrip()` reflects reality after a simulated process death
|
||||
- [x] Double `startTrip()` adopts rather than duplicating
|
||||
- [x] `resumeTrip()` while already RECORDING is a no-op
|
||||
- [x] Upload-error surfacing still works, now via `UploadStatus`
|
||||
- [x] `TelemetryTest` still green (the pure functions are untouched)
|
||||
|
||||
## Tests
|
||||
|
||||
Instrumented, against a real in-memory database:
|
||||
- start → pause → resume → complete produces exactly two segments
|
||||
- discard removes the trip and all its points
|
||||
- transitions from wrong states are no-ops
|
||||
- `observeActiveTrip` emits null after complete
|
||||
|
||||
## Risks / gotchas
|
||||
|
||||
- **Transitions race the writer loop.** The service writes points continuously; pausing
|
||||
mid-flush must not orphan points into a closed segment. T06 handles ordering — pause
|
||||
drains the channel *before* closing the segment.
|
||||
- **Do not delete the pure Telemetry functions.** They are load-bearing and tested.
|
||||
|
||||
## Out of scope
|
||||
|
||||
Wiring transitions to service actions (T06) and aggregate maths (T07).
|
||||
Reference in New Issue
Block a user