import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../estado/estado_visualizador.dart'; import '../../l10n/gen/app_localizations.dart'; import '../../widgets/pluri_glass_surface.dart'; import '../../widgets/pluri_layout.dart'; import '../../widgets/pluri_push_scaffold.dart'; /// AUDIO group · "Onda real del audio" — the point-of-intent opt-in for the /// waveform visualizer's microphone permission. /// /// Before this screen existed, `RECORD_AUDIO` was requested the moment /// `VisualizadorAudio` subscribed to its native EventChannel, which the home /// screen's "Escuchar" hero does on the user's FIRST play. A radio app that /// pops "allow PluriWave to record audio?" the first time you press play is /// asking for a sensitive permission with zero context, and Play expects /// context. /// /// Mirrors `PantallaAjustesSalidaAudio`'s point-of-intent shape (its /// BLUETOOTH_CONNECT request sits behind the multi-device toggle the same /// way), with one addition: the explanation is shown and accepted BEFORE the /// flag flips, since flipping it is what triggers the system dialog. class PantallaAjustesVisualizador extends StatelessWidget { const PantallaAjustesVisualizador({super.key}); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); return PluriPushScaffold( title: l10n.visualizerRealWaveTitle, body: ListView( padding: PluriLayout.pageContentPadding, children: const [_CuerpoVisualizador()], ), ); } } class _CuerpoVisualizador extends StatelessWidget { const _CuerpoVisualizador(); @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); final estado = context.watch(); final habilitada = estado.ondaRealHabilitada; return PluriGlassSurface( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // GestureDetector + custom row instead of SwitchListTile, for the // same reason PantallaAjustesSalidaAudio does it: ListTile ink // inside PluriGlassSurface's DecoratedBox trips a Material // assertion. Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ Expanded( child: GestureDetector( behavior: HitTestBehavior.translucent, onTap: () => _alternar(context, estado, !habilitada), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l10n.visualizerRealWaveTitle, style: Theme.of(context).textTheme.bodyLarge, ), const SizedBox(height: 2), Text( l10n.visualizerRealWaveSubtitle, style: Theme.of(context).textTheme.bodySmall, ), ], ), ), ), Switch.adaptive( value: habilitada, onChanged: (valor) => _alternar(context, estado, valor), ), ], ), ), const SizedBox(height: 8), // The same text the confirmation dialog shows, kept permanently on // screen: a user who already granted the permission should be able // to re-read what it is for without toggling anything. Text( l10n.visualizerRealWavePermissionExplanation, style: Theme.of(context).textTheme.bodySmall, ), ], ), ); } /// Turning it OFF is immediate — withdrawing a permission must never be /// harder than granting it. Turning it ON goes through the explanation /// first, and only a deliberate confirmation flips the flag. Future _alternar( BuildContext context, EstadoVisualizador estado, bool habilitada, ) async { if (!habilitada) { await estado.cambiarOndaReal(false); return; } final l10n = AppLocalizations.of(context); final confirmado = await showDialog( context: context, builder: (ctx) => AlertDialog( key: const ValueKey('visualizador-explicacion-permiso'), title: Text(l10n.visualizerRealWaveTitle), content: Text(l10n.visualizerRealWavePermissionExplanation), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: Text(l10n.cancelAction), ), FilledButton( onPressed: () => Navigator.pop(ctx, true), child: Text(l10n.visualizerRealWaveEnableAction), ), ], ), ); if (confirmado != true) return; await estado.cambiarOndaReal(true); } }