refactor(state): extract export/import service and equalizer state from EstadoRadio
- New ServicioExportImport owns the v2 backup envelope, pretty JSON encode and graceful decode; byte-compatible with existing exports, locked by a round-trip test - pantalla_ajustes delegates backup serialization to the service (inline jsonDecode/jsonEncode removed) - New EstadoEcualizador ChangeNotifier owns all EQ state and persistence (principal/current/per-station presets, active flag), exposed via its own provider so EQ changes no longer rebuild EstadoRadio consumers - EstadoRadio slims down ~210 lines and keeps 15 delegating compat members marked TODO(S4b) for the next slice to remove - Player EQ toggle rewired to the new provider to avoid going stale - 4 new tests (103 total green), flutter analyze clean
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../modelos/preset_ecualizador.dart';
|
||||
import '../servicios/servicio_audio.dart';
|
||||
import '../servicios/servicio_ecualizador.dart';
|
||||
|
||||
/// Equalizer state extracted from `EstadoRadio` (S4-R1).
|
||||
///
|
||||
/// Owns the main preset, the per-station preset map, the current (applied)
|
||||
/// preset and the enabled flag, plus their persistence through
|
||||
/// [ServicioEcualizador] and their application through [ServicioAudio].
|
||||
/// Notifies ONLY its own listeners — EQ changes must not rebuild
|
||||
/// `EstadoRadio` consumers (S4-R1-A, S4-R5).
|
||||
class EstadoEcualizador extends ChangeNotifier {
|
||||
EstadoEcualizador({
|
||||
required this.audio,
|
||||
ServicioEcualizador? servicio,
|
||||
String? Function()? emisoraActualUuid,
|
||||
}) : servicio = servicio ?? ServicioEcualizador(),
|
||||
_emisoraActualUuid = emisoraActualUuid ?? (() => null);
|
||||
|
||||
final ServicioAudio audio;
|
||||
final ServicioEcualizador servicio;
|
||||
|
||||
/// Callback into the owner (EstadoRadio) for the currently playing station;
|
||||
/// keeps this notifier free of any station-list coupling.
|
||||
final String? Function() _emisoraActualUuid;
|
||||
|
||||
final Map<String, PresetEcualizador> _presetsEmisoraMap = {};
|
||||
PresetEcualizador _presetPrincipal = PresetEcualizador.flat;
|
||||
PresetEcualizador _presetActual = PresetEcualizador.flat;
|
||||
bool _activo = true;
|
||||
|
||||
PresetEcualizador get presetActual => _presetActual;
|
||||
PresetEcualizador get presetPrincipal => _presetPrincipal;
|
||||
bool get activo => _activo;
|
||||
bool get disponible => audio.ecualizadorDisponible;
|
||||
Map<String, PresetEcualizador> get presetsPorEmisora =>
|
||||
Map.unmodifiable(_presetsEmisoraMap);
|
||||
|
||||
bool get emisoraActualTienePresetPropio {
|
||||
final uuid = _emisoraActualUuid();
|
||||
if (uuid == null) return false;
|
||||
return tienePresetPorEmisora(uuid);
|
||||
}
|
||||
|
||||
bool tienePresetPorEmisora(String uuid) =>
|
||||
_presetsEmisoraMap.containsKey(uuid);
|
||||
|
||||
PresetEcualizador? presetPorEmisora(String uuid) => _presetsEmisoraMap[uuid];
|
||||
|
||||
PresetEcualizador presetParaEmisora(String uuid) =>
|
||||
_presetsEmisoraMap[uuid] ?? _presetPrincipal;
|
||||
|
||||
/// Loads the persisted EQ configuration and applies it to the audio engine.
|
||||
Future<void> cargarPersistido() async {
|
||||
try {
|
||||
final config = await servicio.cargar();
|
||||
_presetPrincipal = config.principal;
|
||||
_presetActual = config.principal;
|
||||
_activo = config.activo;
|
||||
_presetsEmisoraMap
|
||||
..clear()
|
||||
..addAll(config.porEmisora);
|
||||
await audio.setEcualizadorActivo(_activo);
|
||||
await audio.aplicarPreset(_presetPrincipal);
|
||||
} catch (_) {
|
||||
_presetPrincipal = PresetEcualizador.flat;
|
||||
_presetActual = PresetEcualizador.flat;
|
||||
_activo = true;
|
||||
_presetsEmisoraMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies [preset] to the audio engine and tracks it as current
|
||||
/// WITHOUT persisting it (used when switching stations).
|
||||
Future<void> aplicarPresetActivo(PresetEcualizador preset) async {
|
||||
_presetActual = preset;
|
||||
await audio.aplicarPreset(preset);
|
||||
}
|
||||
|
||||
Future<void> cambiarPresetPrincipal(
|
||||
PresetEcualizador preset, {
|
||||
bool notificar = true,
|
||||
}) async {
|
||||
_presetPrincipal = preset;
|
||||
await servicio.guardarPrincipal(preset);
|
||||
|
||||
final uuid = _emisoraActualUuid();
|
||||
final puedeAplicarAhora =
|
||||
uuid == null || !_presetsEmisoraMap.containsKey(uuid);
|
||||
if (puedeAplicarAhora) {
|
||||
await aplicarPresetActivo(preset);
|
||||
}
|
||||
|
||||
if (notificar) notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> guardarPresetPorEmisora(
|
||||
String uuid,
|
||||
PresetEcualizador preset, {
|
||||
bool notificar = true,
|
||||
}) async {
|
||||
_presetsEmisoraMap[uuid] = preset;
|
||||
await servicio.guardarPorEmisora(uuid, preset);
|
||||
if (_emisoraActualUuid() == uuid) {
|
||||
await aplicarPresetActivo(preset);
|
||||
}
|
||||
if (notificar) notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> habilitarPresetPorEmisora(
|
||||
String uuid, {
|
||||
PresetEcualizador? base,
|
||||
bool notificar = true,
|
||||
}) async {
|
||||
final presetBase = base ?? _presetsEmisoraMap[uuid] ?? _presetPrincipal;
|
||||
await guardarPresetPorEmisora(uuid, presetBase, notificar: notificar);
|
||||
}
|
||||
|
||||
Future<void> deshabilitarPresetPorEmisora(
|
||||
String uuid, {
|
||||
bool notificar = true,
|
||||
}) async {
|
||||
_presetsEmisoraMap.remove(uuid);
|
||||
await servicio.eliminarPorEmisora(uuid);
|
||||
if (_emisoraActualUuid() == uuid) {
|
||||
await aplicarPresetActivo(_presetPrincipal);
|
||||
}
|
||||
if (notificar) notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> cambiarModoEmisoraActual({required bool usarPropio}) async {
|
||||
final uuid = _emisoraActualUuid();
|
||||
if (uuid == null) return;
|
||||
if (usarPropio) {
|
||||
await habilitarPresetPorEmisora(uuid);
|
||||
} else {
|
||||
await deshabilitarPresetPorEmisora(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> cambiarActivo(bool activo) async {
|
||||
_activo = activo;
|
||||
await servicio.guardarActivo(activo);
|
||||
await audio.setEcualizadorActivo(activo);
|
||||
if (activo) {
|
||||
await audio.aplicarPreset(_presetActual);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> cambiarPreset(
|
||||
PresetEcualizador preset, {
|
||||
bool guardarPorEmisora = true,
|
||||
}) async {
|
||||
final uuid = _emisoraActualUuid();
|
||||
final usarPresetPropio =
|
||||
guardarPorEmisora &&
|
||||
uuid != null &&
|
||||
_presetsEmisoraMap.containsKey(uuid);
|
||||
|
||||
if (usarPresetPropio) {
|
||||
await guardarPresetPorEmisora(uuid, preset);
|
||||
return;
|
||||
}
|
||||
await cambiarPresetPrincipal(preset);
|
||||
}
|
||||
|
||||
Future<void> cambiarBanda(int index, double db) async {
|
||||
final bandas = List<double>.from(_presetActual.bandas);
|
||||
if (index < 0 || index >= bandas.length) return;
|
||||
|
||||
bandas[index] = db;
|
||||
final modificado = PresetEcualizador(
|
||||
nombre: 'Personalizado',
|
||||
bandas: bandas,
|
||||
);
|
||||
await cambiarPreset(modificado);
|
||||
}
|
||||
|
||||
/// Replaces the whole EQ configuration (backup import path): persists it,
|
||||
/// re-applies the preset effective for the current station and notifies.
|
||||
Future<void> importarConfiguracion({
|
||||
required PresetEcualizador principal,
|
||||
required Map<String, PresetEcualizador> porEmisora,
|
||||
}) async {
|
||||
_presetPrincipal = principal;
|
||||
_presetsEmisoraMap
|
||||
..clear()
|
||||
..addAll(porEmisora);
|
||||
|
||||
await servicio.guardarConfiguracion(
|
||||
ConfiguracionEcualizador(
|
||||
principal: _presetPrincipal,
|
||||
porEmisora: _presetsEmisoraMap,
|
||||
activo: _activo,
|
||||
),
|
||||
);
|
||||
|
||||
final uuid = _emisoraActualUuid();
|
||||
await aplicarPresetActivo(
|
||||
uuid == null ? _presetPrincipal : presetParaEmisora(uuid),
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
+94
-174
@@ -16,8 +16,10 @@ import '../l10n/gen/app_localizations.dart';
|
||||
import '../modelos/emisora.dart';
|
||||
import '../modelos/grupo_favoritos.dart';
|
||||
import '../modelos/preset_ecualizador.dart';
|
||||
import 'estado_ecualizador.dart';
|
||||
import '../servicios/servicio_audio.dart';
|
||||
import '../servicios/servicio_ecualizador.dart';
|
||||
import '../servicios/servicio_export_import.dart';
|
||||
import '../servicios/servicio_favoritos.dart';
|
||||
import '../servicios/servicio_grabacion_radio.dart';
|
||||
import '../servicios/servicio_radio.dart';
|
||||
@@ -48,6 +50,11 @@ class EstadoRadio extends ChangeNotifier {
|
||||
grabacion = servicioGrabacion ?? ServicioGrabacionRadio(prefs: prefs),
|
||||
_prefs = prefs,
|
||||
_resolverArchivoCustom = resolverArchivoCustom {
|
||||
ecualizador = EstadoEcualizador(
|
||||
audio: this.audio,
|
||||
servicio: this.servicioEcualizador,
|
||||
emisoraActualUuid: () => emisoraActual?.uuid,
|
||||
);
|
||||
timer = ServicioTimer(this.audio);
|
||||
_escucharErroresReproduccion();
|
||||
_escucharGrabacion();
|
||||
@@ -60,7 +67,13 @@ class EstadoRadio extends ChangeNotifier {
|
||||
final ServicioFavoritos favoritos;
|
||||
final ServicioRadio radio;
|
||||
final ServicioEcualizador servicioEcualizador;
|
||||
|
||||
/// EQ state extracted to its own notifier (S4-R1). Owned (and disposed)
|
||||
/// by EstadoRadio during the S4 transition; exposed app-wide through a
|
||||
/// ListenableProvider in app.dart.
|
||||
late final EstadoEcualizador ecualizador;
|
||||
final ServicioGrabacionRadio grabacion;
|
||||
static const ServicioExportImport _exportImport = ServicioExportImport();
|
||||
final SharedPreferences? _prefs;
|
||||
final Future<File> Function()? _resolverArchivoCustom;
|
||||
|
||||
@@ -105,12 +118,6 @@ class EstadoRadio extends ChangeNotifier {
|
||||
List<GrupoFavoritos> _gruposFavoritos = [];
|
||||
List<Emisora> _emisorasCustom = [];
|
||||
|
||||
// Presets EQ guardados por uuid de emisora.
|
||||
final Map<String, PresetEcualizador> _presetsEmisoraMap = {};
|
||||
PresetEcualizador _presetPrincipal = PresetEcualizador.flat;
|
||||
PresetEcualizador _presetActual = PresetEcualizador.flat;
|
||||
bool _ecualizadorActivo = true;
|
||||
|
||||
bool _cargandoPopulares = false;
|
||||
bool _cargandoBusqueda = false;
|
||||
bool _cargandoMasBusqueda = false;
|
||||
@@ -164,10 +171,15 @@ class EstadoRadio extends ChangeNotifier {
|
||||
Emisora? get emisoraPreferida => _resolverEmisoraPreferida();
|
||||
String? get emisoraPreferidaUuid => emisoraPreferida?.uuid;
|
||||
Stream<EstadoReproduccion> get estadoStream => audio.estadoStream;
|
||||
PresetEcualizador get presetEcualizador => _presetActual;
|
||||
PresetEcualizador get presetPrincipalEcualizador => _presetPrincipal;
|
||||
bool get ecualizadorActivo => _ecualizadorActivo;
|
||||
bool get ecualizadorDisponible => audio.ecualizadorDisponible;
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
PresetEcualizador get presetEcualizador => ecualizador.presetActual;
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
PresetEcualizador get presetPrincipalEcualizador =>
|
||||
ecualizador.presetPrincipal;
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
bool get ecualizadorActivo => ecualizador.activo;
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
bool get ecualizadorDisponible => ecualizador.disponible;
|
||||
OrdenEmisoras get ordenListas => _ordenListas;
|
||||
List<int> get timerSuenoPresetsSegundos =>
|
||||
List<int>.unmodifiable(_timerSuenoPresetsSegundos);
|
||||
@@ -178,11 +190,9 @@ class EstadoRadio extends ChangeNotifier {
|
||||
return _listaFavoritos.any((e) => e.uuid == actual.uuid);
|
||||
}
|
||||
|
||||
bool get emisoraActualTienePresetPropio {
|
||||
final actual = emisoraActual;
|
||||
if (actual == null) return false;
|
||||
return tienePresetEcualizadorPorEmisora(actual.uuid);
|
||||
}
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
bool get emisoraActualTienePresetPropio =>
|
||||
ecualizador.emisoraActualTienePresetPropio;
|
||||
|
||||
EstadoGrabacionRadio get estadoGrabacion => grabacion.estado;
|
||||
bool get grabacionActiva => grabacion.estado.activa;
|
||||
@@ -232,7 +242,7 @@ class EstadoRadio extends ChangeNotifier {
|
||||
|
||||
Future<void> _init() async {
|
||||
await grabacion.inicializar();
|
||||
await _cargarEcualizadorPersistido();
|
||||
await ecualizador.cargarPersistido();
|
||||
await _cargarOrdenListas();
|
||||
await _cargarEmisoraPreferida();
|
||||
await _cargarTimerSuenoPresets();
|
||||
@@ -271,25 +281,6 @@ class EstadoRadio extends ChangeNotifier {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _cargarEcualizadorPersistido() async {
|
||||
try {
|
||||
final config = await servicioEcualizador.cargar();
|
||||
_presetPrincipal = config.principal;
|
||||
_presetActual = config.principal;
|
||||
_ecualizadorActivo = config.activo;
|
||||
_presetsEmisoraMap
|
||||
..clear()
|
||||
..addAll(config.porEmisora);
|
||||
await audio.setEcualizadorActivo(_ecualizadorActivo);
|
||||
await audio.aplicarPreset(_presetPrincipal);
|
||||
} catch (_) {
|
||||
_presetPrincipal = PresetEcualizador.flat;
|
||||
_presetActual = PresetEcualizador.flat;
|
||||
_ecualizadorActivo = true;
|
||||
_presetsEmisoraMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> cargarPopulares() async {
|
||||
_cargandoPopulares = true;
|
||||
_errorCarga = null;
|
||||
@@ -609,7 +600,9 @@ class EstadoRadio extends ChangeNotifier {
|
||||
await audio.reproducir(emisora);
|
||||
if (revision != _revisionReproduccion) return;
|
||||
unawaited(radio.registrarClick(emisora.uuid));
|
||||
await _aplicarPresetActivo(_presetParaEmisora(emisora.uuid));
|
||||
await ecualizador.aplicarPresetActivo(
|
||||
ecualizador.presetParaEmisora(emisora.uuid),
|
||||
);
|
||||
if (revision != _revisionReproduccion) return;
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
@@ -728,126 +721,66 @@ class EstadoRadio extends ChangeNotifier {
|
||||
Future<bool> esFavorito(String uuid) => favoritos.esFavorito(uuid);
|
||||
|
||||
// ── Ecualizador ───────────────────────────────────────────────────────────
|
||||
// Transition bridge (S4a): EQ state lives in EstadoEcualizador; these
|
||||
// delegating members keep legacy call sites compiling. They do NOT notify
|
||||
// EstadoRadio listeners (S4-R1-A).
|
||||
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
bool tienePresetEcualizadorPorEmisora(String uuid) =>
|
||||
_presetsEmisoraMap.containsKey(uuid);
|
||||
ecualizador.tienePresetPorEmisora(uuid);
|
||||
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
PresetEcualizador? presetEcualizadorPorEmisora(String uuid) =>
|
||||
_presetsEmisoraMap[uuid];
|
||||
|
||||
PresetEcualizador _presetParaEmisora(String uuid) =>
|
||||
_presetsEmisoraMap[uuid] ?? _presetPrincipal;
|
||||
|
||||
Future<void> _aplicarPresetActivo(PresetEcualizador preset) async {
|
||||
_presetActual = preset;
|
||||
await audio.aplicarPreset(preset);
|
||||
}
|
||||
ecualizador.presetPorEmisora(uuid);
|
||||
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
Future<void> cambiarPresetPrincipalEcualizador(
|
||||
PresetEcualizador preset, {
|
||||
bool notificar = true,
|
||||
}) async {
|
||||
_presetPrincipal = preset;
|
||||
await servicioEcualizador.guardarPrincipal(preset);
|
||||
|
||||
final actual = emisoraActual;
|
||||
final puedeAplicarAhora =
|
||||
actual == null || !_presetsEmisoraMap.containsKey(actual.uuid);
|
||||
if (puedeAplicarAhora) {
|
||||
await _aplicarPresetActivo(preset);
|
||||
}
|
||||
|
||||
if (notificar) notifyListeners();
|
||||
}
|
||||
}) => ecualizador.cambiarPresetPrincipal(preset, notificar: notificar);
|
||||
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
Future<void> guardarPresetEcualizadorPorEmisora(
|
||||
String uuid,
|
||||
PresetEcualizador preset, {
|
||||
bool notificar = true,
|
||||
}) async {
|
||||
_presetsEmisoraMap[uuid] = preset;
|
||||
await servicioEcualizador.guardarPorEmisora(uuid, preset);
|
||||
if (emisoraActual?.uuid == uuid) {
|
||||
await _aplicarPresetActivo(preset);
|
||||
}
|
||||
if (notificar) notifyListeners();
|
||||
}
|
||||
}) => ecualizador.guardarPresetPorEmisora(uuid, preset, notificar: notificar);
|
||||
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
Future<void> habilitarPresetEcualizadorPorEmisora(
|
||||
String uuid, {
|
||||
PresetEcualizador? base,
|
||||
bool notificar = true,
|
||||
}) async {
|
||||
final presetBase = base ?? _presetsEmisoraMap[uuid] ?? _presetPrincipal;
|
||||
await guardarPresetEcualizadorPorEmisora(
|
||||
uuid,
|
||||
presetBase,
|
||||
notificar: notificar,
|
||||
);
|
||||
}
|
||||
}) => ecualizador.habilitarPresetPorEmisora(
|
||||
uuid,
|
||||
base: base,
|
||||
notificar: notificar,
|
||||
);
|
||||
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
Future<void> deshabilitarPresetEcualizadorPorEmisora(
|
||||
String uuid, {
|
||||
bool notificar = true,
|
||||
}) async {
|
||||
_presetsEmisoraMap.remove(uuid);
|
||||
await servicioEcualizador.eliminarPorEmisora(uuid);
|
||||
if (emisoraActual?.uuid == uuid) {
|
||||
await _aplicarPresetActivo(_presetPrincipal);
|
||||
}
|
||||
if (notificar) notifyListeners();
|
||||
}
|
||||
}) => ecualizador.deshabilitarPresetPorEmisora(uuid, notificar: notificar);
|
||||
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
Future<void> cambiarModoEcualizadorEmisoraActual({
|
||||
required bool usarPropio,
|
||||
}) async {
|
||||
final actual = emisoraActual;
|
||||
if (actual == null) return;
|
||||
if (usarPropio) {
|
||||
await habilitarPresetEcualizadorPorEmisora(actual.uuid);
|
||||
} else {
|
||||
await deshabilitarPresetEcualizadorPorEmisora(actual.uuid);
|
||||
}
|
||||
}
|
||||
}) => ecualizador.cambiarModoEmisoraActual(usarPropio: usarPropio);
|
||||
|
||||
Future<void> cambiarEcualizadorActivo(bool activo) async {
|
||||
_ecualizadorActivo = activo;
|
||||
await servicioEcualizador.guardarActivo(activo);
|
||||
await audio.setEcualizadorActivo(activo);
|
||||
if (activo) {
|
||||
await audio.aplicarPreset(_presetActual);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
Future<void> cambiarEcualizadorActivo(bool activo) =>
|
||||
ecualizador.cambiarActivo(activo);
|
||||
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
Future<void> cambiarPresetEcualizador(
|
||||
PresetEcualizador preset, {
|
||||
bool guardarPorEmisora = true,
|
||||
}) async {
|
||||
final actual = emisoraActual;
|
||||
final usarPresetPropio =
|
||||
guardarPorEmisora &&
|
||||
actual != null &&
|
||||
_presetsEmisoraMap.containsKey(actual.uuid);
|
||||
}) => ecualizador.cambiarPreset(preset, guardarPorEmisora: guardarPorEmisora);
|
||||
|
||||
if (usarPresetPropio) {
|
||||
await guardarPresetEcualizadorPorEmisora(actual.uuid, preset);
|
||||
return;
|
||||
}
|
||||
await cambiarPresetPrincipalEcualizador(preset);
|
||||
}
|
||||
|
||||
Future<void> cambiarBandaEcualizador(int index, double db) async {
|
||||
final bandas = List<double>.from(_presetActual.bandas);
|
||||
if (index < 0 || index >= bandas.length) return;
|
||||
|
||||
bandas[index] = db;
|
||||
final modificado = PresetEcualizador(
|
||||
nombre: 'Personalizado',
|
||||
bandas: bandas,
|
||||
);
|
||||
await cambiarPresetEcualizador(modificado);
|
||||
}
|
||||
// TODO(S4b): remove getter — consumers migrate to EstadoEcualizador.
|
||||
Future<void> cambiarBandaEcualizador(int index, double db) =>
|
||||
ecualizador.cambiarBanda(index, db);
|
||||
|
||||
// ── Emisoras personalizadas ───────────────────────────────────────────────
|
||||
|
||||
@@ -912,6 +845,7 @@ class EstadoRadio extends ChangeNotifier {
|
||||
static const _keyAlarmasConfig = 'alarmas_musicales_v1';
|
||||
|
||||
/// Genera el JSON de toda la configuración (v2 — portabilidad completa).
|
||||
/// La forma del sobre v2 vive en [ServicioExportImport] (S4-R4).
|
||||
Future<Map<String, dynamic>> exportarConfig() async {
|
||||
final favs = await favoritos.obtenerTodos();
|
||||
final grupos = await favoritos.obtenerGrupos();
|
||||
@@ -925,29 +859,27 @@ class EstadoRadio extends ChangeNotifier {
|
||||
? jsonDecode(alarmasRaw) as Map<String, dynamic>
|
||||
: null;
|
||||
|
||||
return {
|
||||
'version': 2,
|
||||
'exportedAt': DateTime.now().toIso8601String(),
|
||||
// Favoritos + grupos (preserva asignaciones grupo_id en cada emisora)
|
||||
'gruposFavoritos':
|
||||
grupos.where((g) => !g.esSinAsignar).map((g) => g.toMap()).toList(),
|
||||
'favoritos': favs.map((e) => e.toMap()).toList(),
|
||||
// Emisoras personalizadas
|
||||
'emisorasCustom': _emisorasCustom.map((e) => e.toMap()).toList(),
|
||||
// Ecualizador
|
||||
'presetPrincipalEcualizador': _presetPrincipal.toJson(),
|
||||
'presetsEcualizador': _presetsEmisoraMap.map(
|
||||
(uuid, preset) => MapEntry(uuid, preset.toJson()),
|
||||
),
|
||||
// Alarmas completas (alarmas + vacaciones + excepciones)
|
||||
'alarmas': alarmasData,
|
||||
// Preferencias de usuario
|
||||
'emisoraPreferidaUuid': _emisoraPreferidaUuid,
|
||||
'ordenListas': _ordenListas.name,
|
||||
'timerSuenoPresetsSegundos': _timerSuenoPresetsSegundos,
|
||||
};
|
||||
return _exportImport.construirExportacion(
|
||||
gruposFavoritos: grupos,
|
||||
favoritos: favs,
|
||||
emisorasCustom: _emisorasCustom,
|
||||
presetPrincipal: ecualizador.presetPrincipal,
|
||||
presetsPorEmisora: ecualizador.presetsPorEmisora,
|
||||
alarmas: alarmasData,
|
||||
emisoraPreferidaUuid: _emisoraPreferidaUuid,
|
||||
ordenListas: _ordenListas.name,
|
||||
timerSuenoPresetsSegundos: _timerSuenoPresetsSegundos,
|
||||
);
|
||||
}
|
||||
|
||||
/// Exportación lista para compartir como archivo (JSON con indentación).
|
||||
Future<String> exportarConfigJson() async =>
|
||||
_exportImport.exportar(await exportarConfig());
|
||||
|
||||
/// Parsea un backup JSON; null cuando el contenido no es válido (S4-R4).
|
||||
Map<String, dynamic>? parsearConfigJson(String raw) =>
|
||||
_exportImport.importar(raw);
|
||||
|
||||
/// Importa configuración desde un JSON exportado previamente.
|
||||
/// Soporta v1 (sin grupos, sin alarmas) y v2 (portabilidad completa).
|
||||
Future<void> importarConfig(Map<String, dynamic> data) async {
|
||||
@@ -985,40 +917,27 @@ class EstadoRadio extends ChangeNotifier {
|
||||
|
||||
// ── Ecualizador ───────────────────────────────────────────────────────
|
||||
final principalRaw = data['presetPrincipalEcualizador'];
|
||||
if (principalRaw is Map) {
|
||||
_presetPrincipal = PresetEcualizador.desdeJson(
|
||||
Map<String, dynamic>.from(principalRaw),
|
||||
);
|
||||
} else {
|
||||
_presetPrincipal = PresetEcualizador.flat;
|
||||
}
|
||||
final presetPrincipal =
|
||||
principalRaw is Map
|
||||
? PresetEcualizador.desdeJson(
|
||||
Map<String, dynamic>.from(principalRaw),
|
||||
)
|
||||
: PresetEcualizador.flat;
|
||||
|
||||
final presetsRaw = data['presetsEcualizador'] as Map? ?? {};
|
||||
_presetsEmisoraMap
|
||||
..clear()
|
||||
..addAll(
|
||||
presetsRaw.map<String, PresetEcualizador>(
|
||||
(uuid, presetJson) => MapEntry(
|
||||
uuid as String,
|
||||
PresetEcualizador.desdeJson(
|
||||
Map<String, dynamic>.from(presetJson as Map),
|
||||
),
|
||||
),
|
||||
final presetsPorEmisora = presetsRaw.map<String, PresetEcualizador>(
|
||||
(uuid, presetJson) => MapEntry(
|
||||
uuid as String,
|
||||
PresetEcualizador.desdeJson(
|
||||
Map<String, dynamic>.from(presetJson as Map),
|
||||
),
|
||||
);
|
||||
|
||||
await servicioEcualizador.guardarConfiguracion(
|
||||
ConfiguracionEcualizador(
|
||||
principal: _presetPrincipal,
|
||||
porEmisora: _presetsEmisoraMap,
|
||||
activo: _ecualizadorActivo,
|
||||
),
|
||||
);
|
||||
|
||||
final actual = emisoraActual;
|
||||
final presetActivo =
|
||||
actual == null ? _presetPrincipal : _presetParaEmisora(actual.uuid);
|
||||
await _aplicarPresetActivo(presetActivo);
|
||||
await ecualizador.importarConfiguracion(
|
||||
principal: presetPrincipal,
|
||||
porEmisora: presetsPorEmisora,
|
||||
);
|
||||
|
||||
// ── Alarmas (v2) ──────────────────────────────────────────────────────
|
||||
if (version >= 2) {
|
||||
@@ -1122,6 +1041,7 @@ class EstadoRadio extends ChangeNotifier {
|
||||
_suscripcionEstadoAudio?.cancel();
|
||||
_suscripcionGrabacion?.cancel();
|
||||
_errorController.close();
|
||||
ecualizador.dispose();
|
||||
audio.dispose();
|
||||
unawaited(grabacion.dispose());
|
||||
timer.dispose();
|
||||
|
||||
Reference in New Issue
Block a user