import 'dart:async'; import 'servicio_audio.dart'; /// Opciones predefinidas de timer en minutos. const opcionesTimer = [15, 30, 60, 90]; /// Servicio de auto-apagado de la radio. /// /// Cuenta atrás desde la duración elegida. Cuando llega a 0: /// 1. Hace un fade out de [_fadeDuracion] segundos. /// 2. Llama a [ServicioAudio.detener]. /// /// El UI puede escuchar [tiempoRestanteStream] para mostrar el contador. class ServicioTimer { final ServicioAudio _audio; Timer? _timer; Timer? _fadeTicker; DateTime? _finAt; Duration _tiempoRestante = Duration.zero; bool _activo = false; static const _fadeDuracion = Duration(seconds: 30); static const _fadeStep = Duration(seconds: 1); final _controller = StreamController.broadcast(); ServicioTimer(this._audio); /// Stream que emite el tiempo restante cada segundo. Stream get tiempoRestanteStream => _controller.stream; Duration get tiempoRestante => _tiempoRestante; bool get activo => _activo; /// Inicia el timer para [minutos] minutos. void iniciar(int minutos) { cancelar(); final duracion = Duration(minutes: minutos); _finAt = DateTime.now().add(duracion); _tiempoRestante = duracion; _activo = true; _controller.add(_tiempoRestante); _timer = Timer.periodic(const Duration(seconds: 1), (_) { final ahora = DateTime.now(); final restante = _finAt!.difference(ahora); if (restante <= Duration.zero) { _tiempoRestante = Duration.zero; _controller.add(_tiempoRestante); _iniciarFadeOut(); cancelar(detenerAudio: false); } else { _tiempoRestante = restante; _controller.add(_tiempoRestante); } }); } void _iniciarFadeOut() { final pasos = _fadeDuracion.inSeconds; final volumenInicial = _audio.volumen; int paso = 0; _fadeTicker = Timer.periodic(_fadeStep, (_) async { paso++; final nuevoVolumen = volumenInicial * (1 - paso / pasos); await _audio.setVolumen(nuevoVolumen.clamp(0.0, 1.0)); if (paso >= pasos) { _fadeTicker?.cancel(); await _audio.detener(); await _audio.setVolumen(volumenInicial); // restaurar volumen para próxima vez } }); } /// Cancela el timer activo sin detener el audio. void cancelar({bool detenerAudio = false}) { _timer?.cancel(); _timer = null; _fadeTicker?.cancel(); _fadeTicker = null; _activo = false; _tiempoRestante = Duration.zero; _controller.add(_tiempoRestante); if (detenerAudio) { _audio.detener(); } } void dispose() { cancelar(); _controller.close(); } }