feat(audio): audio session integration and runtime robustness
- Integrate audio_session (new servicio_audio_session.dart): incoming calls pause the radio and resume on end, headphone unplug pauses without auto-resume, permanent focus loss never auto-resumes, duck lowers volume - Add play-intent flag to ServicioAudio so interruption handling and future reconnect logic can distinguish user pause from system-driven stops - Eliminate read-modify-write race in ServicioAlarmas with an in-memory cache and single-writer queue across all mutations; recalcularTodas persists only when state actually changed - Convert ServicioAlarmasAndroid static StreamController/handler to injectable instance fields, restoring test isolation - Inject a single cached SharedPreferences from main.dart across services and state (removes 23 inline getInstance() calls) - Move configurarLocalizaciones out of MiniReproductor.build() (was running on every rebuild during playback) - Bound the alarm fire-dedup set (cap 200 entries, 24h pruning) - 12 new tests (89 total green), flutter analyze clean
This commit is contained in:
@@ -126,6 +126,10 @@ class EjecucionAlarmaNativa {
|
||||
abstract class PuertoAlarmasAndroid {
|
||||
Stream<EventoAlarmaAndroid> get eventosAlarma;
|
||||
|
||||
/// Provides the UI localizations used to localize the alarm/station names
|
||||
/// sent to the native scheduler.
|
||||
void configurarLocalizaciones(AppLocalizations l10n);
|
||||
|
||||
Future<void> programar(AlarmaMusical alarma);
|
||||
Future<void> cancelar(String alarmaId);
|
||||
Future<void> ocultarNotificacionAlarma(String alarmaId);
|
||||
@@ -145,22 +149,24 @@ class ServicioAlarmasAndroid implements PuertoAlarmasAndroid {
|
||||
ServicioAlarmasAndroid({
|
||||
MethodChannel channel = const MethodChannel('pluriwave/alarm_scheduler'),
|
||||
}) : _channel = channel {
|
||||
_instalarHandler(_channel);
|
||||
_instalarHandler();
|
||||
}
|
||||
|
||||
final MethodChannel _channel;
|
||||
static final _eventosController =
|
||||
StreamController<EventoAlarmaAndroid>.broadcast();
|
||||
static bool _handlerInstalado = false;
|
||||
static AppLocalizations? _l10n;
|
||||
|
||||
static AppLocalizations get _textos {
|
||||
// Instance state (S3-R2): each bridge owns its controller and l10n so
|
||||
// independent instances never share events through globals.
|
||||
final _eventosController = StreamController<EventoAlarmaAndroid>.broadcast();
|
||||
AppLocalizations? _l10n;
|
||||
|
||||
AppLocalizations get _textos {
|
||||
final actual = _l10n;
|
||||
if (actual != null) return actual;
|
||||
return lookupAppLocalizations(const Locale('es'));
|
||||
}
|
||||
|
||||
static void configurarLocalizaciones(AppLocalizations l10n) {
|
||||
@override
|
||||
void configurarLocalizaciones(AppLocalizations l10n) {
|
||||
_l10n = l10n;
|
||||
}
|
||||
|
||||
@@ -334,10 +340,11 @@ class ServicioAlarmasAndroid implements PuertoAlarmasAndroid {
|
||||
return _channel.invokeMethod<void>(method, args);
|
||||
}
|
||||
|
||||
static void _instalarHandler(MethodChannel channel) {
|
||||
if (_handlerInstalado) return;
|
||||
_handlerInstalado = true;
|
||||
channel.setMethodCallHandler((call) async {
|
||||
// Installed once per instance from the constructor. Creating a second
|
||||
// instance over the SAME channel re-binds the platform handler to the
|
||||
// newest instance (production has exactly one instance per channel).
|
||||
void _instalarHandler() {
|
||||
_channel.setMethodCallHandler((call) async {
|
||||
if (call.method != 'alarmFired') return;
|
||||
final args = call.arguments;
|
||||
if (args is Map) {
|
||||
|
||||
Reference in New Issue
Block a user