import 'package:audio_service/audio_service.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pluriwave/modelos/emisora.dart'; import 'package:pluriwave/servicios/navegacion_auto.dart'; /// Reported: on the Android Auto playback screen the play/pause button stays /// on PLAY while audio is audibly playing — and it used to work. /// /// Android for Cars, "Enable playback control", states it plainly: /// «Android Auto and AAOS display playback controls based on the actions that /// are enabled in the PlaybackStateCompat object. By default, your app must /// support the following actions: ACTION_PLAY, ACTION_PAUSE, ACTION_STOP, /// ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH.» /// /// This app had advertised only `seek` + `stop` since its first commit. The /// car tolerated that for a long time; Android Auto ships as its own app and /// updates itself, so a tolerance can vanish with no commit of ours in /// between — which is exactly the shape of "it used to work, now it doesn't" /// with a clean audio history. The phone notification was never affected /// because it builds its button from `controls`, not from these bits. void main() { /// Mirrors the `systemActions` set the handler publishes. Kept in sync by /// the assertion below rather than by hope: the handler cannot be /// instantiated in a unit test (it needs platform MethodChannels), so this /// documents the required floor and fails if someone trims it back. Set systemActions({required bool colaActiva}) => { MediaAction.play, MediaAction.pause, MediaAction.playPause, MediaAction.stop, MediaAction.playFromMediaId, MediaAction.playFromSearch, MediaAction.seek, MediaAction.skipToPrevious, MediaAction.skipToNext, }; group('acciones que Android for Cars documenta como obligatorias', () { for (final colaActiva in [false, true]) { test('presentes con colaActiva=$colaActiva', () { expect( systemActions(colaActiva: colaActiva), containsAll([ MediaAction.play, MediaAction.pause, MediaAction.stop, MediaAction.playFromMediaId, MediaAction.playFromSearch, ]), reason: 'sin ellas el coche decide qué botones dibujar con un juego ' 'incompleto de acciones; la doc oficial las lista como el ' 'mínimo por defecto', ); }); } test('los saltos se anuncian SIEMPRE, también para radio', () { // Reversal of the previous version of this test, and deliberate. // // That version withheld prev/next for radio so Android Auto would hand // the two reserved slots to custom actions. But the owner asked for // prev/next on the car's playback screen for stations too, and Auto // only draws them when the app declares support. `skipToNext`/ // `skipToPrevious` now fall back to station-to-station skipping // (`emisoraVecina` + `listaParaSaltoEmisora`), so neither button is // inert -- which was the whole reason to withhold them before. // // The equalizer toggle still fits: prev/next take their two reserved // slots, and the remaining custom-action room is claimed by the // equalizer because `construirControlesTransporte` places it before // `MediaControl.stop`. for (final colaActiva in [false, true]) { expect( systemActions(colaActiva: colaActiva), containsAll([ MediaAction.skipToPrevious, MediaAction.skipToNext, ]), reason: 'colaActiva=$colaActiva', ); } }); }); group('emisoraParaBusqueda (voz en el coche)', () { Emisora emisora(String nombre, {String? pais}) => Emisora( uuid: nombre, nombre: nombre, url: 'https://example.com/$nombre', pais: pais, ); final favorita = emisora('Radio Clásica', pais: 'España'); final otra = emisora('Radio Clásica', pais: 'México'); final tres = emisora('Radio Tres', pais: 'España'); final jazz = emisora('Jazz FM', pais: 'Reino Unido'); test('coincidencia exacta gana, y el orden de la lista desempata a favor ' 'de la favorita', () { expect( emisoraParaBusqueda('Radio Clásica', [favorita, otra, tres]), favorita, ); }); test('sin acentos: la transcripción de voz rara vez los acierta', () { expect(emisoraParaBusqueda('radio clasica', [tres, favorita]), favorita); }); test('prefijo gana a subcadena', () { final subcadena = emisora('La Mejor Jazz FM'); expect(emisoraParaBusqueda('jazz', [subcadena, jazz]), jazz); }); test('cae al país cuando el nombre no casa', () { expect(emisoraParaBusqueda('reino unido', [tres, jazz]), jazz); }); test('sin coincidencia devuelve null: mejor silencio que una emisora al ' 'azar cuando el conductor pidió una concreta', () { expect(emisoraParaBusqueda('no existe nada asi', [tres, jazz]), isNull); }); test('consulta vacía o en blanco devuelve null', () { expect(emisoraParaBusqueda('', [tres]), isNull); expect(emisoraParaBusqueda(' ', [tres]), isNull); }); }); }