Files
pluriwave/openspec/changes/auto-media-art-quality/proposal.md
T
FreeTLab c193650cc4 fix(auto): fall back to brand art and surface quality on dead/missing favicons
Android Auto no longer copies the launcher icon as placeholder art; it
rotates through the same 4 on-brand station_art assets the phone UI
already uses, keyed by the same per-station hash for visual parity.
Malformed or unusable favicon URLs (including a Dart Uri quirk where
'http://' reports hasAuthority=true with an empty host) now fail the
validity gate instead of being handed to the OS media browser as-is.
Browsable items also show codec/bitrate as a subtitle when known.
2026-07-19 13:06:18 +02:00

4.6 KiB

Proposal: Android Auto Media Art & Quality Polish

Intent

Fast-follow to android-auto-media, closing 3 gaps that parent's verify-report flagged (PASS WITH WARNINGS) plus one requested capability. In the car head unit today: (1) stations whose favicon URL is present but dead render broken/blank art — Android Auto resolves artUri OUTSIDE the Flutter engine, so the phone's CachedNetworkImage.errorWidget fallback never runs (only empty-favicon Case A is handled); (2) the fallback art is a byte-for-byte copy of the launcher icon, reading as a bug; (3) browse items expose no audio-quality hint even though Emisora.codec/bitrate already exist. This picks up the "fallback artwork for stations without favicons" work deferred in engram #2290.

Scope

In Scope

  • A — Degrade Android Auto art gracefully when favicon is present but unreachable (Case B), not only empty (Case A).
  • B — Replace launcher-icon placeholder with the 4 on-brand station_art_* assets, reusing _fallbackArtFor's deterministic per-station hash-by-uuid rotation for phone/car parity. Requires native Android drawable copies of the 4 PNGs (same android.resource:// technique as existing default_station_art.png).
  • C — Surface codec + bitrate (e.g. "128 kbps · MP3") via MediaItem.displaySubtitle on browse items; omit gracefully when unknown (never "null kbps").
  • Strict-TDD Dart tests for rotation selection, subtitle formatting, and Case-B fallback path.

Out of Scope

  • Commissioning brand-new custom artwork (no image-gen tooling here, #2290; reuse existing approved assets — external follow-up may add custom art later).
  • iOS CarPlay; voice search; phone-UI art changes (already correct).
  • flutter build/analyze/gen-l10n (hang in this environment).

Capabilities

New Capabilities

  • None.

Modified Capabilities

  • android-auto-media: browse-item requirements extended — artwork MUST degrade gracefully for both empty AND unreachable favicons using on-brand rotating placeholders, and playable items SHOULD carry an audio-quality subtitle.

Approach

Reuse, don't rebuild — mirror the phone UI. Port _fallbackArtFor's uuid-hash rotation into navegacion_auto.dart so _artUriPara() picks one of 4 native drawables. Add displaySubtitle in itemEmisora() from existing codec/bitrate. Deferred to design.md — the real open question: HOW to detect Case B (dead URL) with no Flutter image widget in the OS browser loop. Design must weigh proactive reachability check + cache (network cost, staleness, invalidation) vs. piggybacking the phone's CachedNetworkImage error signal (misses car-first-open stations) vs. documenting the gap as a known limitation this iteration.

Affected Areas

Area Impact Description
lib/servicios/navegacion_auto.dart Modified _artUriPara() rotation + Case-B fallback; itemEmisora() subtitle
android/app/src/main/res/drawable/station_art_*.png New 4 native copies of brand assets
android/app/src/main/res/drawable/default_station_art.png Removed/Kept Retire launcher-icon copy (design confirms)
test/ (Dart) New Rotation, subtitle, fallback tests

Risks

Risk Likelihood Mitigation
Case-B detection adds unbounded network cost Med Design phase decides; may accept as documented gap
Native drawable / asset drift over time Low Comment linking copies to assets/images/ source
Malformed codec/bitrate → ugly subtitle Low Format guard; omit when either is unknown

Split Judgment

Keep as ONE change: A+B+C all touch navegacion_auto.dart browse-item construction and share tests. If design finds Case-B reachability checking too costly/complex, split A into its own follow-up and ship B+C (pure, low-risk parity + subtitle) first.

Rollback Plan

Additive. Revert PR: restore _artUriPara(), drop displaySubtitle, delete the 4 native drawables (and restore default_station_art.png if retired), remove new tests. Phone UI and audio pipeline untouched — zero residual state.

Dependencies

  • audio_service ^0.18.18 (present; MediaItem.displaySubtitle).
  • Existing assets/images/station_art_*.png (source for native copies).

Success Criteria

  • Unreachable favicon shows on-brand art in car, not broken/blank (or gap documented).
  • Fallback uses rotating station_art_*, matching phone per-station selection.
  • Launcher-icon-as-art no longer appears.
  • Browse items show quality subtitle; unknown quality omits cleanly.
  • Dart tests pass under flutter test.