# CarDash — Agent Guide ## 1. Project Overview 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, and committed. Always call this project **CarDash**. ## 2. Commands Run from the repository root. ```bash 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). ## 3. Architecture - `lib/core/` — theme tokens (`AppColors`, `AppTheme`), `ThemeController`, reusable widgets. - `lib/data/models/` — `AppEntry`, `SlotType`, `SlotAssignment`, `SystemStatus`, `VolumeInfo`. - `lib/data/services/` — `NativeBridge` (platform channels), `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` (apps, slots, pinned apps, 16-button cap, HOME role), `SystemController` (volume/battery/Wi-Fi polling). - `lib/features/apps/` — All Apps screen and app-picker sheet. - `lib/features/settings/` — General and Security pages. - `lib/features/lock/` — PIN pad, lock screen, cold-start `LockController`. - `android/app/src/main/kotlin/com/wasp/cardash_launcher/` — `MainActivity` (keep-screen-on, volume keys), `LauncherBridge` (package manager, role), `SystemBridge` (battery, Wi-Fi, volume). - `tool/` — emulator/install/check scripts. `docs/` — implementation plan and design notes. Rules: - Persisted configuration goes through `SettingsRepository`; never add ad-hoc SharedPreferences keys in widgets. - Controllers are `ChangeNotifier`s consumed with `provider`; no business logic in widgets. - Android behavior lives behind `LauncherPlatform` so controllers stay testable. - External apps are launched with an Intent; they are **not** embedded in the launcher. - The PIN is app-local and checked only when the process starts. Persistence pattern: ```dart _slots[slot] = app.toAssignment(); await _settings.setSlot(slot, app.toAssignment()); notifyListeners(); ``` ## 4. Conventions - Use `context.colors` / `AppTheme` tokens, not hard-coded colors in widgets. - All UI copy is sentence case (except the CarDash name). Keep copy short and functional. - New home buttons: extend `SlotType` for fixed slots or use `LauncherController` pinned-app APIs; enforce the 16-button cap in the controller. - New native methods: add them to `LauncherPlatform`, `NativeBridge`, the relevant Kotlin bridge, and the test fake if used. - Tests: fake `LauncherPlatform`/`SecureStore`; `SharedPreferences.setMockInitialValues({})`; widget tests wrap in `MaterialApp(theme: AppTheme.light())`. Test files live in `test/` and end with `_test.dart`. - Run `dart format lib test` before committing. Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`). - Update this file, `README.md`, and `docs/IMPLEMENTATION_PLAN.md` in the same commit as any behavior change. - Keep the UI fast: no large blur layers, no continuously running decorative animation, prefer implicit animations under ~250 ms. ## 5. Boundaries - NEVER embed a third-party app's UI, add an overlay/`SYSTEM_ALERT_WINDOW`, kiosk, root, or split-screen feature. The agreed product launches apps full-screen. - NEVER add a terms-and-conditions screen, blocking first-run modal, or mandatory onboarding. - NEVER change the application ID, HOME intent filter, landscape lock, or minSdk without human confirmation. - NEVER re-lock on resume, screen-off, or route changes; no "Lock now" action. PIN is requested only on cold start. - NEVER store, log, or display a plaintext PIN. - NEVER add the INTERNET permission or weather without human confirmation. - NEVER commit `android/local.properties`, keystores, build outputs, APKs, or secrets. - REQUIRE human confirmation before changing signing configuration, package visibility permissions, or the repository remote. ## 6. Gotchas - The machine default Java is 27; Gradle/AGP for this project is validated with **JDK 21**. Always export `JAVA_HOME=/usr/lib/jvm/java-21-openjdk` for builds. - Only x86_64 system images run here; the pre-installed arm64 image cannot boot. The download script/images are already in `/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. - The Flutter template uses AGP 9.1 built-in Kotlin support. Do not add the Kotlin Gradle plugin unless the build explicitly requires it. - Immersive mode is reapplied on every `resumed` lifecycle event; Android may restore system bars after dialogs or role prompts. - The HOME role must be granted for the dashboard to replace the tablet home screen. On the emulator use `cmd role add-role-holder`; the in-app banner and Settings row use `RoleManager`. - Screen-space is deliberately finite: test the 16-button grid at 1280×800 and 1024×600. Avoid unbounded `ListView`s inside `Column`s. - `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. - While CarDash is foreground, `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. - Reinstalling the app can clear its HOME role; re-run `tool/install.sh` or use Settings > Home app.