diff --git a/android/app/src/main/res/drawable/ic_auto_eq_off.xml b/android/app/src/main/res/drawable/ic_auto_eq_off.xml
new file mode 100644
index 0000000..34416fa
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_auto_eq_off.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/ic_auto_eq_on.xml b/android/app/src/main/res/drawable/ic_auto_eq_on.xml
new file mode 100644
index 0000000..1584ea4
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_auto_eq_on.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/ic_auto_eq_preset.xml b/android/app/src/main/res/drawable/ic_auto_eq_preset.xml
new file mode 100644
index 0000000..0ca5263
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_auto_eq_preset.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/lib/servicios/servicio_audio.dart b/lib/servicios/servicio_audio.dart
index 7f53582..8dd49ee 100644
--- a/lib/servicios/servicio_audio.dart
+++ b/lib/servicios/servicio_audio.dart
@@ -154,12 +154,23 @@ String nombrePresetVisible(AppLocalizations l10n, String nombre) {
/// screen (Design "EQ custom actions", item 4) — deliberately just 2: an
/// on/off toggle plus a cycling-preset action, NOT one action per preset,
/// since Android Auto only surfaces a limited number of custom actions.
+/// Folding "off" into the preset cycle (one button walking
+/// off → preset 1 → preset 2 → ... → preset 6 → off) was considered and
+/// REJECTED: with 6 presets that can take up to 6 taps to switch the EQ
+/// off while driving, which is worse for a driver than a dedicated one-tap
+/// toggle. Do not "simplify" this back down to a single action.
/// Empty when [disponible] is false (gate on EQ availability, mirrors the
/// existing `debeReaplicarEcualizador`/`_eqDisponible` gate) — a device
/// without the native Equalizer effect gets no EQ actions at all, not
-/// broken ones. Reuses the SAME bundled `ic_stat_pluriwave` drawable the
-/// notification's own status-bar icon already uses (an equalizer-bars
-/// glyph) — zero new native assets. Pure, no handler dependency.
+/// broken ones.
+///
+/// On-device feedback follow-up: both actions used to reuse the SAME
+/// `ic_stat_pluriwave` drawable and were visually indistinguishable on a
+/// car head unit, which foregrounds the icon over the label. Each action
+/// now gets its own dedicated drawable (`ic_auto_eq_on`/`ic_auto_eq_off`/
+/// `ic_auto_eq_preset`), and the toggle's icon itself reflects [activo]
+/// (not just its label) so on/off is legible at a glance. Pure, no
+/// handler dependency.
List controlesEcualizadorPersonalizados({
required bool disponible,
required bool activo,
@@ -169,14 +180,16 @@ List controlesEcualizadorPersonalizados({
if (!disponible) return const [];
return [
MediaControl.custom(
- androidIcon: 'drawable/ic_stat_pluriwave',
+ androidIcon: activo
+ ? 'drawable/ic_auto_eq_on'
+ : 'drawable/ic_auto_eq_off',
label: activo
? l10n.eqCustomActionDisableLabel
: l10n.eqCustomActionEnableLabel,
name: accionEqToggle,
),
MediaControl.custom(
- androidIcon: 'drawable/ic_stat_pluriwave',
+ androidIcon: 'drawable/ic_auto_eq_preset',
label: l10n.eqCustomActionPresetLabel(
nombrePresetVisible(l10n, presetActual.nombre),
),
diff --git a/test/servicios/servicio_audio_eq_custom_actions_test.dart b/test/servicios/servicio_audio_eq_custom_actions_test.dart
index 2de6d45..7c25a57 100644
--- a/test/servicios/servicio_audio_eq_custom_actions_test.dart
+++ b/test/servicios/servicio_audio_eq_custom_actions_test.dart
@@ -1,3 +1,4 @@
+import 'dart:io';
import 'dart:ui' show Locale;
import 'package:audio_service/audio_service.dart';
@@ -162,20 +163,53 @@ void main() {
);
});
- test('both actions reuse the bundled notification drawable (zero new '
- 'native assets)', () {
- final controles = controlesEcualizadorPersonalizados(
- disponible: true,
- activo: true,
- presetActual: PresetEcualizador.flat,
- l10n: l10n,
- );
+ test(
+ 'toggle icon reflects EQ state: ON uses ic_auto_eq_on, OFF uses '
+ 'ic_auto_eq_off -- a car head unit foregrounds the icon over the '
+ 'label, so the icon itself must change, not just the text',
+ () {
+ final activado = controlesEcualizadorPersonalizados(
+ disponible: true,
+ activo: true,
+ presetActual: PresetEcualizador.flat,
+ l10n: l10n,
+ ).firstWhere((c) => c.customAction?.name == accionEqToggle);
+ final desactivado = controlesEcualizadorPersonalizados(
+ disponible: true,
+ activo: false,
+ presetActual: PresetEcualizador.flat,
+ l10n: l10n,
+ ).firstWhere((c) => c.customAction?.name == accionEqToggle);
- expect(
- controles.every((c) => c.androidIcon == 'drawable/ic_stat_pluriwave'),
- isTrue,
- );
- });
+ expect(activado.androidIcon, 'drawable/ic_auto_eq_on');
+ expect(desactivado.androidIcon, 'drawable/ic_auto_eq_off');
+ },
+ );
+
+ test(
+ 'the toggle and the preset-cycle action never share an androidIcon, '
+ 'in either EQ state -- this was the on-device bug: both buttons used '
+ 'the same drawable and were visually indistinguishable',
+ () {
+ for (final activo in [true, false]) {
+ final controles = controlesEcualizadorPersonalizados(
+ disponible: true,
+ activo: activo,
+ presetActual: PresetEcualizador.flat,
+ l10n: l10n,
+ );
+ final iconos = controles.map((c) => c.androidIcon).toSet();
+
+ expect(
+ iconos.length,
+ controles.length,
+ reason:
+ 'every custom action must have a distinct androidIcon '
+ '(activo=$activo)',
+ );
+ }
+ },
+ );
});
group('action name constants (item 4 — collision-free with car-tree ids)', () {
@@ -186,4 +220,37 @@ void main() {
expect(accionEqToggle, isNot(equals(accionEqPresetSiguiente)));
});
});
+
+ group(
+ 'equalizer drawable assets on disk (on-device feedback follow-up: the '
+ 'two custom actions used to share one drawable and were visually '
+ 'indistinguishable)',
+ () {
+ test(
+ 'ic_auto_eq_on, ic_auto_eq_off and ic_auto_eq_preset all exist under '
+ 'android/app/src/main/res/drawable/ -- a missing drawable is not a '
+ 'build error, it silently renders blank/default on the head unit, '
+ 'so this is the only safety net that would have caught the original '
+ 'duplication',
+ () {
+ for (final nombre in [
+ 'ic_auto_eq_on',
+ 'ic_auto_eq_off',
+ 'ic_auto_eq_preset',
+ ]) {
+ final archivo = File(
+ 'android/app/src/main/res/drawable/$nombre.xml',
+ );
+ expect(
+ archivo.existsSync(),
+ isTrue,
+ reason:
+ '$nombre.xml must exist under '
+ 'android/app/src/main/res/drawable/',
+ );
+ }
+ },
+ );
+ },
+ );
}