fix(auto): fall back to on-brand artwork when a station or track has none
Stations and tracks with no artwork showed empty tiles in the car. The browse tree's itemEmisora/_itemLocal already fell back to the rotating station_art_* drawable via artUriPara/artUriLocal, but the "now playing" MediaItem built when actually playing something (car tap, phone-initiated play, folder-queue advance, direct local-track tap) did not, so the car's now-playing screen still went blank. Reuse the SAME artUriPara/artUriLocal fallback (already the project's one selection scheme, mirroring PluriStationArtFallback) at every "now playing" construction site: reproducirPorMediaId, ServicioAudio.reproducir (now via the extracted, unit-tested mediaItemParaEmisora), construirMediaItemColaLocal and reproducirPistaLocal. Guard the reverse direction too: emisoraDesdeMediaItem (extracted from the handler's private method, now unit-tested) only reflects artUri back into Emisora.favicon when it passes faviconUsable, so the phone UI's CachedNetworkImage widgets never attempt a doomed fetch of the car's android.resource:// fallback URI -- they keep falling back to PluriStationArtFallback exactly as before.
This commit is contained in:
@@ -836,10 +836,12 @@ Future<void> reproducirPorMediaId(
|
||||
title: emisora.nombre,
|
||||
artist: emisora.pais ?? '',
|
||||
album: 'PluriWave',
|
||||
artUri:
|
||||
emisora.favicon != null && emisora.favicon!.isNotEmpty
|
||||
? Uri.tryParse(emisora.favicon!)
|
||||
: null,
|
||||
// Item 3: reuses [artUriPara] (the SAME fallback the browse tree's
|
||||
// itemEmisora already applies) so the "now playing" media item never
|
||||
// falls back to a blank tile — a real usable favicon still wins, a
|
||||
// missing/unusable one gets the on-brand rotating drawable instead of
|
||||
// `null`.
|
||||
artUri: Uri.parse(artUriPara(emisora)),
|
||||
extras: {'uuid': emisora.uuid},
|
||||
);
|
||||
await reproducir(item);
|
||||
@@ -1143,6 +1145,11 @@ Future<MediaItem?> construirMediaItemColaLocal(
|
||||
id: contentUri,
|
||||
title: _tituloDesdeDocumentId(nodo.documentId),
|
||||
album: 'PluriWave',
|
||||
// Item 3: a queued local track had NO artUri at all before — reuses
|
||||
// [artUriLocal] (the SAME on-brand rotation the browse tree's
|
||||
// `_itemLocal` already falls back to) so the car's now-playing screen
|
||||
// never shows a blank tile for a track with no embedded art.
|
||||
artUri: Uri.parse(artUriLocal(nodo.documentId)),
|
||||
extras: {'documentId': nodo.documentId},
|
||||
);
|
||||
}
|
||||
@@ -1322,6 +1329,9 @@ Future<void> reproducirPistaLocal(
|
||||
id: pista.contentUri,
|
||||
title: pista.titulo,
|
||||
album: 'PluriWave',
|
||||
// Item 3: same fallback as construirMediaItemColaLocal, for a track
|
||||
// tapped directly (not via a folder-play queue).
|
||||
artUri: Uri.parse(artUriLocal(pista.documentId)),
|
||||
extras: {'documentId': pista.documentId},
|
||||
);
|
||||
await reproducir(item);
|
||||
|
||||
@@ -60,6 +60,46 @@ void registrarFuenteMusicaLocal(FuenteMusicaLocalAuto fuente) {
|
||||
_fuenteMusicaLocalGlobal = fuente;
|
||||
}
|
||||
|
||||
/// Builds the phone-initiated "play a station" `MediaItem` (item 3, Android
|
||||
/// Auto fallback artwork): reuses [artUriPara] (`navegacion_auto.dart`) so a
|
||||
/// station with no usable favicon gets the SAME on-brand rotating fallback
|
||||
/// the browse tree and the car-tap path already show, instead of a blank
|
||||
/// tile on the car/lockscreen/notification. Pure — no [PluriWaveAudioHandler]
|
||||
/// dependency — so it is unit-testable without instantiating the handler.
|
||||
MediaItem mediaItemParaEmisora(Emisora emisora, {required AppLocalizations l10n}) {
|
||||
return MediaItem(
|
||||
id: emisora.url,
|
||||
title: localizedStationName(l10n, emisora.nombre),
|
||||
artist: emisora.pais ?? '',
|
||||
album: 'PluriWave',
|
||||
artUri: Uri.parse(artUriPara(emisora)),
|
||||
extras: {'uuid': emisora.uuid},
|
||||
);
|
||||
}
|
||||
|
||||
/// Reconstructs the phone-side [Emisora] from the handler's current
|
||||
/// [MediaItem] (item 3): gates `favicon` through [faviconUsable]
|
||||
/// (`navegacion_auto.dart`) so a car/car-tap "now playing" item's on-brand
|
||||
/// FALLBACK `artUri` (an `android.resource://` drawable, never a real
|
||||
/// favicon) is never misread as a genuine station favicon — the phone UI's
|
||||
/// `CachedNetworkImage` widgets gate only on `favicon != null && isNotEmpty`
|
||||
/// (not on `faviconUsable`'s scheme check), so without this guard they would
|
||||
/// attempt a doomed network fetch of the fallback's non-http URI before
|
||||
/// falling back to [PluriStationArtFallback] themselves. A genuine http(s)
|
||||
/// favicon still round-trips exactly as before. Pure — no handler
|
||||
/// dependency — unit-testable directly.
|
||||
Emisora emisoraDesdeMediaItem(MediaItem mediaItem) {
|
||||
final uuid = mediaItem.extras?['uuid'] as String? ?? mediaItem.id;
|
||||
final artUriTexto = mediaItem.artUri?.toString();
|
||||
return Emisora(
|
||||
uuid: uuid,
|
||||
nombre: mediaItem.title,
|
||||
url: mediaItem.id,
|
||||
pais: (mediaItem.artist?.isNotEmpty ?? false) ? mediaItem.artist : null,
|
||||
favicon: faviconUsable(artUriTexto) ? artUriTexto : null,
|
||||
);
|
||||
}
|
||||
|
||||
/// Wrapper de alto nivel para el UI.
|
||||
class ServicioAudio {
|
||||
PluriWaveAudioHandler get _handler {
|
||||
@@ -94,19 +134,9 @@ class ServicioAudio {
|
||||
});
|
||||
|
||||
Future<void> reproducir(Emisora emisora) async {
|
||||
final item = MediaItem(
|
||||
id: emisora.url,
|
||||
title: localizedStationName(
|
||||
lookupAppLocalizations(const Locale('es')),
|
||||
emisora.nombre,
|
||||
),
|
||||
artist: emisora.pais ?? '',
|
||||
album: 'PluriWave',
|
||||
artUri:
|
||||
emisora.favicon != null && emisora.favicon!.isNotEmpty
|
||||
? Uri.tryParse(emisora.favicon!)
|
||||
: null,
|
||||
extras: {'uuid': emisora.uuid},
|
||||
final item = mediaItemParaEmisora(
|
||||
emisora,
|
||||
l10n: lookupAppLocalizations(const Locale('es')),
|
||||
);
|
||||
await _handler.playMediaItem(item);
|
||||
}
|
||||
@@ -911,14 +941,10 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
||||
}
|
||||
|
||||
Emisora _emisoraDesdeMediaItem(MediaItem mediaItem) {
|
||||
final uuid = mediaItem.extras?['uuid'] as String? ?? mediaItem.id;
|
||||
return Emisora(
|
||||
uuid: uuid,
|
||||
nombre: mediaItem.title,
|
||||
url: mediaItem.id,
|
||||
pais: (mediaItem.artist?.isNotEmpty ?? false) ? mediaItem.artist : null,
|
||||
favicon: mediaItem.artUri?.toString(),
|
||||
);
|
||||
// Item 3: delegates to the top-level, unit-testable function so the
|
||||
// `faviconUsable` guard (never reflect the on-brand fallback artUri
|
||||
// back as a real favicon) is covered without instantiating the handler.
|
||||
return emisoraDesdeMediaItem(mediaItem);
|
||||
}
|
||||
|
||||
// ── Android Auto browsing (thin delegation to navegacion_auto.dart's
|
||||
|
||||
Reference in New Issue
Block a user