IMPLEMENTATION_PLAN.md 20 KB

CarDash implementation plan

Last updated: 2026-10-29

1. What we are building

CarDash is a Flutter/Dart Android home-screen launcher for a landscape car tablet. It deliberately trades the reference head-unit chrome for maximum compatibility:

  • One home page. No top bar, no bottom dock, no pages.
  • Clock/date, battery/charging and Wi-Fi on the page.
  • A prominent now-playing card: song-progress ring around play/pause, scrolling title/artist, volume slider, −/+, mute.
  • Up to 16 home buttons: Navigation, Music, Radio, All apps, plus apps pinned from All Apps. Settings lives next to Wi-Fi in the top-right status area, outside the grid.
  • Navigation/Music/Radio are app slots with show/hide switches. They can be assigned, changed and cleared. All apps is always present and last.
  • Pinned apps are added by long-pressing an app in All Apps and removed by long-pressing the added button on Home. Only pinned apps show their own icon by default; the fixed buttons and predefined slots stay stylized.
  • The grid adapts from 2 to 6 columns and shrinks its tiles as the count grows.
  • Every external app opens full-screen through a normal Android Intent.
  • Settings: light/dark/automatic theme, six accent colors, battery visibility, fixed night window, home-button switches, slot assignment, HOME-role setup, four-digit PIN.
  • The stylized Music tile shows an animated equalizer while media audio is active.
  • Settings → Media grants notification access for the active MediaSession title, duration and position. CarDash does not read notification content.
  • The long-press hint fades away two minutes after app start.
  • The Home-role banner is gone; a one-time first-run tip points to Settings.
  • Long-pressing a pinned app opens Rename, Change icon and Remove. Rename/icon changes persist; Remove needs no confirmation.
  • Optional OBD-II over a paired Bluetooth Classic ELM327: digital speed, throttle bar, rolling 5 km MAF economy and ambient temperature.
  • Optional location weather fallback when OBD has no ambient value; red below 3°C; no row when unavailable.
  • GPS speed fallback when no OBD adapter is connected and precise location was already granted; stationary shows 0 km/h, with no permission prompt.
  • Dark surfaces are tinted by the selected accent instead of retaining a green base.
  • PIN is requested only on a cold start, never on resume or screen-off.
  • The screen stays awake while CarDash is the foreground window.
  • Landscape-only; tablet-first 1280×800, also verified at 1280×720; Android 10 minimum.
  • Settings keeps the display awake by default; the user can switch to the system timeout.
  • Long-press on empty Home enters a black night overlay with a very dim clock; tap wakes.
  • Package: com.wasp.cardash_launcher. Display name: CarDash.

2. Decisions and why

Decision Choice Rationale
Central app content Launcher-native; apps open full-screen Android cannot embed another app; overlay chrome was rejected as fragile and app-obstructing.
UI chrome None (no bars) Removes the mismatch between launcher chrome and full-screen apps; cleaner, more compatible.
Home layout Single page, info + volume + up to 16 buttons Matches the refined brief; tiles scale from 2 to 6 columns.
App slots Navigation, Music, Radio with visibility switches Kept from the original brief; All apps is fixed.
Pinned apps All Apps long-press adds, Home long-press removes Makes any installed app reachable from Home without editing a separate layout screen.
Home cap 16 buttons total, including All apps; Settings is outside the grid Keeps the single page legible at tablet resolutions; enforced in LauncherController.
Settings placement Gear next to Wi-Fi in StatusCluster Keeps Settings reachable without spending a grid slot.
Playback control Circled volume ring is play/pause; Mute pill remains One prominent control for play/resume/pause without adding another card.
Icons Only pinned apps show their own icon by default; fixed slots stay stylized Keeps the dashboard consistent while making added apps recognizable.
Accent Six curated choices, persisted in Settings Changes the whole theme without a color picker or arbitrary contrast failures.
Battery visibility One switch hides level and charging bolt One control for both indicators, as requested.
Media feedback Stylized Music tile + animated bars while media is active Compact visual feedback without adding a media control surface.
Now-playing metadata MediaSessionManager via a notification-listener component The only public path to title, duration and position; read-only and scoped to media sessions.
Song progress Ring fraction from position/duration; scrolling title Replaces the volume ring as requested; paused state holds position and scroll offset.
Hint lifetime Two minutes from process start, then fade out Avoids permanent onboarding chrome; survives widget rebuilds via AppClock.
Home role Settings row only; one-time first-run tip Removes permanent dashboard chrome while keeping discovery.
Pinned editing Long-press menu: Rename / Change icon / Remove Keeps the home page clean; removal remains immediate.
Custom icon source Default app icon plus 29 bundled monochrome line icons Matches the stylized dashboard and follows light/dark theme colors.
OBD transport Bluetooth Classic SPP, bonded ELM327 only, read-only No scanning, BLE, USB or Wi-Fi; one native worker serializes requests.
OBD values Speed, throttle (49/11), MAF economy, ambient (46) The requested dashboard subset; unsupported PIDs stay hidden.
Economy 5 km MAF rolling window, petrol constants, fuel-cut guard Matches the design note; show — until 0.5 km.
Weather fallback Open-Meteo + one-shot device location, Settings toggle No API key/account; hide the row on any failure.
GPS speed fallback Poll GPS updates only when no OBD is connected and fine location is already granted No permission prompt; 0 below 2 km/h to suppress stationary drift.
Temperature priority OBD ambient, else location weather OBD wins whenever a fresh value exists.
Accent background Dark canvas/surfaces/borders blend toward the accent Fixes the green base showing through when Blue is selected.
Screen FLAG_KEEP_SCREEN_ON controlled by the Settings keep-awake switch Default on; no wake-lock permission, and off restores the system display timeout.
Night mode Long-press empty background; black overlay + dim clock; tap wakes Quiet night display without changing panel brightness or adding a wake lock.
Theme Light / Dark / Automatic with fixed 19:00–07:00 window No location permission or network; time pickers make the window adjustable.
PIN Four digits, salted PBKDF2 hash, cold start only Matches the explicit instruction; no re-lock while running.
Min Android API 29 (Android 10) Requested minimum; no in-scope API requires newer.
Target/compile 36 Current Flutter toolchain defaults; runs on API 29.
Orientation sensorLandscape + Dart orientation lock Car/tablet form factor; portrait is intentionally unsupported.
AVD 10.1" WXGA tablet, 1280×800, 160 dpi, API 35 and API 29 Matches a dashboard tablet and the reference resolution class.
HOME role Standard RoleManager request + Settings row A launcher must be chosen as the system home app; never silently forced.
Overlay/kiosk/root Out of scope Rejected for compatibility.

