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 by default (configurable), offers a long-press night screen, light/dark/automatic themes, and 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, DisplayController (keep-awake), AppClock, reusable widgets.lib/data/models/ — AppEntry, SlotType, SlotAssignment, SystemStatus, VolumeInfo, MediaPlayback, OBD/weather models.lib/data/services/ — NativeBridge, TelemetryBridge, WeatherService, UpdateService, 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, GpsSpeedController, equalizer, night overlay.lib/features/apps/ — All Apps screen and app-picker sheet.lib/features/settings/ — General, Media, Car data, Security and Updates 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), MediaBridge, CardashNotificationListener; SystemBridge also dispatches media keys and applies the keep-awake flag.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 is controlled by DisplayController from display.keepScreenOn (default on) through SystemBridge; do not hard-code it in MainActivity or move it to a wake lock.AudioManager.isMusicActive. Now-playing title/progress use the notification-listener component and active MediaSession; never read or store notification content.UpdateController.updateAvailable is true.tool/install.sh or use Settings > Home app.