import 'servicio_alarmas_android.dart'; /// One diagnosable Android alarm-reliability signal (fix/alarmas-fiabilidad). /// /// [DiagnosticoAlarmasAndroid] already collects six raw fields, but only /// three were ever surfaced in the UI -- the two most diagnostic ones /// (battery-optimization exemption and the native pending-alarm count) were /// gathered and thrown away. This module maps the raw snapshot into a /// stable, ordered list of user-facing signals with a clear ok/needs- /// attention state, decoupled from Flutter/localization so it stays a /// trivial pure-Dart unit to test. enum SenalDiagnosticoAlarma { alarmasExactas, notificaciones, pantallaCompleta, optimizacionBateria, alarmasNativasPendientes, } enum EstadoSenalDiagnostico { ok, atencion } /// The system screen a "fix this" action should open for a given signal. /// `ninguna` marks signals with no actionable system screen of their own /// (`alarmasNativasPendientes` is informational -- fixing the OTHER signals /// above is what makes it recover). enum AccionDiagnosticoAlarma { abrirAlarmasExactas, abrirNotificaciones, abrirOptimizacionBateria, abrirPantallaCompleta, ninguna, } class ItemDiagnosticoAlarma { const ItemDiagnosticoAlarma({ required this.senal, required this.estado, required this.accion, }); final SenalDiagnosticoAlarma senal; final EstadoSenalDiagnostico estado; final AccionDiagnosticoAlarma accion; bool get requiereAtencion => estado == EstadoSenalDiagnostico.atencion; } /// Builds the five diagnosable signals in a FIXED, stable order so the /// screen renders them consistently every time. /// /// [hayAlarmasActivas] contextualizes `alarmasNativasPendientes`: a fresh /// install with zero alarms turned on has nothing to register with the OS, /// so a `0` count there is only meaningful once the user actually has an /// active alarm -- THAT combination is direct evidence the alarm never /// reached the operating system at all, which is the single most useful /// signal for the reported "alarm never rings" failure mode. List construirItemsDiagnosticoAlarmas({ required DiagnosticoAlarmasAndroid diagnostico, required bool hayAlarmasActivas, }) { EstadoSenalDiagnostico desde(bool ok) => ok ? EstadoSenalDiagnostico.ok : EstadoSenalDiagnostico.atencion; final alarmasNativasOk = !hayAlarmasActivas || diagnostico.alarmasNativasPendientes > 0; return [ ItemDiagnosticoAlarma( senal: SenalDiagnosticoAlarma.alarmasExactas, estado: desde(diagnostico.puedeProgramarExactas), accion: AccionDiagnosticoAlarma.abrirAlarmasExactas, ), ItemDiagnosticoAlarma( senal: SenalDiagnosticoAlarma.notificaciones, estado: desde(diagnostico.notificacionesPermitidas), accion: AccionDiagnosticoAlarma.abrirNotificaciones, ), ItemDiagnosticoAlarma( senal: SenalDiagnosticoAlarma.pantallaCompleta, estado: desde(diagnostico.puedeUsarPantallaCompleta), accion: AccionDiagnosticoAlarma.abrirPantallaCompleta, ), ItemDiagnosticoAlarma( senal: SenalDiagnosticoAlarma.optimizacionBateria, estado: desde(diagnostico.ignoraOptimizacionBateria), accion: AccionDiagnosticoAlarma.abrirOptimizacionBateria, ), ItemDiagnosticoAlarma( senal: SenalDiagnosticoAlarma.alarmasNativasPendientes, estado: desde(alarmasNativasOk), accion: AccionDiagnosticoAlarma.ninguna, ), ]; } /// Manufacturers/sub-brands known for aggressive background-process killing /// that requires the user to manually enable "Autostart" (or the vendor's /// own equivalent toggle) -- there is NO public Android API to detect or /// grant this setting programmatically, so the app can only explain it. const _fabricantesConGuiaAutostart = [ 'xiaomi', 'redmi', 'poco', 'huawei', 'oppo', 'vivo', 'oneplus', 'samsung', ]; /// Whether [fabricante] (`Build.MANUFACTURER`, e.g. "Xiaomi", "POCO") is a /// known aggressive-background-killer vendor that needs the manual autostart /// explanation. Case-insensitive substring match, since `Build.MANUFACTURER` /// values are not fully standardized across sub-brands/regions/builds. bool fabricanteRequiereGuiaAutostart(String fabricante) { final normalizado = fabricante.trim().toLowerCase(); if (normalizado.isEmpty) return false; return _fabricantesConGuiaAutostart.any(normalizado.contains); }