3. Architecture

Flutter
  HomeScreen
    ClockBlock / StatusCluster / VolumeCard / ShortcutGrid
    LauncherController  -> apps, slots, pinned apps, 16-button cap, default-home state
    SystemController    -> battery/Wi-Fi/volume/media state + media keys
    DisplayController   -> persisted keep-awake window flag
    GpsSpeedController  -> GPS speed fallback (permission already granted)
    HomeScreen          -> NightModeOverlay on empty-background long press
  AllAppsScreen / AppPickerSheet
  SettingsScreen -> General / Car data / Security
  PinSetupScreen / LockScreen -> LockController -> PinService
Android (Kotlin)
  MainActivity -> keep-screen-on, volume keys, permission forwarding
               -> LauncherBridge (packages, icons, system flag, role)
               -> SystemBridge (battery, Wi-Fi, volume, media activity, keys)
               -> MediaBridge (MediaSession title/progress) + NotificationListener
               -> TelemetryBridge (Bluetooth devices, OBD, location)
               -> ObdService (Classic SPP ELM327 worker)
  • SettingsRepository is the only persistence path for theme, night window, slot visibility/assignments and pinned apps. flutter_secure_storage holds the salted PIN hash.
  • Package enumeration and icon rendering happen on a worker thread in Kotlin and are returned as PNG bytes.
  • Timers for volume/status are stopped when the app leaves the foreground.
  • The PIN check is awaited before runApp, so there is no dashboard flash on a locked cold start.
  • Network access is limited to the optional Open-Meteo weather fallback; no telemetry.

4. Milestones

# Milestone State
M0 Local git repo, Flutter scaffold, launcher config, package ID, icons, docs Done
M1 x86_64 API 35/API 29 images, 1280×800 landscape tablet AVDs, emulator/install scripts Done
M2 Theme system, single home layout, clock/status, volume card, shortcut grid Done (initial visual pass)
M3 Kotlin bridges: app list/icons/launch, HOME role, volume, battery/Wi-Fi Done
M4 App picker, All Apps, slot persistence, long-press flows, launch errors Done
M5 Settings, theme auto window, PIN setup/change/remove, cold-start lock Done
M6 Interaction testing, light/dark screenshots, layout polish, API 29 verification Done
M7 Release build, final docs, screenshots, last commits Done
M8 Home-button visibility switches, All Apps pinning, 16-button adaptive grid, keep-awake Done
M9 Accent colors, battery toggle, fading hint, user icons, Media equalizer, install rule Done
M10 Remove Home-role banner, one-time first-run tip, pinned Rename/icon/remove menu Done
M11 Restore stylized fixed icons; pinned apps show own icons by default; 29-icon selector Done
M12 Accent-tinted dark theme, OBD-II adapter/telemetry, location weather fallback, Car data Settings Done
M13 Play/pause volume ring, Settings gear in status area, 16-button cap without Settings Done
M14 Song-progress ring, scrolling now-playing title, notification-access Settings card Done
M15 Keep-awake Settings switch (default on), night mode, 1280×720 layout Done
M16 GPS speed fallback when no OBD adapter and location is already allowed Done

