# CarDash A calm, configurable Android car dashboard launcher, built with Flutter. CarDash replaces the tablet home screen with one landscape page: a large clock, battery and Wi-Fi status, a now-playing media card, and up to 16 buttons — **Navigation**, **Music**, **Radio**, **All apps**, plus any apps you pin from All Apps. The first three can be shown, hidden and assigned to installed apps; Settings opens from the gear next to Wi-Fi. External apps open full-screen using the normal Android launcher mechanism. No overlays, no root, no kiosk setup.
Verified on Android 15 and Android 10 tablet emulators at 1280×800, 1280×720 and 1024×600. Runs on a physical Android 17 HyperOS tablet. OBD-II UI/parser is implemented; the ELM327 socket path still needs a real-car test. Landscape only.
## Download - [Gogs release page](https://git.krasi.net/kkazakov/car-interface-android/releases) — published releases - [cardash-android-1.0.1.apk](releases/cardash-android-1.0.1.apk) — universal Android release - SHA-256 and build details: [releases/README.md](releases/README.md) Gogs limits release attachments to 32 MB, so the 54 MB universal APK is linked directly from the repository. ## Screenshots | Light | Dark | |---|---| |  |  | Also: [Home with top-right Settings](docs/screenshots/home-16.png) · [Home at 1024×600](docs/screenshots/home-16-1024.png) · [All apps](docs/screenshots/all-apps.png) · [app picker](docs/screenshots/app-picker.png) · [Settings · accent & buttons](docs/screenshots/settings-buttons.png) · [Settings · Car data](docs/screenshots/settings-car-data.png) · [Settings · Media access](docs/screenshots/settings-media.png) · [Now playing](docs/screenshots/home-now-playing.png) · [1280×720](docs/screenshots/home-1280x720.png) · [GPS speed](docs/screenshots/home-gps-speed.png) · [Mocked OBD · 1280×720](docs/screenshots/home-obd-mock-1280x720.png) · [Mocked OBD · 1280×800](docs/screenshots/home-obd-mock-1280x800.png) · [Night mode](docs/screenshots/home-night-mode.png) · [Blue accent](docs/screenshots/settings-blue-accent.png) · [user-app icon on Home](docs/screenshots/home-user-icon.png) · [pinned-app menu](docs/screenshots/home-app-menu.png) · [change-icon grid](docs/screenshots/home-app-icons.png) · [PIN lock](docs/screenshots/pin-lock.png) · [1024×600 layout](docs/screenshots/home-compact-1024.png) The two **Mocked OBD** images are documentation renders with a simulated adapter state (82 km/h, 34% throttle, 7.4 L/100 km, Outside 17°C). They are not live vehicle data. ## Features - **One home screen** — no pages, no top bar, no bottom dock; information and controls live on the home page. - **Large clock and date** with the device's 12/24-hour preference. - **Battery, charging and Wi-Fi** status, with one switch to hide the battery level and charging bolt. - **OBD-II car data** — optional Bluetooth Classic ELM327 adapter. Shows digital speed, a throttle bar and rolling 5 km MAF economy, plus ambient temperature when the adapter provides it. - **GPS speed fallback** — when no OBD adapter is connected and precise location is already allowed, show speed from GPS; it reads 0 km/h when stationary. CarDash never prompts for this; GPS must be enabled and location previously allowed (for example through the weather toggle). - **Location temperature fallback** — if OBD has no ambient value, an optional Settings toggle fetches the current temperature from Open-Meteo using the device location. The row reads `Outside, 7°C` for OBD or `Sofia, 7°C` for weather, and turns red below 3°C. No network/selection means no row. - **Now-playing media card** — the circular ring shows song progress around a play/pause button. The song title and artist scroll along the bottom; paused audio stops the scroll and holds its position. When nothing is playing it reads **Nothing playing**. The volume slider, −/+, and Mute pill remain. - **Update check** — on startup, CarDash queries the public Gogs release list without blocking the dashboard. Settings → Updates shows the current version, a Check button, and an Update action that downloads the APK and opens Android’s installer. Android still requires confirmation and a one-time “install unknown apps” allowance; silent installation is not attempted. - **Now-playing access** — Settings → Media grants Android notification access, used only to read the active media session’s title, duration and position. CarDash never reads or stores notification content. - **Configurable home buttons** — switches in Settings show or hide Navigation, Music and Radio. **All apps** is always the last button; **Settings** now sits next to Wi-Fi in the top-right status area and is no longer a grid tile. - **Pin any app from All Apps** — long-press an app to add it to Home. The home page holds up to **16 buttons including All apps**, and the grid shrinks as it fills. - **Edit a pinned app** — long-press it on Home for Rename, Change icon and Remove. Pinned apps show their own icon by default. Rename changes only the Home label; Change icon offers **Default** plus 29 monochrome line icons (white on dark, black on light); Remove takes effect without confirmation. Re-add the app from All Apps. - **One-time launcher tip** — the first run shows a short notification that the default-launcher setting lives in Settings. It is not shown again. - **All apps** — searchable grid of every launchable app, launched full-screen. - **Media-aware Music tile** — while media audio is active and the Music shortcut is assigned, the stylized music icon shows a small animated equalizer. Android exposes media-active state, not the playing package, to a normal launcher; see the limitation below. - **Six accent colors** — Green, Blue, Amber, Rose, Violet or Teal, applied across the whole interface. - **Prevent CarDash from sleeping** — a Settings switch, on by default. Turn it off to use the system display timeout. - **Night mode** — long-press an empty part of the Home background to go black with a very dim centered clock; tap anywhere to return. Long-presses on buttons and cards are unaffected. - **A short onboarding hint** — the long-press hint fades away two minutes after app start, then stays gone. - **Light / Dark / Automatic** theme. Automatic uses a fixed local-time window (default 19:00–07:00), editable in Settings. - **Optional PIN** — four digits, stored as a salted PBKDF2 hash in Keystore-backed storage. It is requested **only when the launcher process cold-starts**, never when returning from an app. - **Standard Android home role** — no overlay, kiosk or root. - **Android 10+**, landscape-only, tablet-first (verified at 1280×800). - No accounts, no telemetry, no T&C screen. Network access is used only for the optional Open-Meteo temperature fallback. ## Requirements - Flutter 3.47.8 / Dart 3.13.7 (or a compatible newer stable release) - Android SDK 36, build-tools 36 - **JDK 21** for Gradle (the machine default is JDK 27, which is not validated) - Android 10 (API 29) or newer device/emulator - A landscape tablet or an emulator; the app locks itself to landscape - The prepared tablet AVDs use the **host GPU**; a working GPU driver is recommended - Optional: a Bluetooth Classic ELM327 adapter already paired in Android - Optional: location access for the Open-Meteo temperature fallback The Android application ID is `com.wasp.cardash_launcher`. Android package segments cannot contain hyphens, so the requested `com.wasp.cardash-launcher` spelling is normalized to an underscore. ```bash export JAVA_HOME=/usr/lib/jvm/java-21-openjdk export ANDROID_HOME=/opt/android-sdk export PATH="$ANDROID_HOME/platform-tools:$PATH" ``` ## Quick start ```bash flutter pub get ./tool/check.sh # Boot the prepared 10.1" 1280×800 Android 15 tablet (headless) ./tool/emulator.sh 35 # Build, install, and make CarDash the HOME app ./tool/install.sh debug ``` If the emulator does not yet exist on another machine, create it with: ```bash sdkmanager "system-images;android-35;google_apis;x86_64" avdmanager create avd -n cardash_tablet_api35 \ -k "system-images;android-35;google_apis;x86_64" \ -d "10.1in WXGA (Tablet)" ``` The repository also has an Android 10 AVD (`cardash_tablet_api29`) for minimum-version checks. ### Manual install ```bash flutter build apk --release adb install -r build/app/outputs/flutter-apk/app-release.apk adb shell cmd role add-role-holder android.app.role.HOME com.wasp.cardash_launcher adb shell am start -a android.intent.action.MAIN -c android.intent.category.HOME ``` On a real tablet, use **Settings → General → Home app → Set as default** and accept the system prompt. Android will not let an app force itself into the home role silently. CarDash no longer keeps a home-role banner on the dashboard; the first run shows a one-time tip pointing to this setting. ## Using CarDash | Action | Result | |---|---| | Tap Navigation / Music / Radio | Open the assigned app full-screen | | Long-press Navigation / Music / Radio | Pick a different installed app | | Tap an unassigned shortcut | Open the app picker | | Settings → Accent | Pick Green, Blue, Amber, Rose, Violet or Teal | | Settings → Battery & charging | Show or hide battery level and charging bolt | | Settings → Prevent CarDash from sleeping | Keep the display on, or use the system timeout | | Long-press an empty part of Home | Enter black night mode with a dim clock | | Tap the night screen | Return to the dashboard | | Settings → Home buttons switches | Show or hide Navigation, Music, Radio | | Long-press an app in All Apps | Add it to Home (up to 16 buttons) | | Long-press an added app on Home | Rename, Change icon or Remove | | Settings → Car data | Pick a paired ELM327 or None | | Settings → Car data → Location temperature | Enable GPS weather fallback | | No OBD + allowed location + GPS on | Shows GPS speed above the volume card; 0 when stopped | | Temperature row | OBD ambient, or location weather when OBD has none | | Settings | Theme, accent, media access, battery, home buttons, default-home role, PIN | | All apps | Search every launchable app; tap to open, hold to pin | | Music tile while media is active | Shows the selected app icon + equalizer | | Circular ring | Song progress; tap the centre to play/pause | | Song title at the card bottom | Scrolls only while playing; holds when paused | | Screen | Stays awake while CarDash is in front | | Mute pill | Toggle media mute | | Slider or − / + | Set media volume | | Hardware volume keys | Adjust the dashboard's media volume; the slider follows | The app hides the Android status and navigation bars while CarDash is in the foreground. Swipe from an edge to reveal them temporarily; they hide again when the app resumes. ## Settings **General** - Light / Dark / Auto theme. - Accent: Green, Blue, Amber, Rose, Violet or Teal. - Battery & charging: one switch hides both the level and charging bolt. - Prevent CarDash from sleeping: keep the display on; on by default. - Auto night window (default dark from 19:00, light from 07:00). - Home buttons: show/hide Navigation, Music, Radio; live count out of 16. - Assign the app for each slot; All apps is fixed and last. - Settings is always available from the gear next to Wi-Fi. - Home role status and shortcut to the system prompt. **Media** - Notification access for the now-playing title, duration and progress. - Read-only: CarDash does not read notification content. - Without access the ring stays empty and the card shows a generic Playing / Nothing playing state. **Car data** - OBD-II adapter dropdown: None plus paired Bluetooth Classic devices. - Connection status and a Find/Allow action for Bluetooth permission. - Location temperature toggle: “Show temperature from current location if no OBD-II connected.” Enabling it asks for location permission. - Read-only note: CarDash never writes to the ECU. **Updates** - Non-blocking check against the public Gogs releases. - Shows current/latest version; Update downloads the APK and opens Android’s installer. - Android confirms the install and, on first use, asks to allow CarDash as an unknown-app source. No silent installation. **Security** - Set, change or remove a four-digit PIN. - The PIN is requested only on a cold start. It does not re-lock when switching apps, after screen-off, or after returning from Navigation. This is the agreed behavior, not an oversight. - The PIN protects the launcher UI. It is **not** the Android lock screen; set a device screen lock separately for device-level security. - There is no PIN recovery. Resetting app data removes it. ## Architecture ``` lib/ core/ design tokens, theme controller, icons, shared widgets data/ models, platform services, SettingsRepository (single source of truth) features/ home/ home page, shortcut grid, volume card, OBD/weather controllers apps/ All Apps, app-picker sheet settings/ General, Car data + Security lock/ PIN pad, cold-start lock screen android/... MainActivity, LauncherBridge, SystemBridge, TelemetryBridge, ObdService tool/ emulator, install, check scripts docs/ implementation plan, design notes ``` - Flutter owns all UI. Kotlin exposes package enumeration/launching, the HOME role, battery/Wi-Fi, media volume, active media metadata, Bluetooth OBD and one-shot location over method channels. - The OBD layer is read-only, Bluetooth Classic SPP, bonded devices only. A worker thread serializes ELM327 requests; Flutter polls the latest snapshot. - `SettingsRepository` is the only persistence path. Controllers are `ChangeNotifier`s injected with `provider`. - External apps are separate activities; Android has no public API to embed them inside the launcher. This app deliberately accepts full-screen launching for maximum compatibility. - Full OBD/weather design and hardware notes: [docs/OBD_TEMPERATURE.md](docs/OBD_TEMPERATURE.md). ## Testing ```bash flutter analyze flutter test ./tool/check.sh ``` The 50 tests cover PIN hashing/persistence, settings persistence, theme/accent, dark accent tinting, battery visibility, first-run state, slot visibility, pinned apps, custom labels/icons, the 16-button cap at 1280×800, 1280×720 and 1024×600, the dense shortcut grid, the equalizer animation, OBD economy, the OBD card, media-key playback, media metadata/progress, the marquee, the play/pause ring, the keep-awake preference, the night overlay, GPS speed fallback and the update version/availability logic. Native package enumeration, the HOME role, volume, app launching and PIN flows were verified on API 35 and API 29 emulators. Location permission, reverse geocoding and the Open-Meteo fallback were verified live on the API 35 emulator. Now-playing title, artist, progress and pause/resume were verified against a live Chrome MediaSession. The OBD hardware path compiles and is covered by the parser/economy design, but no ELM327 adapter is available on this machine; socket behavior still needs a car test. Release The 1.0.1 universal APK is built from this tree. The emulator completed a live 1.0.0 → 1.0.1 in-app update; the physical tablet has the 1.0.0 installer build ready for its own test. ## Troubleshooting the emulator The prepared AVDs use the host GPU (`-gpu host`), which is the verified setup on this machine. Software rendering with SwiftShader segfaulted on the API 35 image. If your machine has no usable GPU, you can try: ```bash CARDASH_GPU=swiftshader_indirect ./tool/emulator.sh 35 ``` The prepared AVD resolution is 1280×800. If the emulator is already running, `./tool/emulator.sh stop` shuts it down before starting a different one. The Android 15 AVD is left running headlessly after development; use `./tool/emulator.sh 35 window` for a visible window. ## Compatibility and limitations - **minSdk 29 (Android 10)**; compile/targetSdk 36. Nothing in the design requires a newer Android release. - **Landscape only** (`sensorLandscape`). The home page is designed for 1280×800 and verified at 1280×720 and 1024×600; 16 buttons fit at all three. - **16-button cap** includes All apps; Settings lives in the top-right status area and does not consume a slot. Only apps pinned from All Apps show an original app icon by default; the predefined slots, All apps and the Settings gear stay stylized. A pinned icon can be replaced with the Default app icon or the 29 stylized choices. - **Accent-tinted dark theme** — dark canvas, surfaces and borders blend toward the selected accent, so Blue no longer shows a green base. - **Media equalizer** reacts to system media activity. The now-playing title and progress use the active Android MediaSession, which requires the special notification-access grant in Settings → Media. - **Notification access is used only for media metadata.** CarDash does not read notification text or store track history. Revoke it in Android settings at any time; the ring and title then fall back to an empty/generic state. - **Screen-on** is controlled by Settings → Prevent CarDash from sleeping (default on). It applies only while CarDash is the foreground window; with the switch off, the normal Android display timeout applies. - **Night mode is a display overlay only.** It does not change panel brightness or keep the screen awake by itself; the screen-on preference still applies. Long-press on cards and buttons is reserved for their own actions. - **Onboarding hint** is anchored to process start and disappears after two minutes; it does not return until the next cold start. - **No embedded third-party app UI.** Navigation/Music/Radio are launched in their own full-screen windows. - **Renaming changes the Home label only.** Android does not allow a launcher to rename another app's installed label. - **Custom icons** are bundled monochrome SVGs, so they follow the light/dark theme. Default restores the pinned app's own icon. - **Play/pause uses Android media keys.** It resumes a paused session when one responds; if nothing is playing, it opens the app assigned to the Music slot (or asks you to choose one). The ring shows Play whenever audio is inactive. - **Updates download and invoke Android’s installer.** CarDash never silently installs. Android requires the user to confirm each install and, once, to allow CarDash as an unknown-app source. - **Song progress/title require notification access.** Without it, Android does not expose MediaSession metadata to a normal launcher; the card still controls playback and shows Nothing playing or Playing. - **GPS speed fallback** requires precise location already granted and the GPS provider enabled; CarDash does not request permission from this path. OBD speed wins whenever an adapter is connected, and stale/no-fix GPS values are hidden. - **OBD-II is a subset**: Bluetooth Classic SPP only, paired devices only, read-only. Speed, throttle, MAF economy and ambient temperature are implemented; RPM, coolant, voltage, MIL/DTCs, fuel level and BLE/Wi-Fi adapters are not. - **Weather fallback** uses Open-Meteo and the device location. It requires location permission and network access; if either is unavailable, the temperature row is hidden. OBD ambient wins whenever present. - **Location data is not stored.** Only the selected adapter address and the weather toggle are persisted. - **`QUERY_ALL_PACKAGES`** is used to list all launchable apps. It is reasonable for a launcher but requires a Play policy declaration if the app is ever distributed; this is a local/sideload build for now. - **Network access** is used for the optional Open-Meteo temperature fallback and the Gogs release check. There are no accounts, ads, telemetry or silent auto-updates. - The launcher cannot replace Android's lock screen. The PIN does not protect a running session, by design. ## Design The visual language is inspired by the [TrailCurrent Godwit](https://trailcurrent.com/godwit.html) head unit (MIT): warm neutral surfaces, a selectable instrument accent, large numerals and quiet line icons. It is not a copy of that UI. - Typeface: **Manrope** (SIL Open Font License), bundled offline. - Icons: **Lucide** (ISC), bundled as SVGs. - Palette and tokens: `lib/core/theme/app_colors.dart`. ## Roadmap - Optional compact now-playing control beside the volume card. - Real-car ELM327 soak test (speed/throttle/MAF/ambient). - Release signing configuration. - Real-device soak test on an Android 10 car tablet. ## License This repository is in local development and has not been pushed or published. A project license has not been chosen yet. Third-party asset licenses: Manrope (OFL) and Lucide (ISC). The design is inspired by TrailCurrent Godwit (MIT).