Last updated: 2026-10-29
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:
Intent.com.wasp.cardash_launcher. Display name: CarDash.| 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; Settings and All apps are 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 Settings and All apps | Keeps the single page legible at tablet resolutions; enforced in LauncherController. |
| 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. |
| 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. |
| 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 while foreground |
The dashboard is the car display; no wake-lock permission needed. |
| 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. |
Flutter
HomeScreen
ClockBlock / StatusCluster / VolumeCard / ShortcutGrid
LauncherController -> apps, slots, pinned apps, 16-button cap, default-home state
SystemController -> battery/Wi-Fi/volume/media polling
AllAppsScreen / AppPickerSheet
SettingsScreen -> General / 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)
-> 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.runApp, so there is no dashboard flash on a
locked cold start.| # | 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 |
flutter analyze clean.STREAM_MUSIC while the dashboard is foreground).adb shell wm size 1024x600).FLAG_KEEP_SCREEN_ON is present on the dashboard window.docs/screenshots/; README/AGENTS/plan/OBD doc updated.ContentObserver would remove
the polling. No global play/pause is in scope.QUERY_ALL_PACKAGES is required for a launcher app list. It is documented
and acceptable for sideloading; a Play distribution needs policy work.-gpu host.AudioManager.isMusicActive,
not a specific package. Exact attribution would require notification access.