Two Android Auto regressions reported from the car. 1. The on/off equalizer action disappeared from the playback screen. That was self-inflicted: commitcacd3ecremoved it on the theory that a custom action in `controls` aborts `AudioService.setState` and kills the media notification. Reading the plugin source refutes it. setState (AudioService.java:513-520) SPLITS the list -- a control carrying a customAction goes to `customActions` (PlaybackStateCompat, i.e. the car), everything else becomes a NotificationCompat.Action in `nativeActions` (the phone notification). The two never mix. And the throw the theory depended on cannot happen here: ic_auto_eq_on/ic_auto_eq_off both exist under res/drawable, and the labels are non-empty in all 13 locales. The notification outage was already fixed byabc6b47(transient idle on a source change, which setState turns into a full stop() at :557). The action is back, with both state-aware icons. The real invariant -- a custom action's icon must resolve and its label must be non-empty -- is now a test that reads res/drawable and fails on a missing file, instead of a comment claiming custom actions are forbidden outright. 2. The Ecualizador folder never listed the user's saved presets. itemsEcualizadorAuto iterated PresetEcualizador.presets, so only the six factory presets appeared -- the user's own were unreachable from the car, the surface where a preset picker matters most. They now arrive through a registered read function (same seam as stations and local music, re-read per browse so a preset saved on the phone shows up without an app restart). presetsEcualizadorAuto is the single source of truth for the ordered universe, used to BUILD the items and to RESOLVE a tap, so the folder cannot show an item that resolution then refuses -- which is what the factory-only default in seleccionarPresetEqPorMediaId would have caused. A custom preset whose name collides with a factory one is dropped: the media id is the raw name, so it could only ever resolve to the factory entry, and an item that applies a preset other than the one it names is worse than an absent one. Tests: 1108 -> 1120.
255 lines
8.6 KiB
Dart
255 lines
8.6 KiB
Dart
import 'dart:ui' show Locale;
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:pluriwave/l10n/gen/app_localizations.dart';
|
|
import 'package:pluriwave/modelos/preset_ecualizador.dart';
|
|
import 'package:pluriwave/servicios/navegacion_auto.dart';
|
|
import 'package:pluriwave/servicios/servicio_audio.dart';
|
|
|
|
/// Decision `auto/ecualizador-diseno` -- the "Ecualizador" browsable
|
|
/// folder's item-building (`itemsEcualizadorAuto`), the pure,
|
|
/// `AppLocalizations`-dependent half of the folder feature. Lives in a
|
|
/// dedicated file, separate from `servicio_audio_eq_custom_actions_test.dart`
|
|
/// (which covers the now-playing screen's on/off toggle) because this is a
|
|
/// different car UI surface: a browsable folder, not a custom action.
|
|
void main() {
|
|
final l10n = lookupAppLocalizations(const Locale('es'));
|
|
|
|
group('itemsEcualizadorAuto (decision `auto/ecualizador-diseno`)', () {
|
|
test('devuelve exactamente 7 items: Desactivar primero, luego los 6 '
|
|
'presets de fábrica, todos playable', () {
|
|
final items = itemsEcualizadorAuto(
|
|
activo: true,
|
|
presetActual: PresetEcualizador.flat,
|
|
l10n: l10n,
|
|
);
|
|
|
|
expect(items, hasLength(7));
|
|
expect(items.first.id, ConstructorArbolAuto.idDesactivarEq);
|
|
for (final item in items) {
|
|
expect(item.playable, isTrue);
|
|
}
|
|
});
|
|
|
|
test('los 6 presets aparecen en el mismo orden que '
|
|
'PresetEcualizador.presets, cada uno con su id eq_preset:<nombre>', () {
|
|
final items = itemsEcualizadorAuto(
|
|
activo: true,
|
|
presetActual: PresetEcualizador.flat,
|
|
l10n: l10n,
|
|
);
|
|
final builder = ConstructorArbolAuto();
|
|
|
|
final idsPresets = items.skip(1).map((i) => i.id).toList();
|
|
final idsEsperados =
|
|
PresetEcualizador.presets
|
|
.map((p) => builder.idPresetEq(p.nombre))
|
|
.toList();
|
|
|
|
expect(idsPresets, idsEsperados);
|
|
});
|
|
|
|
test('los nombres de preset están localizados vía nombrePresetVisible, '
|
|
'no crudos', () {
|
|
final items = itemsEcualizadorAuto(
|
|
activo: true,
|
|
presetActual: PresetEcualizador.flat,
|
|
l10n: l10n,
|
|
);
|
|
final builder = ConstructorArbolAuto();
|
|
|
|
final rock = items.firstWhere(
|
|
(i) => i.id == builder.idPresetEq(PresetEcualizador.rock.nombre),
|
|
);
|
|
|
|
expect(rock.title, contains(nombrePresetVisible(l10n, 'Rock')));
|
|
});
|
|
|
|
test('con el ecualizador ACTIVO, el preset actual queda marcado y '
|
|
'Desactivar NO', () {
|
|
final items = itemsEcualizadorAuto(
|
|
activo: true,
|
|
presetActual: PresetEcualizador.jazz,
|
|
l10n: l10n,
|
|
);
|
|
final builder = ConstructorArbolAuto();
|
|
|
|
final desactivar = items.firstWhere(
|
|
(i) => i.id == ConstructorArbolAuto.idDesactivarEq,
|
|
);
|
|
final jazz = items.firstWhere(
|
|
(i) => i.id == builder.idPresetEq(PresetEcualizador.jazz.nombre),
|
|
);
|
|
final marcados = items.where((i) => i.title.contains('✓')).toList();
|
|
|
|
expect(desactivar.title, isNot(contains('✓')));
|
|
expect(jazz.title, contains('✓'));
|
|
expect(marcados, hasLength(1));
|
|
expect(marcados.single.id, jazz.id);
|
|
});
|
|
|
|
test('con el ecualizador DESACTIVADO, Desactivar queda marcado y NINGÚN '
|
|
'preset lo está', () {
|
|
final items = itemsEcualizadorAuto(
|
|
activo: false,
|
|
presetActual: PresetEcualizador.rock,
|
|
l10n: l10n,
|
|
);
|
|
|
|
final desactivar = items.firstWhere(
|
|
(i) => i.id == ConstructorArbolAuto.idDesactivarEq,
|
|
);
|
|
final marcados = items.where((i) => i.title.contains('✓')).toList();
|
|
|
|
expect(desactivar.title, contains('✓'));
|
|
expect(marcados, hasLength(1));
|
|
expect(marcados.single.id, ConstructorArbolAuto.idDesactivarEq);
|
|
});
|
|
|
|
test('el label de Desactivar usa l10n.autoEqDisableOption', () {
|
|
final items = itemsEcualizadorAuto(
|
|
activo: false,
|
|
presetActual: PresetEcualizador.flat,
|
|
l10n: l10n,
|
|
);
|
|
|
|
final desactivar = items.firstWhere(
|
|
(i) => i.id == ConstructorArbolAuto.idDesactivarEq,
|
|
);
|
|
|
|
expect(desactivar.title, contains(l10n.autoEqDisableOption));
|
|
});
|
|
});
|
|
|
|
/// On-device feedback: the folder only ever listed the six FACTORY
|
|
/// presets, so a user's own saved presets were unreachable from the car —
|
|
/// the surface where a preset picker matters most, since you cannot pull
|
|
/// out the phone and drag five band sliders while driving.
|
|
group('presets personalizados del usuario en la carpeta', () {
|
|
final mio = PresetEcualizador(
|
|
nombre: 'Mi coche',
|
|
bandas: [4.0, 2.0, 0.0, 1.0, 3.0],
|
|
);
|
|
final otro = PresetEcualizador(
|
|
nombre: 'Podcast noche',
|
|
bandas: [-3.0, 0.0, 3.0, 2.0, -1.0],
|
|
);
|
|
|
|
test('se listan tras los 6 de fábrica, en orden de guardado', () {
|
|
final items = itemsEcualizadorAuto(
|
|
activo: true,
|
|
presetActual: PresetEcualizador.flat,
|
|
l10n: l10n,
|
|
presetsPersonalizados: [mio, otro],
|
|
);
|
|
|
|
expect(items, hasLength(9));
|
|
expect(items[7].title, contains('Mi coche'));
|
|
expect(items[8].title, contains('Podcast noche'));
|
|
});
|
|
|
|
test('su id sigue el mismo formato eq_preset:<nombre> y resuelve al '
|
|
'preset correcto con la lista combinada', () {
|
|
final constructor = ConstructorArbolAuto();
|
|
final universo = presetsEcualizadorAuto(personalizados: [mio]);
|
|
final items = itemsEcualizadorAuto(
|
|
activo: true,
|
|
presetActual: PresetEcualizador.flat,
|
|
l10n: l10n,
|
|
presetsPersonalizados: [mio],
|
|
);
|
|
|
|
final item = items.last;
|
|
expect(item.id, constructor.idPresetEq('Mi coche'));
|
|
expect(item.playable, isTrue);
|
|
expect(constructor.resolverPresetEq(item.id, presets: universo), mio);
|
|
});
|
|
|
|
test('sin la lista combinada NO resuelve: es exactamente el fallo que se '
|
|
'reportó, un item visible que al pulsarlo no hacía nada', () {
|
|
final constructor = ConstructorArbolAuto();
|
|
|
|
// Default `presets` == the factory six only. This is what the tap
|
|
// dispatch used to do, and why a listed custom preset was a no-op.
|
|
expect(
|
|
constructor.resolverPresetEq(constructor.idPresetEq('Mi coche')),
|
|
isNull,
|
|
);
|
|
});
|
|
|
|
test('pulsar un preset personalizado lo aplica de verdad', () async {
|
|
final aplicados = <PresetEcualizador>[];
|
|
var encendido = false;
|
|
|
|
await seleccionarPresetEqPorMediaId(
|
|
ConstructorArbolAuto().idPresetEq('Mi coche'),
|
|
activo: false,
|
|
aplicarPreset: (p) async => aplicados.add(p),
|
|
activarEcualizador: (v) async => encendido = v,
|
|
presets: presetsEcualizadorAuto(personalizados: [mio]),
|
|
);
|
|
|
|
expect(aplicados, [mio]);
|
|
// Tapping any preset while the EQ is off must also switch it on,
|
|
// exactly as it already does for a factory preset.
|
|
expect(encendido, isTrue);
|
|
});
|
|
|
|
test('el preset personalizado activo se marca con ✓, y solo él', () {
|
|
final items = itemsEcualizadorAuto(
|
|
activo: true,
|
|
presetActual: mio,
|
|
l10n: l10n,
|
|
presetsPersonalizados: [mio, otro],
|
|
);
|
|
|
|
final marcados = items.where((i) => i.title.contains('✓')).toList();
|
|
expect(marcados, hasLength(1));
|
|
expect(marcados.single.id, ConstructorArbolAuto().idPresetEq('Mi coche'));
|
|
});
|
|
|
|
test('un personalizado que repite el nombre de uno de fábrica se DESCARTA '
|
|
'— su id solo podría resolver al de fábrica, así que mostrarlo '
|
|
'aplicaría un preset distinto del que anuncia', () {
|
|
final impostor = PresetEcualizador(
|
|
nombre: 'Rock',
|
|
bandas: [9.0, 9.0, 9.0, 9.0, 9.0],
|
|
);
|
|
|
|
final universo = presetsEcualizadorAuto(personalizados: [impostor, mio]);
|
|
|
|
expect(universo.where((p) => p.nombre == 'Rock'), hasLength(1));
|
|
expect(
|
|
universo.firstWhere((p) => p.nombre == 'Rock'),
|
|
PresetEcualizador.rock,
|
|
);
|
|
expect(universo, contains(mio));
|
|
expect(
|
|
itemsEcualizadorAuto(
|
|
activo: true,
|
|
presetActual: PresetEcualizador.flat,
|
|
l10n: l10n,
|
|
presetsPersonalizados: [impostor, mio],
|
|
),
|
|
hasLength(8),
|
|
);
|
|
});
|
|
|
|
test('sin presets guardados la carpeta queda exactamente como antes', () {
|
|
expect(
|
|
itemsEcualizadorAuto(
|
|
activo: true,
|
|
presetActual: PresetEcualizador.flat,
|
|
l10n: l10n,
|
|
).map((i) => i.id),
|
|
itemsEcualizadorAuto(
|
|
activo: true,
|
|
presetActual: PresetEcualizador.flat,
|
|
l10n: l10n,
|
|
presetsPersonalizados: const [],
|
|
).map((i) => i.id),
|
|
);
|
|
});
|
|
});
|
|
}
|