fix(alarm): add due alarm watchdog
Build & Deploy Pluriwave / Análisis de código (push) Successful in 14s
Build & Deploy Pluriwave / Build APK + AAB release (push) Successful in 4m14s

This commit is contained in:
2026-05-22 01:26:20 +02:00
parent d8823a328d
commit 4ae93182fa
4 changed files with 61 additions and 13 deletions
+2
View File
@@ -24,6 +24,8 @@
android:launchMode="singleTop" android:launchMode="singleTop"
android:taskAffinity="" android:taskAffinity=""
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:showWhenLocked="true"
android:turnScreenOn="true"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
@@ -41,6 +41,7 @@ class AlarmScheduler(private val context: Context) {
) )
if (preNoticeAtMillis > System.currentTimeMillis()) { if (preNoticeAtMillis > System.currentTimeMillis()) {
try {
alarmManager.setExactAndAllowWhileIdle( alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP, AlarmManager.RTC_WAKEUP,
preNoticeAtMillis, preNoticeAtMillis,
@@ -55,6 +56,9 @@ class AlarmScheduler(private val context: Context) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
) )
) )
} catch (_: SecurityException) {
// The main alarm is already scheduled with setAlarmClock.
}
} }
} }
+17
View File
@@ -50,6 +50,7 @@ class _PaginaPrincipalState extends State<_PaginaPrincipal> {
int _indice = 0; int _indice = 0;
StreamSubscription<String>? _errorSubscription; StreamSubscription<String>? _errorSubscription;
StreamSubscription<EventoAlarmaAndroid>? _alarmaSubscription; StreamSubscription<EventoAlarmaAndroid>? _alarmaSubscription;
StreamSubscription<AlarmaMusical>? _alarmaVencidaSubscription;
EstadoRadio? _estadoSuscrito; EstadoRadio? _estadoSuscrito;
bool _alarmaInicialProcesada = false; bool _alarmaInicialProcesada = false;
@@ -129,6 +130,12 @@ class _PaginaPrincipalState extends State<_PaginaPrincipal> {
if (!mounted) return; if (!mounted) return;
_abrirAlarmaSonando(evento); _abrirAlarmaSonando(evento);
}); });
_alarmaVencidaSubscription ??= alarmas.alarmasVencidasStream.listen((
alarma,
) {
if (!mounted) return;
_abrirAlarmaDirecta(alarma);
});
if (!_alarmaInicialProcesada) { if (!_alarmaInicialProcesada) {
_alarmaInicialProcesada = true; _alarmaInicialProcesada = true;
unawaited(_procesarAlarmaInicial(alarmas)); unawaited(_procesarAlarmaInicial(alarmas));
@@ -139,6 +146,7 @@ class _PaginaPrincipalState extends State<_PaginaPrincipal> {
void dispose() { void dispose() {
_errorSubscription?.cancel(); _errorSubscription?.cancel();
_alarmaSubscription?.cancel(); _alarmaSubscription?.cancel();
_alarmaVencidaSubscription?.cancel();
super.dispose(); super.dispose();
} }
@@ -223,6 +231,15 @@ class _PaginaPrincipalState extends State<_PaginaPrincipal> {
); );
} }
Future<void> _abrirAlarmaDirecta(AlarmaMusical alarma) async {
await Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => PantallaAlarmaSonando(alarma: alarma),
fullscreenDialog: true,
),
);
}
void _mostrarTimerDialog(BuildContext context) { void _mostrarTimerDialog(BuildContext context) {
final estado = context.read<EstadoRadio>(); final estado = context.read<EstadoRadio>();
showModalBottomSheet( showModalBottomSheet(
+25
View File
@@ -26,6 +26,9 @@ class EstadoAlarmas extends ChangeNotifier {
List<ExcepcionAlarma> _excepciones = []; List<ExcepcionAlarma> _excepciones = [];
DiagnosticoAlarmasAndroid? _diagnostico; DiagnosticoAlarmasAndroid? _diagnostico;
Timer? _refresco; Timer? _refresco;
Timer? _vigilancia;
final _alarmasVencidasController = StreamController<AlarmaMusical>.broadcast();
final Set<String> _ejecucionesEmitidas = {};
bool _cargando = false; bool _cargando = false;
String? _error; String? _error;
@@ -35,6 +38,8 @@ class EstadoAlarmas extends ChangeNotifier {
DiagnosticoAlarmasAndroid? get diagnostico => _diagnostico; DiagnosticoAlarmasAndroid? get diagnostico => _diagnostico;
bool get cargando => _cargando; bool get cargando => _cargando;
String? get error => _error; String? get error => _error;
Stream<AlarmaMusical> get alarmasVencidasStream =>
_alarmasVencidasController.stream;
AlarmaMusical? get proximaAlarma { AlarmaMusical? get proximaAlarma {
final candidatas = final candidatas =
@@ -168,11 +173,31 @@ class EstadoAlarmas extends ChangeNotifier {
_refresco = Timer.periodic(const Duration(minutes: 1), (_) { _refresco = Timer.periodic(const Duration(minutes: 1), (_) {
refrescarProgramacion(); 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 @override
void dispose() { void dispose() {
_refresco?.cancel(); _refresco?.cancel();
_vigilancia?.cancel();
_alarmasVencidasController.close();
super.dispose(); super.dispose();
} }
} }