import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../estado/estado_alarmas.dart'; import '../l10n/gen/app_localizations.dart'; import '../servicios/diagnostico_alarmas.dart'; import '../servicios/servicio_alarmas_android.dart'; import '../tema/pluriwave_theme.dart'; import '../widgets/pluri_glass_surface.dart'; import '../widgets/pluri_layout.dart'; import '../widgets/pluri_push_scaffold.dart'; /// Full Android alarm-reliability diagnostics screen (fix/alarmas-fiabilidad). /// /// Replaces the old one-line `_AccesoDiagnostico` button in /// `pantalla_alarmas.dart`, which only ever surfaced 3 of the 6 fields /// `DiagnosticoAlarmasAndroid` collects. This screen shows all five /// diagnosable signals with a clear ok/needs-attention state, a "Fix this" /// action that opens the right system settings screen for each failing one, /// plus manufacturer-specific guidance for vendors known to require manually /// enabling Autostart. class PantallaDiagnosticoAlarmas extends StatelessWidget { const PantallaDiagnosticoAlarmas({super.key}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final estado = context.watch(); final diag = estado.diagnostico; return PluriPushScaffold( title: l10n.androidReliabilityTitle, body: diag == null ? ListView( padding: PluriLayout.pageContentPadding, children: [ PluriGlassSurface( child: Text(l10n.alarmDiagnosticsUnavailableHint), ), ], ) : _CuerpoDiagnostico(estado: estado, diag: diag), ); } } class _CuerpoDiagnostico extends StatelessWidget { const _CuerpoDiagnostico({required this.estado, required this.diag}); final EstadoAlarmas estado; final DiagnosticoAlarmasAndroid diag; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final items = construirItemsDiagnosticoAlarmas( diagnostico: diag, hayAlarmasActivas: estado.alarmas.any((alarma) => alarma.activa), ); final mostrarAutostart = fabricanteRequiereGuiaAutostart(diag.fabricante); return ListView( padding: PluriLayout.pageContentPadding, children: [ for (final item in items) ...[ _FilaDiagnostico(item: item, estado: estado, diag: diag), const SizedBox(height: 10), ], const SizedBox(height: 6), PluriGlassSurface( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _FilaInformativa( titulo: l10n.alarmDiagnosticsManufacturerLabel, valor: diag.fabricante, ), const SizedBox(height: 10), _FilaInformativa( titulo: l10n.alarmDiagnosticsSdkLabel, valor: diag.versionSdk.toString(), ), ], ), ), if (mostrarAutostart) ...[ const SizedBox(height: 16), _GuiaAutostart(fabricante: diag.fabricante), ], ], ); } } class _FilaDiagnostico extends StatelessWidget { const _FilaDiagnostico({ required this.item, required this.estado, required this.diag, }); final ItemDiagnosticoAlarma item; final EstadoAlarmas estado; final DiagnosticoAlarmasAndroid diag; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final tokens = context.pluriTokens; final ok = item.estado == EstadoSenalDiagnostico.ok; final color = ok ? tokens.liveGreen : Theme.of(context).colorScheme.error; final esConteoNativo = item.senal == SenalDiagnosticoAlarma.alarmasNativasPendientes; return PluriGlassSurface( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon( ok ? Icons.check_circle_rounded : Icons.warning_amber_rounded, color: color, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( _tituloSenal(l10n, item.senal), style: const TextStyle(fontWeight: FontWeight.w700), ), ), Text( ok ? l10n.statusOk : l10n.alarmDiagnosticsNeedsAttentionStatus, style: TextStyle( color: color, fontWeight: FontWeight.w700, ), ), ], ), const SizedBox(height: 4), if (esConteoNativo) ...[ Text( l10n.alarmDiagnosticsNativeCountValue( diag.alarmasNativasPendientes, ), ), if (!ok) ...[ const SizedBox(height: 4), Text(l10n.alarmDiagnosticsNativeCountAttentionHint), ], ] else Text(_hintSenal(l10n, item.senal)), if (!ok && item.accion != AccionDiagnosticoAlarma.ninguna) ...[ const SizedBox(height: 8), Align( alignment: Alignment.centerRight, child: OutlinedButton( onPressed: () => _ejecutarAccion(context, l10n), child: Text(l10n.alarmDiagnosticsFixAction), ), ), ], ], ), ), ], ), ); } /// Runs the system action for [item.accion] and reloads the diagnostic /// snapshot. Never throws across the widget boundary: every underlying /// `PuertoAlarmasAndroid` call already reports `false` instead (native side /// catches any intent-resolution failure), and a `false` here surfaces a /// calm SnackBar instead of leaving the tap looking like a no-op. Future _ejecutarAccion( BuildContext context, AppLocalizations l10n, ) async { final resuelto = switch (item.accion) { AccionDiagnosticoAlarma.abrirAlarmasExactas => await estado.android.solicitarPermisoAlarmasExactas(), AccionDiagnosticoAlarma.abrirNotificaciones => await estado.android.abrirConfiguracionNotificaciones(), AccionDiagnosticoAlarma.abrirOptimizacionBateria => await estado.android.solicitarExencionBateria(), AccionDiagnosticoAlarma.abrirPantallaCompleta => await estado.android.solicitarPermisoPantallaCompleta(), AccionDiagnosticoAlarma.ninguna => true, }; await estado.cargarDiagnostico(); if (!resuelto && context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(l10n.alarmDiagnosticsIntentUnavailable)), ); } } } String _tituloSenal(AppLocalizations l10n, SenalDiagnosticoAlarma senal) => switch (senal) { SenalDiagnosticoAlarma.alarmasExactas => l10n.alarmDiagnosticsExactAlarmsTitle, SenalDiagnosticoAlarma.notificaciones => l10n.alarmDiagnosticsNotificationsTitle, SenalDiagnosticoAlarma.pantallaCompleta => l10n.alarmDiagnosticsFullScreenTitle, SenalDiagnosticoAlarma.optimizacionBateria => l10n.alarmDiagnosticsBatteryTitle, SenalDiagnosticoAlarma.alarmasNativasPendientes => l10n.alarmDiagnosticsNativeCountTitle, }; /// Static one-line explanation per signal. `alarmasNativasPendientes` builds /// its own dynamic body in [_FilaDiagnostico] instead (count + conditional /// attention hint), so this branch is never actually rendered for it -- kept /// only so the switch stays exhaustive over the enum. String _hintSenal( AppLocalizations l10n, SenalDiagnosticoAlarma senal, ) => switch (senal) { SenalDiagnosticoAlarma.alarmasExactas => l10n.alarmDiagnosticsExactAlarmsHint, SenalDiagnosticoAlarma.notificaciones => l10n.alarmDiagnosticsNotificationsHint, SenalDiagnosticoAlarma.pantallaCompleta => l10n.alarmDiagnosticsFullScreenHint, SenalDiagnosticoAlarma.optimizacionBateria => l10n.alarmDiagnosticsBatteryHint, SenalDiagnosticoAlarma.alarmasNativasPendientes => '', }; class _FilaInformativa extends StatelessWidget { const _FilaInformativa({required this.titulo, required this.valor}); final String titulo; final String valor; @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(titulo), Text(valor, style: const TextStyle(fontWeight: FontWeight.w700)), ], ); } } /// Manufacturer-specific autostart explanation (fix/alarmas-fiabilidad item /// 3). Deliberately never claims the app can detect or grant this setting -- /// there is no public API for it, so this is explanation only, never an /// action button. class _GuiaAutostart extends StatelessWidget { const _GuiaAutostart({required this.fabricante}); final String fabricante; @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final tokens = context.pluriTokens; return PluriGlassSurface( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.info_outline_rounded, color: tokens.warmCoral), const SizedBox(width: 8), Expanded( child: Text( l10n.alarmDiagnosticsAutostartTitle, style: const TextStyle(fontWeight: FontWeight.w700), ), ), ], ), const SizedBox(height: 8), Text(l10n.alarmDiagnosticsAutostartBody(fabricante)), ], ), ); } }