# CarDash implementation plan _Last updated: 2026-11-01_ ## 1. What we are building CarDash is a Flutter/Dart Android **home-screen launcher** for a landscape car tablet. It deliberately trades the reference head-unit chrome for maximum compatibility: - One home page. No top bar, no bottom dock, no pages. - Clock/date, battery/charging and Wi-Fi on the page. - A prominent now-playing card: song-progress ring around play/pause, scrolling title/artist, volume slider, −/+, mute. - Up to **16 home buttons**: Navigation, Music, optional Jellyfin, Radio, All apps, plus apps pinned from All Apps. Settings lives next to Wi-Fi in the top-right status area, outside the grid. - Navigation/Music/Radio are **app slots** with show/hide switches. They can be assigned, changed and cleared. Jellyfin is a built-in optional music source (no app assignment): a dashboard, album page, grouped genres and an in-app Media3 player. All apps is always present and last. - Pinned apps are added by long-pressing an app in All Apps and removed by long-pressing the added button on Home. Only pinned apps show their own icon by default; the fixed buttons and predefined slots stay stylized. - The grid adapts from 2 to 6 columns, shrinks its tiles as the count grows, and is anchored to the bottom edge so a short row never floats mid-page. - Every external app opens full-screen through a normal Android `Intent`. - Settings: light/dark/automatic theme, six accent colors, battery visibility, fixed night window, home-button switches, slot assignment, HOME-role setup, four-digit PIN, plus separate **OBD-II**, **Speed** and **Temperature** cards. - The stylized Music tile shows an animated equalizer while another app is playing; the Jellyfin tile shows it only while CarDash's Jellyfin player plays. - Settings → Media grants notification access for the active MediaSession title, duration and position. CarDash does not read notification content. - The long-press hint is visible only during the first ten minutes after a fresh installation, then never returns. Upgraded installs are migrated to expired. - The Home-role banner is gone; a one-time first-run tip points to Settings. - Long-pressing a pinned app opens Rename, Change icon and Remove. Rename/icon changes persist; Remove needs no confirmation. - Optional OBD-II over a paired Bluetooth Classic ELM327: digital speed, throttle bar, rolling 5 km economy (ECU fuel rate, MAF fallback) and ambient temperature. - Speed card with a show/hide switch and, while OBD is selected, an OBD-II/GPS source switch. GPS is the fallback; stationary shows 0 km/h. - Temperature row with a show/hide switch and, while OBD is selected, an OBD-II/weather source switch. Open-Meteo weather + location is the fallback; red below 3°C. - Non-blocking Gogs update check with a Settings → Updates card and a release-page link; no automatic download or silent installation. - Dark surfaces are tinted by the selected accent instead of retaining a green base. - PIN is requested **only on a cold start**, never on resume or screen-off. - The screen stays awake while CarDash is the foreground window. - Landscape-only; tablet-first 1280×800, also verified at 1280×720; Android 10 minimum. - Settings keeps the display awake by default; the user can switch to the system timeout. - Long-press on empty Home enters a black night overlay with a very dim clock; tap wakes. - Package: `com.wasp.cardash_launcher`. Display name: **CarDash**. ## 2. Decisions and why | Decision | Choice | Rationale | |---|---|---| | Central app content | Launcher-native; apps open full-screen | Android cannot embed another app; overlay chrome was rejected as fragile and app-obstructing. | | UI chrome | None (no bars) | Removes the mismatch between launcher chrome and full-screen apps; cleaner, more compatible. | | Home layout | Single page, info + volume + up to 16 buttons | Matches the refined brief; tiles scale from 2 to 6 columns. | | App slots | Navigation, Music, Radio with visibility switches | Kept from the original brief; All apps is fixed. | | Pinned apps | All Apps long-press adds, Home long-press removes | Makes any installed app reachable from Home without editing a separate layout screen. | | Home cap | 16 buttons total, including All apps; Settings is outside the grid | Keeps the single page legible at tablet resolutions; enforced in `LauncherController`. | | Settings placement | Gear next to Wi-Fi in `StatusCluster` | Keeps Settings reachable without spending a grid slot. | | Playback control | Circled volume ring is play/pause; Mute pill remains | One prominent control for play/resume/pause without adding another card. | | Volume control | FYT/Syu MCU sound module, then absolute `setStreamVolume`, UI-panel write, relative `adjustStreamVolume`, fixed-policy detection | Car amplifiers ignore the Android stream on many Chinese units; the MCU module is the only app-reachable master volume there. | | Icons | Only pinned apps show their own icon by default; fixed slots stay stylized | Keeps the dashboard consistent while making added apps recognizable. | | Accent | Six curated choices, persisted in Settings | Changes the whole theme without a color picker or arbitrary contrast failures. | | Battery visibility | One switch hides level and charging bolt | One control for both indicators, as requested. | | Media feedback | Stylized Music tile + animated bars while media is active | Compact visual feedback without adding a media control surface. | | Now-playing metadata | MediaSessionManager via a notification-listener component | The only public path to title, duration and position; read-only and scoped to media sessions. | | Song progress | Ring fraction from position/duration; scrolling title | Replaces the volume ring as requested; paused state holds position and scroll offset. | | Hint lifetime | Ten minutes from the first launch after a fresh install, then never | Avoids permanent onboarding chrome; upgraded installs migrate to expired. | | Home role | Settings row only; one-time first-run tip | Removes permanent dashboard chrome while keeping discovery. | | Pinned editing | Long-press menu: Rename / Change icon / Remove | Keeps the home page clean; removal remains immediate. | | Custom icon source | Default app icon plus 29 bundled monochrome line icons | Matches the stylized dashboard and follows light/dark theme colors. | | OBD transport | Bluetooth Classic SPP, bonded ELM327 only, read-only | No scanning, BLE, USB or Wi-Fi; one native worker serializes requests. | | OBD values | Speed, throttle (`49`/`11`), fuel rate (`5E`), MAF (`10`), ambient (`46`) | The requested dashboard subset; unsupported PIDs stay hidden. | | Economy | 5 km rolling window; ECU fuel rate first, petrol MAF fallback with fuel-cut guard | Matches the car's trip computer on vehicles that report `5E`; show `—` until 0.5 km. | | Weather fallback | Open-Meteo + one-shot device location via the Temperature card | No API key/account; hide the row on any failure. | | GPS speed fallback | Poll GPS when speed source is GPS, OBD is unavailable, or the Speed card is off | Asks for location when speed is enabled with GPS as the source; 0 below 2 km/h to suppress stationary drift. | | Speed source | OBD-II preferred while connected; GPS fallback, switch in Settings | The user can force GPS even with an adapter connected. | | Update check | Public Gogs releases API, non-blocking startup check, Settings card | Update downloads the APK and opens Android’s installer; installation stays user-confirmed. Yellow dot on Settings gear when available. | | Temperature priority | OBD ambient while its source switch is on, else location weather | OBD wins when fresh; weather is the fallback. | | Settings cards | Separate OBD-II, Speed and Temperature cards | Adapter settings stay isolated from display/source choices. | | Accent background | Dark canvas/surfaces/borders blend toward the accent | Fixes the green base showing through when Blue is selected. | | Screen | `FLAG_KEEP_SCREEN_ON` controlled by the Settings keep-awake switch | Default on; no wake-lock permission, and off restores the system display timeout. | | Night mode | Long-press empty background; black overlay + dim clock; tap wakes | Quiet night display without changing panel brightness or adding a wake lock. | | Theme | Light / Dark / Automatic with fixed 19:00–07:00 window | No location permission or network; time pickers make the window adjustable. | | PIN | Four digits, salted PBKDF2 hash, cold start only | Matches the explicit instruction; no re-lock while running. | | Min Android | API 29 (Android 10) | Requested minimum; no in-scope API requires newer. | | Target/compile | 36 | Current Flutter toolchain defaults; runs on API 29. | | Orientation | `sensorLandscape` + Dart orientation lock | Car/tablet form factor; portrait is intentionally unsupported. | | AVD | 10.1" WXGA tablet, 1280×800, 160 dpi, API 35 and API 29 | Matches a dashboard tablet and the reference resolution class. | | HOME role | Standard `RoleManager` request + Settings row | A launcher must be chosen as the system home app; never silently forced. | | Overlay/kiosk/root | Out of scope | Rejected for compatibility. | ## 3. Architecture ``` Flutter HomeScreen ClockBlock / StatusCluster / VolumeCard / ShortcutGrid LauncherController -> apps, slots, pinned apps, 16-button cap, default-home state SystemController -> battery/Wi-Fi/volume/media state + media keys DisplayController -> persisted keep-awake window flag GpsSpeedController -> GPS speed fallback (permission requested from Speed settings) TelemetrySettingsController -> speed/temperature visibility + sources UpdateController -> public Gogs release check + release-page link JellyfinController -> Quick Connect pairing, token reuse, retry/offline state, genre selections JellyfinDashboardScreen -> JellyfinDashboardController -> JellyfinLibraryService JellyfinAlbumScreen -> track list + JellyfinPlaybackController JellyfinPlaybackController -> JellyfinPlaybackPlatform -> NativeJellyfinPlayback HomeScreen -> NightModeOverlay on empty-background long press AllAppsScreen / AppPickerSheet SettingsScreen -> General / Media / Jellyfin / OBD-II / Speed / Temperature / Security / Updates PinSetupScreen / LockScreen -> LockController -> PinService Android (Kotlin) MainActivity -> keep-screen-on, volume keys, permission forwarding -> LauncherBridge (packages, icons, system flag, role) -> SystemBridge (battery, Wi-Fi, volume, media activity, keys) -> MediaBridge (MediaSession title/progress) + NotificationListener -> TelemetryBridge (Bluetooth devices, OBD, location) -> ObdService (Classic SPP ELM327 worker) -> JellyfinPlaybackBridge -> MediaController -> JellyfinPlaybackService (Media3 ExoPlayer, MediaSession, notification, JellyfinPlaybackReporter) ``` - `SettingsRepository` is the only persistence path for theme, night window, slot visibility/assignments, pinned apps, onboarding time, speed/temperature visibility and source choices, the Jellyfin switch/URL and the selected genre families. The Jellyfin access token and the salted PIN hash live in `flutter_secure_storage`. - Package enumeration and icon rendering happen on a worker thread in Kotlin and are returned as PNG bytes. - Timers for volume/status are stopped when the app leaves the foreground. - The PIN check is awaited before `runApp`, so there is no dashboard flash on a locked cold start. - Jellyfin uses Quick Connect only. The pairing check runs after `runApp`, and a missing network retries with backoff instead of blocking the dashboard. `configured` (URL + saved token) gates the Home switch; `connected` gates network actions. - The Jellyfin dashboard asks for 6 albums per row and shows 6 columns when each card is at least 160 px wide, otherwise 5. Album labels share the cover's left edge; the album page cover is top-aligned at 21% of the body width (140–252 px); a Random selection re-rolls that row after playback starts. The album navigation card holds the 69 px favorite heart on the left, the transport in the centre and three 60 px volume buttons on the right. Both Jellyfin headers use an 83 px back button and a top-right time / speed / temperature / Home strip. - Jellyfin playback is Media3 1.11.1 in a foreground `mediaPlayback` service. The Dart controller owns the queue and polls state; the service owns audio focus, the notification and playback reporting. It pauses on permanent focus loss, resumes after transient loss, and stops at album end. - Network access is limited to the optional Open-Meteo weather fallback, the user-configured Jellyfin server (library, artwork, audio), and the Gogs release check; no telemetry. ## 4. Milestones | # | Milestone | State | |---|---|---| | M0 | Local git repo, Flutter scaffold, launcher config, package ID, icons, docs | Done | | M1 | x86_64 API 35/API 29 images, 1280×800 landscape tablet AVDs, emulator/install scripts | Done | | M2 | Theme system, single home layout, clock/status, volume card, shortcut grid | Done (initial visual pass) | | M3 | Kotlin bridges: app list/icons/launch, HOME role, volume, battery/Wi-Fi | Done | | M4 | App picker, All Apps, slot persistence, long-press flows, launch errors | Done | | M5 | Settings, theme auto window, PIN setup/change/remove, cold-start lock | Done | | M6 | Interaction testing, light/dark screenshots, layout polish, API 29 verification | Done | | M7 | Release build, final docs, screenshots, last commits | Done | | M8 | Home-button visibility switches, All Apps pinning, 16-button adaptive grid, keep-awake | Done | | M9 | Accent colors, battery toggle, fading hint, user icons, Media equalizer, install rule | Done | | M10 | Remove Home-role banner, one-time first-run tip, pinned Rename/icon/remove menu | Done | | M11 | Restore stylized fixed icons; pinned apps show own icons by default; 29-icon selector | Done | | M12 | Accent-tinted dark theme, OBD-II adapter/telemetry, location weather fallback, Car data Settings | Done | | M13 | Play/pause volume ring, Settings gear in status area, 16-button cap without Settings | Done | | M14 | Song-progress ring, scrolling now-playing title, notification-access Settings card | Done | | M15 | Keep-awake Settings switch (default on), night mode, 1280×720 layout | Done | | M16 | GPS speed fallback when no OBD adapter and location is already allowed | Done | | M17 | Non-blocking Gogs update check; 1.0.1 version-only update-test release | Done | | M18 | Bluetooth-off settings action; 1.0.2 release | Done | | M19 | OBD-II/Speed/Temperature Settings cards; source switches; 10-minute first-install hint; 1.0.3 | Done | | M20 | Fixed-volume car detection; relative volume fallback; 1.0.4 | Done | | M21 | On-screen volume diagnostics and stream snapshots; 1.0.5 | Done | | M22 | Bluetooth/OBD freeze hardening: UI-thread offload, device filtering, bounded retries, resume debounce; 1.0.6 | Done | | M23 | Move the Impeller opt-out into `` so Skia is actually used; 1.0.7 | Done | | M24 | Jellyfin 1.2.0: Home slot, dashboard/genres, album page, Media3 playback, background notification, audio focus, tests and release | Done | ## 5. Verification checklist - [x] `flutter analyze` clean. - [x] Unit/widget tests pass (77 tests, including OBD device filtering, bounded retries, resume debounce and the application-scoped Impeller opt-out). - [x] Debug APK builds with JDK 21. - [x] Installs on API 35 1280×800 tablet. - [x] HOME role granted; Home opens CarDash. - [x] Immersive full-screen; no Android bars. - [x] Real app list and icons in All Apps. - [x] App picker assign / long-press change / clear, with persistence across restart. - [x] Launch Navigation/Music/Radio full-screen and return Home (verified with Maps and YouTube Music). - [x] Volume slider, mute and hardware-key synchronization (keys are routed to `STREAM_MUSIC` while the dashboard is foreground). - [x] Fixed-volume car units disable the slider and pass hardware keys through; absolute-write failures fall back to relative stream steps. - [x] Volume diagnostics expose raw stream values and last-write results on screen and under the `CarDashVolume` logcat tag. - [x] Light theme, dark theme and automatic switch. - [x] PIN set/change/remove; cold start shows lock; wrong PIN; no re-lock after returning from an app. - [x] Verify 1024×600 compact layout (`adb shell wm size 1024x600`). - [x] Boot and verify on Android 10 (API 29) AVD, including PIN and release APK. - [x] Release APK on both AVDs (54 MB universal APK). - [x] Navigation/Music/Radio switches hide/show buttons, persist, and preserve assignments. - [x] All Apps long-press pins apps; the 16-button cap rejects a 17th button. - [x] Long-press on an added app removes it; it can be re-added from All Apps. - [x] 16 buttons fit at 1280×800, 1280×720 and 1024×600; the grid uses 2–6 columns. - [x] `FLAG_KEEP_SCREEN_ON` follows the keep-awake switch: present by default, cleared when off and restored when on. - [x] Long-press on empty Home enters night mode; card/button long-presses are unaffected; tapping wakes. - [x] Six accent choices change the Material theme immediately and persist. - [x] Battery switch hides both the battery level and charging bolt. - [x] Settings is next to Wi-Fi; All apps is the last grid button. - [x] A user-installed app (F-Droid) shows its own icon; system apps stay generic. - [x] Music equalizer widget animates; native media-active polling is wired. - [x] Home hint is gone ten minutes after the first launch of a fresh install. - [x] All Apps search icon is visibly smaller. - [x] No Home-role banner; the first-run tip appears once, then never again. - [x] Long-press pinned app shows Rename, Change icon and Remove. - [x] Rename updates the Home label; Change icon persists a stylized icon; Remove is immediate. - [x] Fixed buttons stay stylized; pinned apps show their own icons by default. - [x] Change icon offers Default plus 29 monochrome icons and applies the chosen icon. - [x] Accent-tinted dark background changes from green to blue when Blue is chosen. - [x] OBD Settings device dropdown and Bluetooth permission action are present. - [x] OBD economy/card tests pass (ECU fuel rate and MAF fallback); native ELM327 path compiles. - [x] Location permission, reverse geocoding and Open-Meteo fallback verified live (Ukiah, 17°C). - [x] Temperature row renders below the date and follows the selected OBD/weather source. - [x] Circled ring shows song progress; 44 tests include media keys, metadata, progress, the marquee, the ring, keep-awake and night mode. - [x] Scrolling title/artist verified with a live Chrome MediaSession; pause holds both the ring and scroll position, resume continues. - [x] Settings → Media grants notification access and shows Granted. - [x] Settings gear is next to Wi-Fi; the grid ends with All apps. - [x] GPS speed shows 0 km/h when stationary with no OBD adapter; enabling speed asks for location permission. - [x] 1.0.0 updater build installed on emulator/tablet; it reported up to date before 1.0.1. - [x] 1.0.1 universal APK built and added to `releases/`; release published for update testing. - [x] Emulator completed the full in-app 1.0.0 → 1.0.1 download/installer flow. - [x] Yellow update dot appears on the Settings gear; 66 tests pass. - [x] Bluetooth-off Settings card exposes **Open Bluetooth settings** and refreshes on return. - [x] 1.0.2 universal APK built and published for in-app update. - [x] Settings split into separate OBD-II, Speed and Temperature cards; OBD-II holds adapter settings only. - [x] Speed and Temperature toggles hide/show their Home elements and persist. - [x] OBD/GPS source switches appear while an adapter is selected and persist. - [x] Onboarding hint is limited to ten minutes after a fresh install; upgraded installs never show it. - [x] 66 tests pass, including the onboarding window and source preferences. - [x] 1.0.3 universal APK built and published for in-app update. - [x] Jellyfin Settings card: enable switch, URL entry, Quick Connect code, saved-token reuse, status, offline retry and token invalidation covered by unit/widget tests. - [x] Jellyfin Home switch after Music, disabled/off until configured, counts toward the 16-button cap and clears on disconnect. - [x] Jellyfin dashboard: Albums/Artists strip, Recently played / Recently added / Random, grouped genres, 6 cards when they fit and 5 otherwise, Random re-shuffle after selection. - [x] Jellyfin album page: top-aligned cover, track list, current track, previous/next, play/pause and volume. - [x] Media3 playback: background foreground service, media notification, hardware media keys, audio-focus pause/resume, end-of-album stop and Jellyfin playback reporting. - [x] Exclusive Music/Jellyfin equalizers and the Jellyfin tile subtitle clipped to the button width. - [x] 134 tests pass, including the Jellyfin API contract, genre catalog, controllers, album page, dashboard and genre picker. - [x] 1.2.0 universal APK built and published for in-app update. - [x] Tablet has the 1.0.0 installer build; unknown-app confirmation is user-controlled. - [x] Screenshots in `docs/screenshots/`; README/AGENTS/plan/OBD doc updated. ## 6. Known risks and mitigations 1. **Media-volume polling** is every 900 ms while foreground. It is cheap and handles hardware keys, but a future native `ContentObserver` would remove the polling. Play/pause uses standard Android media keys. 2. **`QUERY_ALL_PACKAGES`** is required for a launcher app list. It is documented and acceptable for sideloading; a Play distribution needs policy work. 3. **PIN security scope**: cold-start only by design; it does not replace the Android keyguard or protect against adb/data extraction. README explains it. 4. **Emulator stability**: headless emulators can be killed by the host; scripts use managed boot and the host GPU. SwiftShader is only a fallback. 5. **Flutter 3.47 / AGP 9.1 / JDK 21**: works on this machine; Java 27 is not the validated toolchain. The emulator uses the host GPU; SwiftShader segfaulted on the API 35 image, so the helper script defaults to `-gpu host`. 6. **16-button density**: small labels and mixed pinned-app icons are the trade-off for a single page. The Settings count and grid scaling make the limit explicit. 7. **Screen-on battery use**: the flag applies only while the dashboard is foreground; it is not a background wake lock. 8. **Media metadata**: the Music equalizer follows `AudioManager.isMusicActive` and is suppressed while Jellyfin plays and while the active system session is CarDash's own package. That package guard prevents a flash while a Jellyfin pause propagates through the 900 ms system poll. Jellyfin title/progress come from its own controller. Other apps' title/progress query the active MediaSession through the notification-listener component, which is user-granted and scoped to media metadata. 9. **Pinned customizations**: Rename affects only the Home label. Default restores the app icon; a chosen stylized icon is a bundled asset and cannot become unavailable. 10. **OBD hardware is untested here.** No ELM327 adapter is available on this machine; the native socket/protocol path compiles and the parser/economy UI is unit-tested, but a real-car soak test is still required. 11. **Weather depends on location, permission and network.** Any failure hides the row; OBD ambient is always preferred when available. 12. **Uninstall/reinstall** may drop the HOME role; Settings and the installer script can restore it. 13. **Jellyfin 12.2 API differences** from older docs: keep the verified endpoints and auth header in `JellyfinService`; recently played is derived from played Audio tracks because album `UserData` is not updated by playback. Parse defensively. 14. **Media3/audio focus**: the service pauses and keeps position on permanent focus loss and resumes only after a transient loss. The notification needs POST_NOTIFICATIONS on Android 13+; playback works when it is denied. Test on API 29/34/35 and a real tablet. 15. **Jellyfin layout density**: 6 cards only when each is at least 160 px wide; otherwise 5. Labels are clipped to the card/button width so long album names cannot break the grid. 16. **Jellyfin genre grouping is heuristic**: built-in families cover the known tags (including Cyrillic); unknown tags become standalone. The picker is capped at five families. 17. **High-resolution Jellyfin audio on a car head unit**: direct play can sound slow or quiet on some DSP/audio HAL combinations. The staged streaming quality setting offers 320/256/192/128 kbps MP3 transcodes; verify which one fixes the user's unit. The choice applies to the next album queue. ## 7. Out of scope (for now) - Embedded third-party apps, overlay bars, split screen, kiosk/device-owner. - Global play/pause or now-playing metadata. - Wi-Fi/BLE/USB OBD adapters, DTC contents, RPM/coolant/voltage/MIL, fuel level/range. - Calls, climate, reverse camera. - An app drawer with folders/pages; a single searchable All Apps page is enough. - Play Store packaging/signing, remote git, CI. - Jellyfin Artists tab, artist/album browsing and search. - Jellyfin offline caching/downloads; queue persistence across process death. - Data-aware automatic streaming quality (Wi-Fi vs metered). A manual Direct/320/256/192/128 kbps choice is staged in the next build. - User-editable genre mapping, gapless playback and richer metadata. ## 8. Change log - 2026-11-01: CarDash 1.2.6+26 uses the stacked Jellyfin album layout at every resolution: cover and controls on top, track list below. The cover is 30% of the body width clamped to 200–360 px, so larger screens get a larger cover; the wide three-column layout is gone. The album header shows the release year in parentheses after the name, e.g. `Ascension (2025)`, without duplicating a year already present. Includes the 1.2.5 bottom alignment and larger controls. 150 tests pass; release `releases/cardash-android-1.2.6.apk`. - 2026-11-01: CarDash 1.2.5+25 aligns the Jellyfin album progress/transport card with the bottom of the album cover in both wide and narrow layouts. The previous/play-pause/next buttons grow to 62/80 px and the compact volume steps to 40 px. The album page still has one seekable slider with elapsed / total time and the compact −/Mute/+ card beside the now-playing block. 150 tests pass; release `releases/cardash-android-1.2.5.apk`. - 2026-11-01: CarDash 1.2.4+24 redesigns the Jellyfin album controls. The old ring card is replaced by one seekable song-progress slider with elapsed / total time, and a centered previous / play-pause / next row. A compact volume card (−, Mute / Unmute, +) sits to the right of the now-playing block. The album slider seeks through `JellyfinPlaybackController.seek` and keeps a local drag value until the next poll. Includes the 1.2.3 recents/normal-app work and the Jellyfin progress-duration fallback. 150 tests pass; release `releases/cardash-android-1.2.4.apk`. - 2026-11-01: CarDash 1.2.3+23 makes the launcher usable as a normal app as well as a HOME app. The activity no longer sets `excludeFromRecents`, so it appears in the recents/overview list and can be switched back to. `resumeWhilePausing` and `alwaysRetainTaskState` were added for a cleaner Home transition and task retention. Android can still kill a background app; no keep-alive service, `persistent` flag or battery-exemption request was added. On vendor firmware the user must allow autostart/no battery restrictions. Includes all 1.2.2 Syu volume, Jellyfin favorites and streaming-quality work. Release `releases/cardash-android-1.2.3.apk`. - 2026-10-31: adds a Jellyfin **Favorites** tab between Albums and Artists, backed by the Jellyfin favorites API; a scrollable 5/6-column grid and a centered `No favorites` empty state. The album page gains a heart that sets or unsets the server favorite, and the Favorites tab refreshes when you return. The Home Jellyfin tile opens the currently active album with the dashboard underneath, so Back returns to Albums instead of Home. Also adds a **Streaming quality** dropdown: Direct (default) or 320/256/192/128 kbps MP3 transcoding. The quality is persisted in `SettingsRepository`, read when the next album queue is built, and shown in the Jellyfin settings card. Included in 1.2.2. - 2026-10-31: CarDash 1.2.1+21 fixes the brief Music-tile equalizer flash after pausing Jellyfin from the Home card. The Music equalizer now ignores CarDash's own stale system MediaSession while a Jellyfin pause propagates, and the Home card stays on the paused Jellyfin queue during that gap. 139 tests pass; release `releases/cardash-android-1.2.1.apk`. - 2026-10-31: CarDash 1.2.0+20 adds the full optional Jellyfin music source. The earlier Settings-only Quick Connect work now feeds a Home tile after Music, a dashboard (Recently played / Recently added / Random, six cards when they fit and five otherwise, grouped genre families, Random re-shuffle), and an album page with a top-aligned cover, track list, previous/next, play/pause and volume. Playback is Media3 1.11.1 in a foreground `mediaPlayback` service with a media notification, background playback, hardware media keys, audio-focus pause/resume, end-of-album stop and Jellyfin playback reporting. The Music/Jellyfin equalizers are exclusive and the home tile subtitle is clipped to the button width. 134 tests pass; the universal release is `releases/cardash-android-1.2.0.apk`. - 2026-10-31: Added the optional Settings → Jellyfin integration. A switch and server URL drive the Quick Connect flow; the returned token is kept in secure storage and reused on later starts. The card shows connecting, waiting, connected, offline and error states. A rejected token disables the integration and clears the token; an unreachable server retries with 5–60 s backoff. 84 tests passed at this intermediate step. - 2026-10-29: Pivoted from an overlay/head-unit chrome design to a launcher-native single home page, no top/bottom bars, full-screen external apps. Implemented core UI, Kotlin bridges, settings, PIN and documentation. Verified initial home, app picker, All Apps, volume, themes and cold-start PIN on the API 35 AVD; then verified the release build and PIN flows on the API 29 AVD. Added hardware volume-key routing and PIN verification progress. Screenshots captured at 1280×800 and 1024×600. - 2026-10-29: Added home-button visibility switches, All Apps long-press pinning, a 16-button cap with an adaptive 2–6 column grid, pinned-app removal, and a foreground keep-screen-on flag. Verified 17 tests, 16-button layouts at 1280×800 and 1024×600, cap/removal/re-add, and 0.2.0 on the physical tablet. - 2026-10-29: Added six accent choices, battery/charging visibility, a two-minute fading hint anchored to process start, All apps last, user-app icons, a media-active equalizer, and a smaller All Apps search icon. Added the two-attempt ADB install rule. Verified 21 tests, accent/battery toggles, a user-app icon (F-Droid), hint expiry, and 0.3.0 on the emulator/tablet. - 2026-10-29: Removed the Home-role banner; added a once-only first-run tip. Replaced immediate removal with a pinned-app menu (Rename, Change icon, Remove), custom Home labels and up to 25 installed-app icon choices. Verified 23 tests and the full menu flow at 1280×800; 0.4.0 installed on the tablet. - 2026-10-29: Clarified icon policy: fixed/predefined buttons are always stylized; only pinned apps show their own icon by default. The Change icon grid now offers Default plus 29 bundled monochrome line icons. Verified 0.5.0 on the emulator and tablet. - 2026-10-29: Fixed the dark accent background. Added optional ELM327 OBD-II (speed, throttle, rolling MAF economy, ambient), location/Open-Meteo fallback, Car data Settings with adapter dropdown and weather toggle, and permission flows. Verified 30 tests, live weather on the emulator and 0.6.0 installation; OBD hardware remains a car test. - 2026-10-29: The circled volume ring is now play/pause (media-key resume, or opens the assigned Music app when nothing responds). Settings moved next to Wi-Fi in the status area and no longer consumes a grid slot. Verified 33 tests and 0.7.0 on the emulator and physical tablet. - 2026-10-29: Replaced the volume ring with song progress and added the scrolling now-playing title/artist. Added a MediaSession bridge and a Settings → Media notification-access grant; no notification content is read. Verified metadata, progress, scrolling/pause/resume with a live Chrome media session, 41 tests and 0.8.0 on emulator/tablet. - 2026-10-29: Added a default-on keep-awake switch, a black long-press night mode with a dim clock, and verified the 16-button layout at 1280×720. Verified the native window flag toggles off/on, night mode/card long-press isolation, 44 tests and 0.9.0 on emulator/tablet. - 2026-10-29: Added GPS speed fallback when no OBD adapter is connected and fine location is already allowed. No permission prompt; stationary values below 2 km/h render as 0. Verified 47 tests and 0.10.0 on emulator/tablet. - 2026-10-30: Added a non-blocking Gogs update checker and installed a 1.0.0 updater build on emulator/tablet. Published 1.0.1+13 as a version-only update test; 50 tests passed. The emulator later completed the in-app 1.0.0 → 1.0.1 download and package-installer flow. - 2026-10-30: Added the Bluetooth-off **Open Bluetooth settings** action and release/deployment runbook. Bumped to 1.0.2+14 and published it for in-app update. - 2026-10-30: Split Settings into OBD-II, Speed and Temperature cards, added independent show/hide switches plus OBD/GPS and OBD/weather source switches, and limited the long-press hint to ten minutes after a fresh install. Bumped to 1.0.3+15; 66 tests pass. - 2026-10-30: Added fixed-volume detection and a relative volume-step fallback for car units that ignore absolute `setStreamVolume`; fixed-volume sliders now explain that the vehicle amplifier owns the level. Bumped to 1.0.4+16. - 2026-10-31: Found the emulator's Settings crash was Impeller, not the app's layout. The `EnableImpeller=false` opt-out had been placed under ``, where Flutter silently ignored it; moved it to ``, so 1.0.7 actually uses Skia. Bumped to 1.0.7+19. - 2026-10-31: Hardened the OBD-II/Bluetooth path after a freeze triggered by returning from Bluetooth settings. Bluetooth binder calls now run on a serialized native worker with timeouts; the picker filters to likely OBD-II adapters; connections stop after three failures with a **Retry connection** action; and resume reuses the cached bonded list instead of querying it every time. Bumped to 1.0.6+18. - 2026-10-30: Added a long-press volume diagnostics panel with all stream indices and the last target/actual write, plus `CarDashVolume` logcat output, to identify devices that ignore both absolute and relative volume writes. Bumped to 1.0.5+17; 66 tests pass. - 2026-10-30: Versioned the universal Android release as 1.0.0+12 and added `releases/cardash-android-1.0.0.apk` with checksum and release notes. - 2026-10-29: Centered the now-playing title across the whole media card instead of the right-hand control column. Verified 0.9.1 on emulator/tablet. - 2026-10-31: Enabling **Show speed** with GPS as the source (no OBD adapter selected or OBD speed off) now requests location permission and retries the GPS fallback; a denied request leaves the switch on and shows an explanation. The normal resume path still never prompts. 89 tests pass. - 2026-11-01: Volume against a real FYT/Syu head unit. CarDash now binds the car's `com.syu.ms` toolkit service and drives the sound module's absolute master volume (command 0, module 4, 0–36), so the on-screen slider changes the amplifier instead of the unused Android media stream. The car's own knob and steering-wheel controls stay independent; the Android key path is unchanged. On other units the existing absolute/relative/UI-panel fallbacks remain. Reads that never follow a write no longer snap the slider back: the requested level is kept and shown with a `?`, and the diagnostics panel gained the minimum, control source and vendor state. No new permission, no root and no manifest change. - 2026-11-01: CarDash 1.2.2+22 releases the car-volume work together with the Jellyfin favorites/active-album shortcut and streaming-quality setting. The volume card now prefers the FYT/Syu MCU master level, keeps an unconfirmed write instead of snapping back, and exposes the control source in diagnostics. 149 tests pass; release `releases/cardash-android-1.2.2.apk`. - 2026-11-01: Verified the FYT/Syu MCU volume bridge on the real car (Android 13 Chinese firmware): the on-screen slider changes the amplifier, follows the car's own volume control, and no longer snaps back to 100%. No code change. - 2026-11-02: Fuel economy now prefers the ECU's engine-fuel-rate PID (`5E`) instead of always deriving fuel from MAF/AFR. The MAF path remains a petrol-only fallback. This fixes the high reading on diesel cars, whose lean combustion makes a stoichiometric MAF calculation overestimate fuel. - 2026-11-02: Fixed the 1.2.7 economy regression that left the card at `—`. The one-shot `0140` supported-PID query could race the `ATSP0` protocol search and shift every later ELM327 response by one command. 1.2.9 warms up with `0100`, drains stale input before each command, removes the `0140` query, and probes `5E` in the poll loop every 30 cycles while unsupported. Bumped to 1.2.9+29.