fix(alarms): prevent overlapping playback
This commit is contained in:
+50
-11
@@ -63,6 +63,8 @@ class _PaginaPrincipalState extends State<_PaginaPrincipal> {
|
|||||||
StreamSubscription<AlarmaMusical>? _alarmaVencidaSubscription;
|
StreamSubscription<AlarmaMusical>? _alarmaVencidaSubscription;
|
||||||
EstadoRadio? _estadoSuscrito;
|
EstadoRadio? _estadoSuscrito;
|
||||||
bool _alarmaInicialProcesada = false;
|
bool _alarmaInicialProcesada = false;
|
||||||
|
bool _alarmaSonandoActiva = false;
|
||||||
|
String? _alarmaSonandoId;
|
||||||
|
|
||||||
static const _paginas = [
|
static const _paginas = [
|
||||||
PantallaInicio(),
|
PantallaInicio(),
|
||||||
@@ -224,21 +226,58 @@ class _PaginaPrincipalState extends State<_PaginaPrincipal> {
|
|||||||
setState(() => _indice = 3);
|
setState(() => _indice = 3);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await Navigator.of(context).push(
|
await _mostrarAlarmaSonando(alarma);
|
||||||
MaterialPageRoute<void>(
|
|
||||||
builder: (_) => PantallaAlarmaSonando(alarma: alarma!),
|
|
||||||
fullscreenDialog: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _abrirAlarmaDirecta(AlarmaMusical alarma) async {
|
Future<void> _abrirAlarmaDirecta(AlarmaMusical alarma) async {
|
||||||
await Navigator.of(context).push(
|
await _mostrarAlarmaSonando(alarma);
|
||||||
MaterialPageRoute<void>(
|
}
|
||||||
builder: (_) => PantallaAlarmaSonando(alarma: alarma),
|
|
||||||
fullscreenDialog: true,
|
Future<void> _mostrarAlarmaSonando(AlarmaMusical alarma) async {
|
||||||
),
|
final alarmas = context.read<EstadoAlarmas>();
|
||||||
|
alarmas.marcarEjecucionGestionada(alarma);
|
||||||
|
|
||||||
|
if (_alarmaSonandoActiva) {
|
||||||
|
debugPrint(
|
||||||
|
'[PluriWave][alarmas] alarma ignorada porque ya hay una activa id=${alarma.id} activa=$_alarmaSonandoId',
|
||||||
|
);
|
||||||
|
await alarmas.android.ocultarNotificacionAlarma(alarma.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_alarmaSonandoActiva = true;
|
||||||
|
_alarmaSonandoId = alarma.id;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _prearrancarAudioAlarma(alarma);
|
||||||
|
if (!mounted) return;
|
||||||
|
await Navigator.of(context).push(
|
||||||
|
MaterialPageRoute<void>(
|
||||||
|
builder:
|
||||||
|
(_) => PantallaAlarmaSonando(
|
||||||
|
alarma: alarma,
|
||||||
|
audioPrearrancado: alarma.emisora != null,
|
||||||
|
),
|
||||||
|
fullscreenDialog: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
if (_alarmaSonandoId == alarma.id) {
|
||||||
|
_alarmaSonandoActiva = false;
|
||||||
|
_alarmaSonandoId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _prearrancarAudioAlarma(AlarmaMusical alarma) async {
|
||||||
|
final emisora = alarma.emisora;
|
||||||
|
if (emisora == null) return;
|
||||||
|
final radio = context.read<EstadoRadio>();
|
||||||
|
debugPrint(
|
||||||
|
'[PluriWave][alarmas] prearrancar emisora alarma id=${alarma.id} emisora=${emisora.nombre}',
|
||||||
);
|
);
|
||||||
|
await radio.audio.setVolumen(alarma.volumen.clamp(0.0, 1.0));
|
||||||
|
unawaited(radio.reproducir(emisora));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _mostrarTimerDialog(BuildContext context) {
|
void _mostrarTimerDialog(BuildContext context) {
|
||||||
|
|||||||
@@ -96,6 +96,16 @@ class EstadoAlarmas extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void marcarEjecucionGestionada(AlarmaMusical alarma) {
|
||||||
|
final proxima = alarma.proximaEjecucion;
|
||||||
|
if (proxima == null) return;
|
||||||
|
final key = '${alarma.id}:${proxima.millisecondsSinceEpoch}';
|
||||||
|
_ejecucionesEmitidas.add(key);
|
||||||
|
debugPrint(
|
||||||
|
'[PluriWave][alarmas] ejecucion gestionada id=${alarma.id} proxima=${proxima.toIso8601String()}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> eliminarAlarma(String id) async {
|
Future<void> eliminarAlarma(String id) async {
|
||||||
debugPrint('[PluriWave][alarmas] eliminar id=$id');
|
debugPrint('[PluriWave][alarmas] eliminar id=$id');
|
||||||
final config = await servicio.eliminarAlarma(id);
|
final config = await servicio.eliminarAlarma(id);
|
||||||
|
|||||||
@@ -11,9 +11,14 @@ import '../servicios/servicio_audio.dart';
|
|||||||
import '../widgets/pluri_glass_surface.dart';
|
import '../widgets/pluri_glass_surface.dart';
|
||||||
|
|
||||||
class PantallaAlarmaSonando extends StatefulWidget {
|
class PantallaAlarmaSonando extends StatefulWidget {
|
||||||
const PantallaAlarmaSonando({super.key, required this.alarma});
|
const PantallaAlarmaSonando({
|
||||||
|
super.key,
|
||||||
|
required this.alarma,
|
||||||
|
this.audioPrearrancado = false,
|
||||||
|
});
|
||||||
|
|
||||||
final AlarmaMusical alarma;
|
final AlarmaMusical alarma;
|
||||||
|
final bool audioPrearrancado;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PantallaAlarmaSonando> createState() => _PantallaAlarmaSonandoState();
|
State<PantallaAlarmaSonando> createState() => _PantallaAlarmaSonandoState();
|
||||||
@@ -45,7 +50,9 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
|
|||||||
|
|
||||||
_radioIntentada = true;
|
_radioIntentada = true;
|
||||||
await radio.audio.setVolumen(widget.alarma.volumen.clamp(0.0, 1.0));
|
await radio.audio.setVolumen(widget.alarma.volumen.clamp(0.0, 1.0));
|
||||||
unawaited(radio.reproducir(emisora));
|
if (!widget.audioPrearrancado) {
|
||||||
|
unawaited(radio.reproducir(emisora));
|
||||||
|
}
|
||||||
|
|
||||||
_estadoSub = radio.estadoStream.listen((estado) {
|
_estadoSub = radio.estadoStream.listen((estado) {
|
||||||
if (estado == EstadoReproduccion.reproduciendo && mounted) {
|
if (estado == EstadoReproduccion.reproduciendo && mounted) {
|
||||||
@@ -59,6 +66,9 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
|
|||||||
_fallbackTimer = Timer(const Duration(seconds: 12), () {
|
_fallbackTimer = Timer(const Duration(seconds: 12), () {
|
||||||
if (mounted) _iniciarFallback();
|
if (mounted) _iniciarFallback();
|
||||||
});
|
});
|
||||||
|
if (widget.audioPrearrancado && radio.audio.estaSonando) {
|
||||||
|
_fallbackTimer?.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _iniciarFallback() async {
|
Future<void> _iniciarFallback() async {
|
||||||
|
|||||||
Reference in New Issue
Block a user