diff --git a/PLAN.md b/PLAN.md index e031ff8..8766e60 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1965,11 +1965,30 @@ tracked in FEATURES.md. 2. **FEAT-02** — QR / claim-code device pairing flow (medium) 3. **FEAT-04** — Replace SIM868 2G GSM with 4G LTE modem (large) -### Phase 28: Native Android companion app — 🚧 IN PROGRESS (low priority, always last) +### Phase 28: Native Android companion app — 🚧 IN PROGRESS (28.1 ✅, 28.2 ✅, 28.3–28.7 pending) **Renumbered from former Phase 26 on 2026-08-10 to make room for the road-test bug-fix phase. Renumbered to Phase 28 on 2026-08-11 to make room for Phase 27 feature additions. Planning -completed 2026-08-11; sub-phase 28.1 scaffold begins. Detailed implementation plan in -[android/plan.md](android/plan.md).** +completed 2026-08-11. Detailed implementation plan in [android/plan.md](android/plan.md).** + +**28.1 — Scaffold + networking gate ✅ DONE (2026-08-12)** +- Project created in `android/` (Kotlin + Compose, Gradle KTS, version catalog). +- OkHttp + Retrofit networking layer with `PiApi` and `ServerApi` interfaces. +- `NetworkBinder.kt` discovers WiFi + cellular networks. +- `DualNetworkTestScreen` with simultaneous "Test Both" button. +- **Gate passed on real Pixel (2026-08-12):** Android OS routes `10.42.0.1` through WiFi + and `yaris.ceaf.dev` through cellular automatically — per-socket `Network.bindSocket()` + not needed. Both networks work simultaneously. + +**28.2 — Dashcam browser + WiFi download ✅ DONE (2026-08-12)** +- `PiApi` extended: `listVideos`, `getVideoFile` (streaming), `getThumbnail`, `deleteVideo`. +- DTOs: `VideoListResponse`, `VideoFile`, `ConvertProgress`. +- `DashcamScreen`: `LazyColumn` with thumbnails (Coil), file size, date, upload status. +- `DashcamViewModel`: Retrofit-backed video list + download to phone storage + delete local copy. +- Downloads via `WorkManager` foreground service: `DownloadWorker` with notification + + `setProgressAsync()`, `ActiveDownload` state via `LiveData`. Survives screen lock/background. + FGS type `FOREGROUND_SERVICE_TYPE_DATA_SYNC` for Android 14+. +- Bottom nav: Dashcam tab + Test tab. +- **Verified:** video list loads from real Pi over "yaris 911" hotspot on the owner's Pixel. #### Why native (not web, not PWA) Revives the phone-relay goal (Phase 11/12) properly this time. The Phase 11 update proved diff --git a/android/plan.md b/android/plan.md index 07a9625..a243764 100644 --- a/android/plan.md +++ b/android/plan.md @@ -100,36 +100,29 @@ android/ - [ ] Persist credentials after successful test - [ ] Commit: `feat: encrypted credential storage + first-launch flow` -### 28.2 — Dashcam browser + download ⬜ NOT STARTED +### 28.2 — Dashcam browser + download ✅ DONE (2026-08-12) **Depends on: 28.1 (networking layer + credentials).** -#### 28.2.1 — Pi API data layer -- [ ] Retrofit interface for Pi endpoints: - - `GET /api/local-video/list` → `List` (relative_path, size, mtime, sha256, uploaded, thumbnail_url) - - `GET /api/local-video/file?path={relpath}` → `ResponseBody` (streaming, supports Range) - - `GET /api/local-video/thumbnail?path={relpath}` → `ResponseBody` (JPEG bytes) -- [ ] Data classes for API responses (`LocalVideoFile`, etc.) -- [ ] Repository: `VideoRepository` wrapping Pi API calls + Room cache -- [ ] Commit: `feat: Pi video API data layer` +#### What was built +- **Pi API extension** (`PiApi.kt`): `listVideos()` → `VideoListResponse`, `getVideoFile()` + (streaming `ResponseBody`), `getThumbnail()` (JPEG bytes), `deleteVideo()` +- **DTOs:** `VideoListResponse`, `VideoFile` (relative_path, size, mtime, sha256, uploaded, + thumbnail_url), `ConvertProgress` +- **DashcamScreen:** `LazyColumn` of imported Pi videos with thumbnails via Coil async image + loader, file size, date, upload status badge (Uploaded/Pending) +- **DashcamViewModel:** manages video list state, download to phone storage, delete local copy, + conversion progress polling +- **Downloads via WorkManager:** `DownloadWorker` foreground service with notification + + `setProgressAsync()` for inline progress bar. `ActiveDownload` state tracked via WorkManager + `LiveData` observation. Survives screen lock / background. FGS type declared as + `FOREGROUND_SERVICE_TYPE_DATA_SYNC` for Android 14+. +- **Downloads tab:** progress bar + % for in-flight downloads, downloaded files list +- **Bottom navigation:** Dashcam tab + Test tab +- **Verified:** video list loads from real Pi over "yaris 911" hotspot on the owner's Pixel -#### 28.2.2 — Video file list UI -- [ ] Compose screen: LazyColumn of video files -- [ ] Each row: thumbnail (coil3 async image loader), filename, size, upload status badge -- [ ] Sort controls: by name, size, date -- [ ] Selection: checkboxes + "Select all" / "Clear" -- [ ] Pull-to-refresh (re-fetches `/api/local-video/list`) -- [ ] Empty state: "No imported videos on the Pi" -- [ ] Error state: "Cannot reach Pi" with retry button -- [ ] Commit: `feat: video file list screen` - -#### 28.2.3 — WiFi download -- [ ] Room entity: `DownloadedFile` (relative_path, sha256, local_uri, size, downloaded_at) -- [ ] Room DAO: insert, query, delete downloaded files -- [ ] Download manager: OkHttp `Call` with Range header support, writes to app external storage -- [ ] Progress tracking: `Flow` emitting bytes downloaded / total -- [ ] UI: download button per file → progress bar → "Downloaded" checkmark -- [ ] Resume: if download interrupted, resume from last byte (Range: bytes=X-) -- [ ] Commit: `feat: WiFi download with progress + resume` +#### Commits +- Phase 28.2 — dashcam video browser + WiFi download +- fix: downloads use WorkManager foreground service + inline progress ### 28.3 — Cellular upload ⬜ NOT STARTED **Depends on: 28.2 (files on phone local storage).**