feat(alarm): make the ring immune to device media volume
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:
@@ -40,6 +40,7 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
|
||||
bool _fallbackActivo = false;
|
||||
bool _radioIntentada = false;
|
||||
bool _audioFlutterConfirmado = false;
|
||||
bool _volumenMediaRestaurado = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -138,6 +139,23 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Restores the ring-scoped `STREAM_MUSIC` override at most once per
|
||||
/// screen instance (Requirement: Ring-scoped device-volume override,
|
||||
/// Scenario "Restore is idempotent across double-exit paths"). Both
|
||||
/// `_silenciarAudio` (dismiss/snooze) and `dispose` call this; the guard
|
||||
/// here plus the idempotent Kotlin-side restore together keep any
|
||||
/// exit-path ordering safe. Failures never propagate — a broken restore
|
||||
/// must not block dismiss/snooze.
|
||||
Future<void> _restaurarVolumenMediaUnaVez() async {
|
||||
if (_volumenMediaRestaurado) return;
|
||||
_volumenMediaRestaurado = true;
|
||||
try {
|
||||
await context.read<EstadoAlarmas>().android.restaurarVolumenMedia();
|
||||
} catch (e) {
|
||||
debugPrint('[PluriWave][alarmas] restaurar volumen media fallo: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared local-audio teardown for stop and snooze (Design 2.3): the Dart
|
||||
/// fallback player and fade timer MUST die before the alarm is re-programmed
|
||||
/// natively, otherwise the local fallback keeps looping after snooze.
|
||||
@@ -210,6 +228,7 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
|
||||
} catch (e) {
|
||||
debugPrint('[PluriWave][alarmas] pausar radio fallo: $e');
|
||||
}
|
||||
await _restaurarVolumenMediaUnaVez();
|
||||
}
|
||||
|
||||
/// Dismisses the alarm screen safely in both live-app and dead-app states.
|
||||
@@ -240,6 +259,7 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
|
||||
_fadeInTimer?.cancel();
|
||||
_estadoSub?.cancel();
|
||||
_fallbackPlayer.dispose();
|
||||
unawaited(_restaurarVolumenMediaUnaVez());
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user