AGENTS.md 7.1 KB

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.

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, AccentChoice), AppTheme, ThemeController (theme/accent/battery), AppClock, 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/media polling), equalizer.
  • 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 (packages, icons, system flag, role), SystemBridge (battery, Wi-Fi, volume, media activity).
  • 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 ChangeNotifiers 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:

_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 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.
  • 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.
  • The Music equalizer follows AudioManager.isMusicActive, not the playing package. Exact attribution needs notification access and is out of scope unless requested.
  • ADB installs: always try at most twice per connected device. If both attempts fail, stop silently rather than looping or switching install methods.
  • Reinstalling the app can clear its HOME role; re-run tool/install.sh or use Settings > Home app.