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>
99 lines
3.8 KiB
Markdown
99 lines
3.8 KiB
Markdown
# T08 — Navigation shell + ViewModels
|
||
|
||
**Phase** 4 · **Depends on** T01 · **Status** Done
|
||
|
||
## Goal
|
||
|
||
Three navigable destinations, theme extracted out of the Activity, and state moved into
|
||
ViewModels. Done when Record, Trips, and Trip Detail are reachable with a working back
|
||
stack and `MainActivity` no longer owns screen state.
|
||
|
||
## Context
|
||
|
||
`app/src/main/java/com/rippr/MainActivity.kt` (216 lines) currently does everything:
|
||
permission launcher, service intents, theme definition, screen composition, and state.
|
||
That was proportionate for one screen and will not survive three.
|
||
|
||
Existing pieces to preserve rather than rewrite:
|
||
- The permission flow (`RequestMultiplePermissions`, with `POST_NOTIFICATIONS` added only
|
||
on TIRAMISU+) works and should move largely as-is.
|
||
- The battery-optimisation exemption prompt is genuinely useful for an app that must
|
||
survive a multi-hour ride; keep it on the Record screen.
|
||
- `RipprTheme` is currently a private composable applying `darkColorScheme()`. Dylan
|
||
called the UI "simple and clean" and explicitly likes it — **do not redesign it**.
|
||
Extract, do not restyle.
|
||
- `StatRow` and `BigStat` are reusable; move them into a shared `ui/components/`.
|
||
|
||
## Design
|
||
|
||
```
|
||
ui/
|
||
theme/ Theme.kt, Color.kt
|
||
components/ StatRow.kt, BigStat.kt, ConfirmDialog.kt
|
||
record/ RecordScreen.kt, RecordViewModel.kt
|
||
trips/ TripsScreen.kt, TripsViewModel.kt
|
||
detail/ TripDetailScreen.kt, TripDetailViewModel.kt
|
||
RipprNavHost.kt
|
||
```
|
||
|
||
Routes:
|
||
|
||
| Route | Screen |
|
||
|---|---|
|
||
| `record` | Start destination |
|
||
| `trips` | Trip list |
|
||
| `trip/{tripId}` | Detail, `tripId: Long` argument |
|
||
|
||
`navigation-compose` rather than hand-rolled state switching: the back stack from detail
|
||
→ list is worth not writing by hand, and it gives correct behaviour on the system back
|
||
gesture for free.
|
||
|
||
**ViewModels** get the repository through a simple factory — the app already uses a
|
||
`@Volatile` singleton for `AppDatabase` and T03 adds one for `TripRepository`. Hilt would
|
||
be more ceremony than a three-screen app earns.
|
||
|
||
**Navigation from Record → Trips** goes in a top bar action, keeping the Record screen's
|
||
centre free for the numeric readout.
|
||
|
||
## Implementation
|
||
|
||
1. Extract `ui/theme/` from `MainActivity`.
|
||
2. Move `StatRow` / `BigStat` into `ui/components/`, unchanged.
|
||
3. Create `RipprNavHost` with the three routes.
|
||
4. Add a ViewModel per screen with a shared factory.
|
||
5. Reduce `MainActivity` to: permissions, battery exemption, `setContent { RipprTheme { RipprNavHost() } }`.
|
||
6. Move service-intent dispatch into `RecordViewModel`.
|
||
|
||
## Acceptance criteria
|
||
|
||
- [x] All three destinations reachable; system back works from detail
|
||
- [x] `MainActivity` holds no screen state
|
||
- [x] Visual appearance of the Record screen is unchanged
|
||
- [x] Permission flow and battery-optimisation prompt still function
|
||
- [ ] Config change (rotation) does not lose state
|
||
|
||
> **Not verified.** The unchecked items above, and the Compose UI tests below, were
|
||
> not done. Verification for T08–T11 was manual on the emulator (screenshots through
|
||
> the full flow) plus the existing unit and instrumented suites. Compose UI tests are
|
||
> outstanding — tracked in T18.
|
||
|
||
## Tests
|
||
|
||
Instrumented Compose UI tests:
|
||
- Navigate record → trips → detail → back → back
|
||
- Rotation preserves screen state
|
||
- Existing suites stay green
|
||
|
||
## Risks / gotchas
|
||
|
||
- **Do not restyle.** The brief was "simple and clean, I like that". This task is
|
||
structural only.
|
||
- **`tripId` argument type.** Room ids are `Long`; declare `NavType.LongType` explicitly
|
||
or it silently arrives as a String.
|
||
- The v1 UI reads state via `collectAsStateWithLifecycle` already — keep that, it is the
|
||
correct choice over `collectAsState` for lifecycle-aware collection.
|
||
|
||
## Out of scope
|
||
|
||
Screen content beyond wiring (T09, T10, T11); any map (T13).
|