5. Verification checklist

  • flutter analyze clean.
  • Unit/widget tests pass (23 tests: PIN, repository, theme/accent, battery, first-run state, slot visibility, pinned apps, custom labels/icons, cap, dense grid, equalizer).
  • Debug APK builds with JDK 21.
  • Installs on API 35 1280×800 tablet.
  • HOME role granted; Home opens CarDash.
  • Immersive full-screen; no Android bars.
  • Real app list and icons in All Apps.
  • App picker assign / long-press change / clear, with persistence across restart.
  • Launch Navigation/Music/Radio full-screen and return Home (verified with Maps and YouTube Music).
  • Volume slider, mute and hardware-key synchronization (keys are routed to STREAM_MUSIC while the dashboard is foreground).
  • Light theme, dark theme and automatic switch.
  • PIN set/change/remove; cold start shows lock; wrong PIN; no re-lock after returning from an app.
  • Verify 1024×600 compact layout (adb shell wm size 1024x600).
  • Boot and verify on Android 10 (API 29) AVD, including PIN and release APK.
  • Release APK on both AVDs (54 MB universal APK).
  • Navigation/Music/Radio switches hide/show buttons, persist, and preserve assignments.
  • All Apps long-press pins apps; the 16-button cap rejects a 17th button.
  • Long-press on an added app removes it; it can be re-added from All Apps.
  • 16 buttons fit at 1280×800, 1280×720 and 1024×600; the grid uses 2–6 columns.
  • FLAG_KEEP_SCREEN_ON follows the keep-awake switch: present by default, cleared when off and restored when on.
  • Long-press on empty Home enters night mode; card/button long-presses are unaffected; tapping wakes.
  • Six accent choices change the Material theme immediately and persist.
  • Battery switch hides both the battery level and charging bolt.
  • Settings is next to Wi-Fi; All apps is the last grid button.
  • A user-installed app (F-Droid) shows its own icon; system apps stay generic.
  • Music equalizer widget animates; native media-active polling is wired.
  • Home hint is gone after two minutes from process start.
  • All Apps search icon is visibly smaller.
  • No Home-role banner; the first-run tip appears once, then never again.
  • Long-press pinned app shows Rename, Change icon and Remove.
  • Rename updates the Home label; Change icon persists a stylized icon; Remove is immediate.
  • Fixed buttons stay stylized; pinned apps show their own icons by default.
  • Change icon offers Default plus 29 monochrome icons and applies the chosen icon.
  • Accent-tinted dark background changes from green to blue when Blue is chosen.
  • OBD Settings device dropdown and Bluetooth permission action are present.
  • OBD economy/card tests pass; native ELM327 path compiles.
  • Location permission, reverse geocoding and Open-Meteo fallback verified live (Ukiah, 17°C).
  • Temperature row renders below the date and follows OBD/weather priority.
  • Circled ring shows song progress; 44 tests include media keys, metadata, progress, the marquee, the ring, keep-awake and night mode.
  • Scrolling title/artist verified with a live Chrome MediaSession; pause holds both the ring and scroll position, resume continues.
  • Settings → Media grants notification access and shows Granted.
  • Settings gear is next to Wi-Fi; the grid ends with All apps.
  • GPS speed shows 0 km/h when stationary with no OBD adapter; no permission prompt.
  • Release 0.10.0 installed on the emulator and physical Android 17 tablet.
  • Screenshots in docs/screenshots/; README/AGENTS/plan/OBD doc updated.

