# 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, the main media-volume control, and five shortcuts — **Navigation**, **Music**, **Radio**, **Settings** and **All apps**. The first three are configurable; a long press picks a different installed app. External apps open full-screen using the normal Android launcher mechanism. No overlays, no root, no kiosk setup.

Early preview — implementation and UI screenshots are in progress.

## 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. - **Media volume** — ring, slider, step and mute controls; hardware volume keys stay in sync. This is the phone's media stream, not a private volume. - **Configurable shortcuts** for Navigation, Music and Radio. Generic icons on the dashboard; real app icons in the picker and All Apps. - **All apps** — searchable grid of every launchable app, launched full-screen. - **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 special overlays or permissions. - **Android 10+**, landscape-only, tablet-first (verified at 1280×800). - No network permission, no accounts, no telemetry, no T&C screen. ## 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 ```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 --debug adb install -r build/app/outputs/flutter-apk/app-debug.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. The in-app "Make CarDash your home app" card does the same thing. Android will not let an app force itself into the home role silently. ## 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 | Theme, home shortcuts, default-home role, PIN | | All apps | Search every launchable app; tap to open | | Volume ring / mute | Toggle media mute | | Slider or − / + | Set media volume | | Hardware volume keys | Update the on-screen control automatically | 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. - Auto night window (default dark from 19:00, light from 07:00). - The three home shortcuts. - Home role status and shortcut to the system prompt. **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, controllers apps/ All Apps, app-picker sheet settings/ General + Security lock/ PIN pad, cold-start lock screen android/... MainActivity, LauncherBridge, SystemBridge (Kotlin) tool/ emulator, install, check scripts docs/ implementation plan, design notes ``` - Flutter owns all UI. Kotlin only exposes package enumeration/launching, the HOME role, battery/Wi-Fi and media volume over two `MethodChannel`s. - `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. ## Testing ```bash flutter analyze flutter test ./tool/check.sh ``` Current tests cover PIN hashing/persistence, settings persistence, theme resolution and the shortcut tile widget. Native package enumeration and the HOME role are verified on the emulator. ## 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 remains usable at 1024×600. - **No embedded third-party app UI.** Navigation/Music/Radio are launched in their own full-screen windows. - **No global play/pause control.** Volume is on the home screen; playback buttons live in the media app itself. - **No weather, Bluetooth, calls or climate** in this version. - **`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. - **No internet permission.** The app does not fetch maps, weather or 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 single deep-green 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. - Configurable home layout or additional shortcut slots. - Screenshot set in `docs/screenshots/`. - Release signing configuration. - Real-device soak test on an Android 10 car tablet. ## License This repository is currently local-only; a project license has not been chosen. Third-party asset licenses: Manrope (OFL) and Lucide (ISC). The design is inspired by TrailCurrent Godwit (MIT).