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:
2026-06-11 16:25:09 +02:00
parent f3e9487215
commit 079e19f0ee
21 changed files with 1059 additions and 151 deletions
+11 -3
View File
@@ -27,12 +27,20 @@ class ContenidoAyudaPluri {
}
class ServicioContenidoApp {
ServicioContenidoApp({SharedPreferences? prefs}) : _prefs = prefs;
static const _keyOnboardingVisto = 'pluri_onboarding_visto_v1';
static const _keyVersionVista = 'pluri_ultima_version_novedades_v1';
static const _versiones = ['0.1.47'];
final SharedPreferences? _prefs;
/// Injected startup instance (S3-R4); getInstance() is only a fallback.
Future<SharedPreferences> _resolverPrefs() async =>
_prefs ?? SharedPreferences.getInstance();
Future<bool> debeMostrarInicio() async {
final prefs = await SharedPreferences.getInstance();
final prefs = await _resolverPrefs();
final info = await PackageInfo.fromPlatform();
final versionActual = info.version;
return !(prefs.getBool(_keyOnboardingVisto) ?? false) ||
@@ -40,7 +48,7 @@ class ServicioContenidoApp {
}
Future<void> marcarVisto() async {
final prefs = await SharedPreferences.getInstance();
final prefs = await _resolverPrefs();
final info = await PackageInfo.fromPlatform();
await prefs.setBool(_keyOnboardingVisto, true);
await prefs.setString(_keyVersionVista, info.version);
@@ -51,7 +59,7 @@ class ServicioContenidoApp {
bool soloPendientes = false,
}) async {
final info = await PackageInfo.fromPlatform();
final prefs = await SharedPreferences.getInstance();
final prefs = await _resolverPrefs();
final ultimaVista = prefs.getString(_keyVersionVista);
final idioma = _idiomaSoportado(codigoIdioma);
final mostrarOnboarding =