fix(alarms): harden native playback and pre-notice actions

This commit is contained in:
Javier Bautista Fernández
2026-05-28 12:03:58 +02:00
parent 41bbd0ea17
commit 659e6da189
16 changed files with 1370 additions and 180 deletions
@@ -30,6 +30,7 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
Timer? _fallbackTimer;
bool _fallbackActivo = false;
bool _radioIntentada = false;
bool _audioFlutterConfirmado = false;
@override
void initState() {
@@ -57,6 +58,7 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
_estadoSub = radio.estadoStream.listen((estado) {
if (estado == EstadoReproduccion.reproduciendo && mounted) {
_fallbackTimer?.cancel();
_confirmarAudioFlutterListo();
}
if (estado == EstadoReproduccion.error && mounted) {
_iniciarFallback();
@@ -76,9 +78,18 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
_fallbackActivo = true;
await _fallbackPlayer.setAsset(_assetFallback(widget.alarma.sonidoInterno));
await _fallbackPlayer.play();
await _confirmarAudioFlutterListo();
if (mounted) setState(() {});
}
Future<void> _confirmarAudioFlutterListo() async {
if (_audioFlutterConfirmado) return;
_audioFlutterConfirmado = true;
await context.read<EstadoAlarmas>().android.confirmarAudioFlutter(
widget.alarma.id,
);
}
Future<void> _detener() async {
final radio = context.read<EstadoRadio>();
final alarmas = context.read<EstadoAlarmas>();
+19 -12
View File
@@ -82,8 +82,9 @@ class _PanelProximaAlarma extends StatelessWidget {
final proxima = estado.proximaAlarma;
final activasSinProxima =
estado.alarmas
.where((a) => a.activa && a.proximaEjecucion == null)
.where((a) => a.activa && a.proximaProgramable == null)
.length;
final proximaProgramable = proxima?.proximaProgramable;
return PluriGlassSurface(
glowColor: const Color(0xFFFFB86B).withValues(alpha: 0.28),
child: Row(
@@ -110,7 +111,7 @@ class _PanelProximaAlarma extends StatelessWidget {
? activasSinProxima > 0
? 'Hay $activasSinProxima alarma(s) activas, pero ahora mismo no tienen una fecha futura válida. Revisá fecha, días y vacaciones.'
: 'Creá una alarma y PluriWave calculará la siguiente ejecución automáticamente.'
: '${proxima.nombre} · ${_fechaHora(proxima.proximaEjecucion!)}',
: '${proxima.nombre} · ${_fechaHora(proximaProgramable!)}',
),
],
),
@@ -193,11 +194,11 @@ class _TarjetaAlarma extends StatelessWidget {
],
),
const SizedBox(height: 12),
if (alarma.proximaEjecucion != null)
if (alarma.proximaProgramable != null)
_NoticeLine(
icon: Icons.event_available_rounded,
text:
'Siguiente ejecución: ${_fechaHora(alarma.proximaEjecucion!)}',
'Siguiente ejecución: ${_fechaHora(alarma.proximaProgramable!)}',
)
else
const _NoticeLine(
@@ -231,7 +232,7 @@ class _TarjetaAlarma extends StatelessWidget {
icon: const Icon(Icons.skip_next_rounded),
label: const Text('Omitir siguiente'),
onPressed:
alarma.proximaEjecucion == null
alarma.proximaProgramable == null
? null
: () async {
await estado.saltarProxima(alarma.id);
@@ -248,9 +249,9 @@ class _TarjetaAlarma extends StatelessWidget {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
actualizada?.proximaEjecucion == null
actualizada?.proximaProgramable == null
? 'Alarma omitida. No queda próxima ejecución.'
: 'Alarma omitida. Volverá el ${_fechaHora(actualizada!.proximaEjecucion!)}.',
: 'Alarma omitida. Volverá el ${_fechaHora(actualizada!.proximaProgramable!)}.',
),
),
);
@@ -281,13 +282,13 @@ class _TarjetaAlarma extends StatelessWidget {
}
}
if (actual != null) {
if (alarma.proximaEjecucion == null) {
if (alarma.proximaProgramable == null) {
return 'Está pausada por vacaciones (${actual.nombre}) y sin próxima ejecución.';
}
return 'Está pausada por vacaciones (${actual.nombre}) y vuelve el ${_fechaHora(alarma.proximaEjecucion!)}.';
return 'Está pausada por vacaciones (${actual.nombre}) y vuelve el ${_fechaHora(alarma.proximaProgramable!)}.';
}
if (alarma.proximaEjecucion != null) {
return 'Con vacaciones activas, volverá a sonar el ${_fechaHora(alarma.proximaEjecucion!)}.';
if (alarma.proximaProgramable != null) {
return 'Con vacaciones activas, volverá a sonar el ${_fechaHora(alarma.proximaProgramable!)}.';
}
return null;
}
@@ -690,12 +691,18 @@ class _AccesoDiagnostico extends StatelessWidget {
label: Text(
diag == null
? 'Revisar fiabilidad Android'
: 'Fiabilidad: exactas ${diag.puedeProgramarExactas ? 'OK' : 'pendiente'} · notificaciones ${diag.notificacionesPermitidas ? 'OK' : 'pendiente'}',
: 'Fiabilidad: exactas ${diag.puedeProgramarExactas ? 'OK' : 'pendiente'} ? notificaciones ${diag.notificacionesPermitidas ? 'OK' : 'pendiente'} ? pantalla ${diag.puedeUsarPantallaCompleta ? 'OK' : 'pendiente'}',
),
onPressed: () async {
if (diag != null && !diag.puedeProgramarExactas) {
await estado.android.solicitarPermisoAlarmasExactas();
}
if (diag != null && !diag.notificacionesPermitidas) {
await estado.android.solicitarPermisoNotificaciones();
}
if (diag != null && !diag.puedeUsarPantallaCompleta) {
await estado.android.solicitarPermisoPantallaCompleta();
}
await estado.cargarDiagnostico();
},
);