6. Known risks and mitigations

  1. Media-volume polling is every 900 ms while foreground. It is cheap and handles hardware keys, but a future native ContentObserver would remove the polling. Play/pause uses standard Android media keys.
  2. QUERY_ALL_PACKAGES is required for a launcher app list. It is documented and acceptable for sideloading; a Play distribution needs policy work.
  3. PIN security scope: cold-start only by design; it does not replace the Android keyguard or protect against adb/data extraction. README explains it.
  4. Emulator stability: headless emulators can be killed by the host; scripts use managed boot and the host GPU. SwiftShader is only a fallback.
  5. Flutter 3.47 / AGP 9.1 / JDK 21: works on this machine; Java 27 is not the validated toolchain. The emulator uses the host GPU; SwiftShader segfaulted on the API 35 image, so the helper script defaults to -gpu host.
  6. 16-button density: small labels and mixed pinned-app icons are the trade-off for a single page. The Settings count and grid scaling make the limit explicit.
  7. Screen-on battery use: the flag applies only while the dashboard is foreground; it is not a background wake lock.
  8. Media metadata: the equalizer follows AudioManager.isMusicActive; title/progress query the active MediaSession through the notification-listener component. Notification access is user-granted and scoped to media metadata.
  9. Pinned customizations: Rename affects only the Home label. Default restores the app icon; a chosen stylized icon is a bundled asset and cannot become unavailable.
  10. OBD hardware is untested here. No ELM327 adapter is available on this machine; the native socket/protocol path compiles and the parser/economy UI is unit-tested, but a real-car soak test is still required.
  11. Weather depends on location, permission and network. Any failure hides the row; OBD ambient is always preferred when available.
  12. Uninstall/reinstall may drop the HOME role; Settings and the installer script can restore it.

7. Out of scope (for now)

  • Embedded third-party apps, overlay bars, split screen, kiosk/device-owner.
  • Global play/pause or now-playing metadata.
  • Wi-Fi/BLE/USB OBD adapters, DTC contents, RPM/coolant/voltage/MIL, fuel level/range.
  • Calls, climate, reverse camera.
  • An app drawer with folders/pages; a single searchable All Apps page is enough.
  • Play Store packaging/signing, remote git, CI.

8. Change log

  • 2026-10-29: Pivoted from an overlay/head-unit chrome design to a launcher-native single home page, no top/bottom bars, full-screen external apps. Implemented core UI, Kotlin bridges, settings, PIN and documentation. Verified initial home, app picker, All Apps, volume, themes and cold-start PIN on the API 35 AVD; then verified the release build and PIN flows on the API 29 AVD. Added hardware volume-key routing and PIN verification progress. Screenshots captured at 1280×800 and 1024×600.
  • 2026-10-29: Added home-button visibility switches, All Apps long-press pinning, a 16-button cap with an adaptive 2–6 column grid, pinned-app removal, and a foreground keep-screen-on flag. Verified 17 tests, 16-button layouts at 1280×800 and 1024×600, cap/removal/re-add, and 0.2.0 on the physical tablet.
  • 2026-10-29: Added six accent choices, battery/charging visibility, a two-minute fading hint anchored to process start, All apps last, user-app icons, a media-active equalizer, and a smaller All Apps search icon. Added the two-attempt ADB install rule. Verified 21 tests, accent/battery toggles, a user-app icon (F-Droid), hint expiry, and 0.3.0 on the emulator/tablet.
  • 2026-10-29: Removed the Home-role banner; added a once-only first-run tip. Replaced immediate removal with a pinned-app menu (Rename, Change icon, Remove), custom Home labels and up to 25 installed-app icon choices. Verified 23 tests and the full menu flow at 1280×800; 0.4.0 installed on the tablet.
  • 2026-10-29: Clarified icon policy: fixed/predefined buttons are always stylized; only pinned apps show their own icon by default. The Change icon grid now offers Default plus 29 bundled monochrome line icons. Verified 0.5.0 on the emulator and tablet.
  • 2026-10-29: Fixed the dark accent background. Added optional ELM327 OBD-II (speed, throttle, rolling MAF economy, ambient), location/Open-Meteo fallback, Car data Settings with adapter dropdown and weather toggle, and permission flows. Verified 30 tests, live weather on the emulator and 0.6.0 installation; OBD hardware remains a car test.
  • 2026-10-29: The circled volume ring is now play/pause (media-key resume, or opens the assigned Music app when nothing responds). Settings moved next to Wi-Fi in the status area and no longer consumes a grid slot. Verified 33 tests and 0.7.0 on the emulator and physical tablet.
  • 2026-10-29: Replaced the volume ring with song progress and added the scrolling now-playing title/artist. Added a MediaSession bridge and a Settings → Media notification-access grant; no notification content is read. Verified metadata, progress, scrolling/pause/resume with a live Chrome media session, 41 tests and 0.8.0 on emulator/tablet.
  • 2026-10-29: Added a default-on keep-awake switch, a black long-press night mode with a dim clock, and verified the 16-button layout at 1280×720. Verified the native window flag toggles off/on, night mode/card long-press isolation, 44 tests and 0.9.0 on emulator/tablet.
  • 2026-10-29: Added GPS speed fallback when no OBD adapter is connected and fine location is already allowed. No permission prompt; stationary values below 2 km/h render as 0. Verified 47 tests and 0.10.0 on emulator/tablet.
  • 2026-10-29: Centered the now-playing title across the whole media card instead of the right-hand control column. Verified 0.9.1 on emulator/tablet.