feat(alarmas): surface the three native scheduling failures in Dart

Completes the bridge the native side already exposed. AlarmScheduler and
PluriWaveAlarmService record a pre-notice that could not be armed, a
refused foreground-service start, and a per-alarm reschedule that failed
after a reboot -- but nothing read them, so all three still ended at
logcat.

EstadoAlarmas now drains them at startup and turns each into a per-alarm
exception, which the card already knows how to mark. An alarm that never
reached the OS stops looking identical to one that did.

The read is deliberately tolerant: a failure to read is logged and
swallowed, never surfaced as an alarm error, so a diagnostics gap cannot
masquerade as a scheduling problem.
This commit is contained in:
2026-07-31 23:24:01 +02:00
parent 7722f204ca
commit a8dca83cd9
10 changed files with 599 additions and 16 deletions
+25
View File
@@ -14,6 +14,7 @@ class FakePuertoAlarmasAndroid implements PuertoAlarmasAndroid {
final soloOcultadas = <String>[];
final ejecucionesNativas = <EjecucionAlarmaNativa>[];
final snoozesNativos = <EstadoSnoozeNativo>[];
final fallosProgramacionNativos = <FalloProgramacionNativo>[];
final _eventos = StreamController<EventoAlarmaAndroid>.broadcast();
bool ignoraOptimizacionBateria = true;
int solicitudesExencionBateria = 0;
@@ -186,9 +187,33 @@ class FakePuertoAlarmasAndroid implements PuertoAlarmasAndroid {
Future<List<EstadoSnoozeNativo>> obtenerEstadoSnoozeNativo() async =>
List.of(snoozesNativos);
@override
Future<List<FalloProgramacionNativo>>
obtenerFallosProgramacionNativos() async =>
List.of(fallosProgramacionNativos);
int solicitudesPermisoAlarmasExactas = 0;
int solicitudesPermisoPantallaCompleta = 0;
/// Native-recorded failures the next read should return. Tests seed this
/// to simulate a pre-notice that never armed, a refused foreground-service
/// start, or a per-alarm reschedule that failed after a reboot.
List<Map<String, Object?>> fallosNativos = const [];
int lecturasFallosNativos = 0;
/// Simulates an older native build with no such channel method.
bool fallaLecturaFallosNativos = false;
@override
Future<List<Map<String, Object?>>> fallosNativosProgramacion() async {
lecturasFallosNativos++;
if (fallaLecturaFallosNativos) {
throw StateError('canal no disponible');
}
return fallosNativos;
}
@override
Future<bool> solicitarPermisoAlarmasExactas() async {
solicitudesPermisoAlarmasExactas++;