fix(alarmas): wire scheduling failures into per-alarm exceptions
guardarAlarma/posponerAlarma/posponerProximaDesdePreaviso now record a scheduling failure via ServicioAlarmas.registrarFalloProgramacion on catch and clear it on a successful (re)schedule, in addition to the existing transient EstadoAlarmas.error string. This makes the failure visible per-alarm via ultimaExcepcionPara instead of only a generic app-wide message. Also fixes _sincronizarTodas: a single alarm's android.programar throw used to abort the whole loop, silently skipping every sibling alarm scheduled AFTER it on that pass (including on every app launch, via inicializar). Each alarm's outcome is now independent.
This commit is contained in:
@@ -117,8 +117,10 @@ class EstadoAlarmas extends ChangeNotifier {
|
||||
'[PluriWave][alarmas] guardada id=${guardada.id} proxima=${guardada.proximaEjecucion?.toIso8601String()}',
|
||||
);
|
||||
await android.programar(guardada);
|
||||
await _limpiarFalloProgramacion(guardada.id);
|
||||
} catch (e) {
|
||||
_error = 'Alarma guardada, pero Android no pudo programarla todavía: $e';
|
||||
await _registrarFalloProgramacion(alarma.id);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -198,6 +200,45 @@ class EstadoAlarmas extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
/// Records a main-alarm scheduling failure per-alarm (fix/alarmas-fallos-
|
||||
/// silenciosos): before this, a failed `android.programar` call only set
|
||||
/// the transient, alarm-agnostic [_error] string — the alarms list had no
|
||||
/// way to mark the SPECIFIC card affected, so a failed alarm rendered
|
||||
/// exactly like a working one. Never rethrows: a failure recording its own
|
||||
/// failure must not mask the ORIGINAL scheduling error already captured in
|
||||
/// [_error].
|
||||
Future<void> _registrarFalloProgramacion(
|
||||
String alarmaId, {
|
||||
String tipo = ExcepcionAlarma.tipoFalloProgramacion,
|
||||
}) async {
|
||||
try {
|
||||
final alarma = _buscarAlarma(alarmaId);
|
||||
final ejecucion = alarma?.proximaProgramable ?? servicio.ahora();
|
||||
final config = await servicio.registrarFalloProgramacion(
|
||||
alarmaId,
|
||||
ejecucion,
|
||||
tipo,
|
||||
);
|
||||
_aplicar(config);
|
||||
} catch (e) {
|
||||
debugPrint(
|
||||
'[PluriWave][alarmas] registrar fallo programacion ERROR $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears a previously recorded scheduling failure once a later attempt
|
||||
/// for the same alarm succeeds (D5-style recovery, mirroring how [_error]
|
||||
/// itself already clears on a successful retry).
|
||||
Future<void> _limpiarFalloProgramacion(String alarmaId) async {
|
||||
try {
|
||||
final config = await servicio.limpiarFalloProgramacion(alarmaId);
|
||||
_aplicar(config);
|
||||
} catch (e) {
|
||||
debugPrint('[PluriWave][alarmas] limpiar fallo programacion ERROR $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> cambiarActiva(AlarmaMusical alarma, bool activa) async {
|
||||
await guardarAlarma(alarma.copyWith(activa: activa));
|
||||
}
|
||||
@@ -267,10 +308,12 @@ class EstadoAlarmas extends ChangeNotifier {
|
||||
if (actualizada != null) {
|
||||
await _solicitarPermisosNecesariosParaAlarma();
|
||||
await android.programar(actualizada);
|
||||
await _limpiarFalloProgramacion(alarma.id);
|
||||
}
|
||||
} catch (e) {
|
||||
_error =
|
||||
'Alarma pospuesta, pero Android no pudo reprogramarla todavía: $e';
|
||||
await _registrarFalloProgramacion(alarma.id);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -298,10 +341,12 @@ class EstadoAlarmas extends ChangeNotifier {
|
||||
if (actualizada != null) {
|
||||
await _solicitarPermisosNecesariosParaAlarma();
|
||||
await android.programar(actualizada);
|
||||
await _limpiarFalloProgramacion(alarma.id);
|
||||
}
|
||||
} catch (e) {
|
||||
_error =
|
||||
'Alarma pospuesta, pero Android no pudo reprogramarla todavía: $e';
|
||||
await _registrarFalloProgramacion(alarma.id);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -622,8 +667,24 @@ class EstadoAlarmas extends ChangeNotifier {
|
||||
if (_alarmas.any((alarma) => alarma.activa)) {
|
||||
await _solicitarPermisosNecesariosParaAlarma();
|
||||
}
|
||||
// Per-alarm try/catch (fix/alarmas-fallos-silenciosos): before this, a
|
||||
// SINGLE alarm's `programar` throw aborted the whole loop, so every
|
||||
// sibling AFTER the failing one in `_alarmas` silently never reached
|
||||
// `android.programar` on this pass -- on a fresh launch (`inicializar`)
|
||||
// that meant some active alarms were never (re)armed with the OS at all,
|
||||
// with nothing to show for it beyond a generic load error. Each alarm
|
||||
// now gets its own outcome recorded, and one failure never blocks its
|
||||
// siblings.
|
||||
for (final alarma in _alarmas) {
|
||||
await android.programar(alarma);
|
||||
try {
|
||||
await android.programar(alarma);
|
||||
await _limpiarFalloProgramacion(alarma.id);
|
||||
} catch (e) {
|
||||
debugPrint(
|
||||
'[PluriWave][alarmas] sincronizar todas ERROR id=${alarma.id} $e',
|
||||
);
|
||||
await _registrarFalloProgramacion(alarma.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user