From 4ae93182fadf6d9cde79b5ac5511d2978f5c9813 Mon Sep 17 00:00:00 2001 From: freetlab Date: Fri, 22 May 2026 01:26:20 +0200 Subject: [PATCH] fix(alarm): add due alarm watchdog --- android/app/src/main/AndroidManifest.xml | 2 ++ .../freetimelab/pluriwave/AlarmScheduler.kt | 30 +++++++++++-------- lib/app.dart | 17 +++++++++++ lib/estado/estado_alarmas.dart | 25 ++++++++++++++++ 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 2000b90..24a4a5d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -24,6 +24,8 @@ android:launchMode="singleTop" android:taskAffinity="" android:theme="@style/LaunchTheme" + android:showWhenLocked="true" + android:turnScreenOn="true" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> diff --git a/android/app/src/main/kotlin/es/freetimelab/pluriwave/AlarmScheduler.kt b/android/app/src/main/kotlin/es/freetimelab/pluriwave/AlarmScheduler.kt index 0bc1d82..11352a4 100644 --- a/android/app/src/main/kotlin/es/freetimelab/pluriwave/AlarmScheduler.kt +++ b/android/app/src/main/kotlin/es/freetimelab/pluriwave/AlarmScheduler.kt @@ -41,20 +41,24 @@ class AlarmScheduler(private val context: Context) { ) if (preNoticeAtMillis > System.currentTimeMillis()) { - alarmManager.setExactAndAllowWhileIdle( - AlarmManager.RTC_WAKEUP, - preNoticeAtMillis, - PendingIntent.getBroadcast( - context, - requestCode(id, 3), - Intent(context, PluriWaveAlarmReceiver::class.java).apply { - action = PluriWaveAlarmReceiver.ACTION_PRE_NOTICE - putExtra(PluriWaveAlarmReceiver.EXTRA_ALARM_ID, id) - putExtra(PluriWaveAlarmReceiver.EXTRA_ALARM_TITLE, title) - }, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + try { + alarmManager.setExactAndAllowWhileIdle( + AlarmManager.RTC_WAKEUP, + preNoticeAtMillis, + PendingIntent.getBroadcast( + context, + requestCode(id, 3), + Intent(context, PluriWaveAlarmReceiver::class.java).apply { + action = PluriWaveAlarmReceiver.ACTION_PRE_NOTICE + putExtra(PluriWaveAlarmReceiver.EXTRA_ALARM_ID, id) + putExtra(PluriWaveAlarmReceiver.EXTRA_ALARM_TITLE, title) + }, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) ) - ) + } catch (_: SecurityException) { + // The main alarm is already scheduled with setAlarmClock. + } } } diff --git a/lib/app.dart b/lib/app.dart index dc81ae3..d086cd3 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -50,6 +50,7 @@ class _PaginaPrincipalState extends State<_PaginaPrincipal> { int _indice = 0; StreamSubscription? _errorSubscription; StreamSubscription? _alarmaSubscription; + StreamSubscription? _alarmaVencidaSubscription; EstadoRadio? _estadoSuscrito; bool _alarmaInicialProcesada = false; @@ -129,6 +130,12 @@ class _PaginaPrincipalState extends State<_PaginaPrincipal> { if (!mounted) return; _abrirAlarmaSonando(evento); }); + _alarmaVencidaSubscription ??= alarmas.alarmasVencidasStream.listen(( + alarma, + ) { + if (!mounted) return; + _abrirAlarmaDirecta(alarma); + }); if (!_alarmaInicialProcesada) { _alarmaInicialProcesada = true; unawaited(_procesarAlarmaInicial(alarmas)); @@ -139,6 +146,7 @@ class _PaginaPrincipalState extends State<_PaginaPrincipal> { void dispose() { _errorSubscription?.cancel(); _alarmaSubscription?.cancel(); + _alarmaVencidaSubscription?.cancel(); super.dispose(); } @@ -223,6 +231,15 @@ class _PaginaPrincipalState extends State<_PaginaPrincipal> { ); } + Future _abrirAlarmaDirecta(AlarmaMusical alarma) async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => PantallaAlarmaSonando(alarma: alarma), + fullscreenDialog: true, + ), + ); + } + void _mostrarTimerDialog(BuildContext context) { final estado = context.read(); showModalBottomSheet( diff --git a/lib/estado/estado_alarmas.dart b/lib/estado/estado_alarmas.dart index fc6161f..31e9fcb 100644 --- a/lib/estado/estado_alarmas.dart +++ b/lib/estado/estado_alarmas.dart @@ -26,6 +26,9 @@ class EstadoAlarmas extends ChangeNotifier { List _excepciones = []; DiagnosticoAlarmasAndroid? _diagnostico; Timer? _refresco; + Timer? _vigilancia; + final _alarmasVencidasController = StreamController.broadcast(); + final Set _ejecucionesEmitidas = {}; bool _cargando = false; String? _error; @@ -35,6 +38,8 @@ class EstadoAlarmas extends ChangeNotifier { DiagnosticoAlarmasAndroid? get diagnostico => _diagnostico; bool get cargando => _cargando; String? get error => _error; + Stream get alarmasVencidasStream => + _alarmasVencidasController.stream; AlarmaMusical? get proximaAlarma { final candidatas = @@ -168,11 +173,31 @@ class EstadoAlarmas extends ChangeNotifier { _refresco = Timer.periodic(const Duration(minutes: 1), (_) { refrescarProgramacion(); }); + _vigilarAlarmasVencidas(); + _vigilancia?.cancel(); + _vigilancia = Timer.periodic(const Duration(seconds: 10), (_) { + _vigilarAlarmasVencidas(); + }); + } + + void _vigilarAlarmasVencidas() { + final ahora = DateTime.now(); + for (final alarma in _alarmas) { + final proxima = alarma.proximaEjecucion; + if (!alarma.activa || proxima == null) continue; + if (proxima.isAfter(ahora)) continue; + final key = '${alarma.id}:${proxima.millisecondsSinceEpoch}'; + if (_ejecucionesEmitidas.add(key)) { + _alarmasVencidasController.add(alarma); + } + } } @override void dispose() { _refresco?.cancel(); + _vigilancia?.cancel(); + _alarmasVencidasController.close(); super.dispose(); } }