ExoPlayer assigns a new audio session id after transient audio-focus interruptions (navigation prompts, radar warnings), leaving the AndroidEqualizer attached to the dead session so playback resumed without equalization until the next station switch. The session-id listener now detects genuine rotations through a dedicated guard and re-activates the equalizer with the current preset, gated on EQ availability to stay clear of player teardown/rebuild.
3.9 KiB
Exploration: eq-apply-lifecycle (split into two changes)
Shared exploration for eq-audiofocus-reapply (Part B) and eq-device-disconnect-revert (Part A).
Current State
The equalizer is just_audio's AndroidEqualizer audio-pipeline effect (not a raw platform-channel android.media.audiofx.Equalizer), attached at AudioPlayer construction in lib/servicios/servicio_audio.dart. Device detection (pluriwave/audio_devices, MainActivity.kt) emits BOTH onAudioDevicesAdded and onAudioDevicesRemoved correctly — the native side is not the problem for Part A.
Part B — EQ lost after audio-focus ducking (root cause CONFIRMED)
AndroidEqualizer._activate(platform) (just_audio 0.9.46, just_audio.dart:3996) re-attaches the effect to the native session, and it fires only from the platform-connect loop — which in this app only runs from _cambiarFuente() (explicit station switch, servicio_audio.dart:449). The native Android session id rotates independently on audio-focus transitions (fed from native PlaybackEvents). Documented upstream ExoPlayer behavior (issue #5302): "a new audio session ID... attached equalizer and other audio effects stop working," workaround = re-initialize the equalizer every time the session id changes.
servicio_audio.dart already exposes androidAudioSessionIdStream (L197) but nothing listens to it for EQ purposes — only lib/widgets/visualizador_audio.dart:82-88 consumes it, and it correctly re-syncs on every session-id change. That is the reference pattern. servicio_audio_session.dart (duck/pause/resume) never touches the EQ.
Fix shape: subscribe to androidAudioSessionIdStream, guard on changed+non-null (like the visualizer), call the existing idempotent choke-points _activarEcualizador() (L526) / aplicarPreset() (L540).
Part A — device disconnect/cold-start revert (gap, not regression)
_onDispositivoCambiado() (estado_ecualizador.dart:210-227) is a single handler for every device event, connect or disconnect alike. There is zero "previous config" tracking anywhere. On disconnect it re-resolves the 4-level hierarchy for whatever device is now active (usually builtin_speaker), which coincidentally looks like revert-to-base only when no station/matrix override interferes. Zero disconnect test coverage in test/estado/estado_ecualizador_test.dart. The archived eq-device-autoswitch-ux change never touched disconnect.
Revert semantics — DECISION (orchestrator)
Deterministic re-resolution of the existing 4-level hierarchy for the now-active device — NOT a literal snapshot/restore. Justification: the user's second scenario ("app opens with no device connected") has no snapshot to restore, so re-resolution is the only mechanism that covers both scenarios; it is also stateless and self-healing. What's missing is making the disconnect path explicit, correct, and tested (including cold-start resolution).
Affected Areas
lib/estado/estado_ecualizador.dart—_onDispositivoCambiado(), no disconnect/revert logic (Part A)lib/servicios/servicio_audio.dart—_androidAudioSessionIdSub(L258-265) stores the id but never re-applies EQ;_activarEcualizador()/aplicarPreset()are the reuse points (Part B)lib/widgets/visualizador_audio.dart:82-88— reference pattern for session-id reactiontest/estado/estado_ecualizador_test.dart— no disconnect coveragetest/servicios/servicio_audio_session_test.dart,servicio_audio_source_switch_test.dart— no EQ-reapply coverage
Risks
- Part B: guard against re-firing on every session-id emission (including legitimate first activation); avoid racing
_recrearPlayer()teardown/rebuild - Part B must apply regardless of
_eqMultiDeviceEnabled— ducking bug is orthogonal to the multi-device toggle - Both: must not regress the passing connect/4-level-resolution suite or manual slider path (
cambiarBanda/setBanda)