CarDash is a Flutter/Dart Android home-screen launcher for a car tablet. It has one configurable home page (clock, battery/Wi-Fi, media volume, up to 16 home buttons), launches other apps full-screen, keeps the screen awake while foreground, and offers light/dark/automatic themes plus an optional four-digit PIN requested only on cold start.
Stack: Flutter 3.47.8 / Dart 3.13.7, Android minSdk 29 (Android 10), compile/targetSdk 36, Kotlin bridges, AGP 9.1.0, Gradle 9.3.1, JDK 21. Package com.wasp.cardash_launcher. Landscape-only, tablet-first (1280×800).
You are the implementation agent: keep the app buildable, tested, documented, committed, and pushed. Always call this project CarDash.
Run from the repository root.
flutter pub get
flutter analyze
flutter test
flutter test test/pin_service_test.dart
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk
flutter build apk --debug
flutter build apk --release
./tool/check.sh # analyze + tests
./tool/emulator.sh 35 # boot the Android 15 tablet (headless)
./tool/emulator.sh 29 # boot the Android 10 tablet
./tool/install.sh debug # build with JDK 21, install, set HOME role
./tool/install.sh release
ADB lives at /opt/android-sdk/platform-tools/adb; the SDK is at /opt/android-sdk. AVDs: cardash_tablet_api35, cardash_tablet_api29 (1280×800, landscape).
lib/core/ — theme tokens (AppColors, AccentChoice), AppTheme, ThemeController (theme/accent/battery), AppClock, reusable widgets.lib/data/models/ — AppEntry, SlotType, SlotAssignment, SystemStatus, VolumeInfo.lib/data/services/ — NativeBridge, TelemetryBridge, WeatherService, PinService, secure-store adapter.lib/data/repositories/ — SettingsRepository, the SSOT for theme, night window, slot assignments, visibility flags and pinned apps.lib/features/home/ — home screen, LauncherController, SystemController, ObdController, WeatherController, equalizer.lib/features/apps/ — All Apps screen and app-picker sheet.lib/features/settings/ — General, Car data and Security pages.lib/features/lock/ — PIN pad, lock screen, cold-start LockController.android/app/src/main/kotlin/com/wasp/cardash_launcher/ — MainActivity, LauncherBridge, SystemBridge, TelemetryBridge, ObdService (Classic SPP ELM327); SystemBridge also dispatches media keys.tool/ — emulator/install/check scripts. docs/ — implementation plan and design notes.Rules:
SettingsRepository; never add ad-hoc SharedPreferences keys in widgets.ChangeNotifiers consumed with provider; no business logic in widgets.LauncherPlatform so controllers stay testable.Persistence pattern:
_slots[slot] = app.toAssignment();
await _settings.setSlot(slot, app.toAssignment());
notifyListeners();
context.colors / AppTheme tokens, not hard-coded colors in widgets.SlotType for fixed slots or use LauncherController pinned-app APIs; fixed slots stay stylized, pinned apps default to their own icon, and icon edits use style_icon_choices.dart.LauncherPlatform, NativeBridge, the relevant Kotlin bridge, and the test fake if used.LauncherPlatform/SecureStore; SharedPreferences.setMockInitialValues({}); widget tests wrap in MaterialApp(theme: AppTheme.light()). Test files live in test/ and end with _test.dart.dart format lib test before committing. Conventional Commits (feat:, fix:, docs:, chore:). After every commit, push to origin (git push origin main); never leave commits unpushed and never force-push.README.md, and docs/IMPLEMENTATION_PLAN.md in the same commit as any behavior change.SYSTEM_ALERT_WINDOW, kiosk, root, or split-screen feature. The agreed product launches apps full-screen.android/local.properties, keystores, build outputs, APKs, or secrets.JAVA_HOME=/usr/lib/jvm/java-21-openjdk for builds./opt/android-sdk.QUERY_ALL_PACKAGES is required to list every app. It is acceptable for a launcher but is not a Play-Store-distributable choice as-is; document it.resumed lifecycle event; Android may restore system bars after dialogs or role prompts.cmd role add-role-holder; the Settings row uses RoleManager.LauncherController.homeRoleHintShown; do not show it again.StatusCluster and does not consume a slot. Avoid unbounded ListViews inside Columns.tool/emulator.sh starts a headless AVD with the host GPU; SwiftShader segfaulted on this machine's API 35 image. Use CARDASH_GPU=swiftshader_indirect only as a fallback. Use adb exec-out screencap -p for screenshots; there may be multiple adb binaries on PATH, so use the absolute one.MainActivity consumes hardware volume keys and adjusts STREAM_MUSIC so they match the dashboard. Do not remove that override without providing another sync path; otherwise Android controls the ring stream when no media is playing.FLAG_KEEP_SCREEN_ON in MainActivity is intentional. It applies while CarDash is the foreground window; do not move it to a permission-based wake lock.AudioManager.isMusicActive, not the playing package. Exact attribution needs notification access and is out of scope unless requested.tool/install.sh or use Settings > Home app.