114 lines
3.0 KiB
Dart
114 lines
3.0 KiB
Dart
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<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) {
|
|
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);
|
|
|
|
_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.
|
|
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();
|
|
}
|
|
}
|