Files
pluriwave/lib/servicios/servicio_timer.dart
T
FreeTLab e1d1d6c639
Build & Deploy Pluriwave / Análisis de código (push) Successful in 21s
Build & Deploy Pluriwave / Build APK + AAB release (push) Successful in 2m19s
feat(ui): refine navigation and sleep timer
2026-05-22 13:13:05 +02:00

132 lines
3.3 KiB
Dart

import 'dart:async';
import 'servicio_audio.dart';
/// Opciones predefinidas de timer en segundos.
const opcionesTimerSegundos = [
180,
300,
600,
900,
1800,
3600,
5400,
7200,
10800,
];
/// Compatibilidad con el reproductor completo, que aún muestra minutos.
const opcionesTimer = [3, 5, 10, 15, 30, 60, 90, 120, 180];
/// 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<Duration>.broadcast();
ServicioTimer(this._audio);
/// Stream que emite el tiempo restante cada segundo.
Stream<Duration> get tiempoRestanteStream => _controller.stream;
Duration get tiempoRestante => _tiempoRestante;
bool get activo => _activo;
/// Inicia el timer para [minutos] minutos.
void iniciar(int minutos) {
iniciarDuracion(Duration(minutes: minutos));
}
/// Inicia el timer para una duración exacta.
void iniciarDuracion(Duration duracion) {
if (duracion <= Duration.zero) return;
unawaited(cancelar());
_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);
_timer?.cancel();
_timer = null;
_activo = false;
_iniciarFadeOut();
} else {
_tiempoRestante = restante;
_controller.add(_tiempoRestante);
}
// 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.
Future<void> cancelar({bool detenerAudio = false}) async {
_timer?.cancel();
_timer = null;
_fadeTicker?.cancel();
_fadeTicker = null;
_activo = false;
_tiempoRestante = Duration.zero;
_controller.add(_tiempoRestante);
if (detenerAudio) {
await _audio.detener();
}
}
void dispose() {
unawaited(cancelar());
_controller.close();
}
}