Last updated: 2026-10-30
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; 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. |
| Volume control | Absolute setStreamVolume, relative adjustStreamVolume fallback, fixed-policy detection |
Some car units ignore absolute writes; fixed-volume amplifiers cannot be controlled by apps. |
| 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 | Ten minutes from the first launch after a fresh install, then never | Avoids permanent onboarding chrome; upgraded installs migrate to expired. |
| 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 via the Temperature card | No API key/account; hide the row on any failure. |
| GPS speed fallback | Poll GPS when speed source is GPS, OBD is unavailable, or the Speed card is off | No permission prompt from this path; 0 below 2 km/h to suppress stationary drift. |
| Speed source | OBD-II preferred while connected; GPS fallback, switch in Settings | The user can force GPS even with an adapter connected. |
| Update check | Public Gogs releases API, non-blocking startup check, Settings card | Update downloads the APK and opens Android’s installer; installation stays user-confirmed. Yellow dot on Settings gear when available. |
| Temperature priority | OBD ambient while its source switch is on, else location weather | OBD wins when fresh; weather is the fallback. |
| Settings cards | Separate OBD-II, Speed and Temperature cards | Adapter settings stay isolated from display/source choices. |
| 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. |
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)
TelemetrySettingsController -> speed/temperature visibility + sources
UpdateController -> public Gogs release check + release-page link
HomeScreen -> NightModeOverlay on empty-background long press
AllAppsScreen / AppPickerSheet
SettingsScreen -> General / OBD-II / Speed / Temperature / 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, pinned apps, onboarding time, speed/temperature
visibility and source choices. 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 |
| 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 |
| M17 | Non-blocking Gogs update check; 1.0.1 version-only update-test release | Done |
| M18 | Bluetooth-off settings action; 1.0.2 release | Done |
| M19 | OBD-II/Speed/Temperature Settings cards; source switches; 10-minute first-install hint; 1.0.3 | Done |
| M20 | Fixed-volume car detection; relative volume fallback; 1.0.4 | Done |
flutter analyze clean.STREAM_MUSIC while the dashboard is foreground).adb shell wm size 1024x600).FLAG_KEEP_SCREEN_ON follows the keep-awake switch: present by default, cleared when off and restored when on.releases/; release published for update testing.docs/screenshots/; README/AGENTS/plan/OBD doc updated.ContentObserver would remove
the polling. Play/pause uses standard Android media keys.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;
title/progress query the active MediaSession through the notification-listener
component. Notification access is user-granted and scoped to media metadata.setStreamVolume; fixed-volume sliders now
explain that the vehicle amplifier owns the level. Bumped to 1.0.4+16.releases/cardash-android-1.0.0.apk with checksum and release notes.