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 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 ·
Home at 1024×600 ·
All apps ·
app picker ·
Settings · accent & buttons ·
Settings · Car data ·
Settings · Media access ·
Now playing ·
1280×720 ·
GPS speed ·
Mocked OBD · 1280×720 ·
Mocked OBD · 1280×800 ·
Night mode ·
Blue accent ·
user-app icon on Home ·
pinned-app menu ·
change-icon grid ·
PIN lock ·
1024×600 layout
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.
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
export ANDROID_HOME=/opt/android-sdk
export PATH="$ANDROID_HOME/platform-tools:$PATH"
Quick start
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:
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
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
ChangeNotifiers 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.
Testing
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:
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
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).