feat(audio): audio session integration and runtime robustness
- Integrate audio_session (new servicio_audio_session.dart): incoming calls pause the radio and resume on end, headphone unplug pauses without auto-resume, permanent focus loss never auto-resumes, duck lowers volume - Add play-intent flag to ServicioAudio so interruption handling and future reconnect logic can distinguish user pause from system-driven stops - Eliminate read-modify-write race in ServicioAlarmas with an in-memory cache and single-writer queue across all mutations; recalcularTodas persists only when state actually changed - Convert ServicioAlarmasAndroid static StreamController/handler to injectable instance fields, restoring test isolation - Inject a single cached SharedPreferences from main.dart across services and state (removes 23 inline getInstance() calls) - Move configurarLocalizaciones out of MiniReproductor.build() (was running on every rebuild during playback) - Bound the alarm fire-dedup set (cap 200 entries, 24h pruning) - 12 new tests (89 total green), flutter analyze clean
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pluriwave/estado/estado_radio.dart';
|
||||
import 'package:pluriwave/l10n/gen/app_localizations.dart';
|
||||
import 'package:pluriwave/widgets/mini_reproductor.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../helpers/fakes.dart';
|
||||
import '../helpers/fakes_alarmas.dart';
|
||||
|
||||
class _EstadoRadioContador extends EstadoRadio {
|
||||
_EstadoRadioContador()
|
||||
: super(
|
||||
audio: FakeServicioAudio(),
|
||||
favoritos: FakeServicioFavoritos(),
|
||||
radio: FakeServicioRadio(),
|
||||
servicioEcualizador: FakeServicioEcualizador(),
|
||||
servicioGrabacion: FakeServicioGrabacionRadioInactiva(),
|
||||
iniciarAutomaticamente: false,
|
||||
);
|
||||
|
||||
int llamadasConfigurar = 0;
|
||||
|
||||
@override
|
||||
void configurarLocalizaciones(AppLocalizations l10n) {
|
||||
llamadasConfigurar++;
|
||||
super.configurarLocalizaciones(l10n);
|
||||
}
|
||||
}
|
||||
|
||||
/// S3-R3: `configurarLocalizaciones` must run once per locale change, not on
|
||||
/// every rebuild triggered by playback notifications.
|
||||
void main() {
|
||||
testWidgets(
|
||||
'configurarLocalizaciones corre una vez por locale, no por rebuild (S3-R3-A)',
|
||||
(tester) async {
|
||||
final estado = _EstadoRadioContador();
|
||||
addTearDown(estado.dispose);
|
||||
var locale = const Locale('es');
|
||||
late StateSetter cambiarLocale;
|
||||
|
||||
await tester.pumpWidget(
|
||||
ChangeNotifierProvider<EstadoRadio>.value(
|
||||
value: estado,
|
||||
child: StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
cambiarLocale = setState;
|
||||
return MaterialApp(
|
||||
locale: locale,
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
home: const Scaffold(body: MiniReproductor()),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Ten rebuilds driven by playback state notifications.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
estado.notifyListeners();
|
||||
await tester.pump();
|
||||
}
|
||||
|
||||
expect(
|
||||
estado.llamadasConfigurar,
|
||||
1,
|
||||
reason: 'diez rebuilds con el mismo locale => una sola configuracion',
|
||||
);
|
||||
|
||||
cambiarLocale(() => locale = const Locale('en'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
estado.llamadasConfigurar,
|
||||
2,
|
||||
reason: 'el cambio de locale debe reconfigurar exactamente una vez',
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user