feat(alarm): make the ring immune to device media volume
Build & Deploy PluriWave / Análisis de código (push) Successful in 41s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m39s

The alarm's steady-state audio runs on the Flutter media-stream
player after the native handoff, so device volume 0 silenced it
entirely. The ring now forces STREAM_MUSIC to an audible reference:
Dart requests the override before pre-starting alarm audio (fallback
WAV included), Kotlin captures the current volume once and restores
it idempotently on every exit path (dismiss, snooze, dispose), with
a native best-effort backstop in service teardown.

The backstop is handoff-aware via PluriWaveAlarmService.flutterOwnsRing:
confirmFlutterAudio marks the handoff before triggering the native
stop, so the backstop cannot restore the volume mid-ring right as the
Flutter player takes over (that would re-silence the alarm at volume
0). The flag resets at every ring start; Flutter process death after
handoff remains a documented best-effort gap.

The alarm's perceived loudness keeps ramping 5% to the configured
volume through the player as before; normal radio playback and call
ducking never touch the override.

Work unit 2/3 of alarm-volume-ramp-restore (ring volume override).
This commit is contained in:
2026-07-11 09:15:37 +02:00
parent 251d3fd3cd
commit acd903d9a8
9 changed files with 440 additions and 21 deletions
+18
View File
@@ -17,6 +17,14 @@ class FakePuertoAlarmasAndroid implements PuertoAlarmasAndroid {
bool ignoraOptimizacionBateria = true;
int solicitudesExencionBateria = 0;
/// Records each [forzarVolumenMediaParaAlarma] call (Slice 2: ring-scoped
/// device-volume override).
final volumenForzado = <double>[];
/// Counts [restaurarVolumenMedia] calls (Slice 2). A plain counter, not a
/// list: widget tests only need to assert how many times restore ran.
int volumenRestaurado = 0;
/// Test-only failure switch (Design D7): when true, [programar] throws
/// instead of scheduling, enabling failure-path coverage that the fake
/// could not otherwise produce.
@@ -59,6 +67,16 @@ class FakePuertoAlarmasAndroid implements PuertoAlarmasAndroid {
detenidas.add(alarmaId);
}
@override
Future<void> forzarVolumenMediaParaAlarma(double fraccion) async {
volumenForzado.add(fraccion);
}
@override
Future<void> restaurarVolumenMedia() async {
volumenRestaurado++;
}
@override
Future<DiagnosticoAlarmasAndroid> diagnostico() async =>
DiagnosticoAlarmasAndroid(