fix(eq): stop the phone's FM sink from posing as the active output
Regression from the previous commit. Ranking every AudioDeviceInfo type this
build does not name individually ABOVE the built-in speaker was meant to let
a car stereo on LE Audio or an automotive bus win. It also promoted the
internal sinks a phone exposes permanently: on the Xiaomi test device
AudioManager reports TYPE_FM (14) as an output, so getActiveAudioDevice
picked it over the real speaker with nothing connected at all. Confirmed on
device:
audio_devices.onListen -> {id=other:14:4, type=14, name=2412DPC0AG}
It then reached Dart under an `other:14:4` id whose type is neither the base
speaker nor a known one, slipped past the collision guard and had a preset
row persisted for it -- reinstating the exact symptom this series set out to
kill: a permanent green active-output dot on a device that was not connected.
Replace the deny-by-omission ranking with an explicit allow list of outputs a
user actually connects. The built-in speaker sits below all of them and above
everything else, so any sink that physically exists but is never where media
plays (TYPE_FM, TYPE_BUILTIN_SPEAKER_SAFE, telephony, remote submix) can no
longer be selected. A one-time purge clears the `other:` rows the bad build
persisted; genuine ones re-register on their next connection.
Fix the USB type constant while here: TYPE_USB_HEADSET is 22, not 14, and 14
is TYPE_FM. The Kotlin USB branch hardcoded 14 and the Dart type table
mirrored the same mistake, so the two cancelled out for real USB headsets
while making a phone's own FM sink decode as USB audio. Both now use 22.
Verified with javap against android.jar (android-36) rather than trusting the
comment that introduced the error.
This commit is contained in:
@@ -4,7 +4,8 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../modelos/preset_ecualizador.dart';
|
||||
import 'persistencia_tolerante.dart';
|
||||
import 'servicio_dispositivo_audio.dart' show idAltavozInterno;
|
||||
import 'servicio_dispositivo_audio.dart'
|
||||
show idAltavozInterno, prefijoDispositivoOtro;
|
||||
|
||||
class ConfiguracionEcualizador {
|
||||
const ConfiguracionEcualizador({
|
||||
@@ -106,9 +107,64 @@ class ServicioEcualizador {
|
||||
final prefs = await _resolverPrefs();
|
||||
if (prefs.getBool(_keyColisionBasePurgaHecha) ?? false) return;
|
||||
await _purgarDeviceId(prefs, idAltavozInterno);
|
||||
await _purgarPrefijoDispositivo(prefs, prefijoDispositivoOtro);
|
||||
await prefs.setBool(_keyColisionBasePurgaHecha, true);
|
||||
}
|
||||
|
||||
/// Removes every device-keyed entry whose id starts with [prefijo].
|
||||
///
|
||||
/// Used for the `other:` namespace: a build that ranked every unlisted
|
||||
/// `AudioDeviceInfo` type above the phone speaker selected permanent internal
|
||||
/// sinks (TYPE_FM on Xiaomi, TYPE_BUILTIN_SPEAKER_SAFE elsewhere) as the
|
||||
/// active output and persisted rows for them. Genuine external outputs in
|
||||
/// this namespace are re-registered on their next connection, so clearing the
|
||||
/// whole prefix costs nothing and needs no per-type knowledge here.
|
||||
Future<void> _purgarPrefijoDispositivo(
|
||||
SharedPreferences prefs,
|
||||
String prefijo,
|
||||
) async {
|
||||
final presetsPorDispositivo = _leerMapa(prefs, _keyPresetsPorDispositivo);
|
||||
final dispositivos = presetsPorDispositivo.keys
|
||||
.where((clave) => clave.startsWith(prefijo))
|
||||
.toList();
|
||||
|
||||
final nombres = _leerMapaStrings(prefs, _keyNombresDispositivos);
|
||||
final nombresAPurgar = nombres.keys
|
||||
.where((clave) => clave.startsWith(prefijo))
|
||||
.toList();
|
||||
|
||||
final matriz = _leerMapa(prefs, _keyPresetsMatriz);
|
||||
final matrizAPurgar = matriz.keys.where((clave) {
|
||||
final separador = clave.indexOf(':');
|
||||
if (separador == -1) return false;
|
||||
return clave.substring(separador + 1).startsWith(prefijo);
|
||||
}).toList();
|
||||
|
||||
for (final clave in dispositivos) {
|
||||
presetsPorDispositivo.remove(clave);
|
||||
}
|
||||
for (final clave in nombresAPurgar) {
|
||||
nombres.remove(clave);
|
||||
}
|
||||
for (final clave in matrizAPurgar) {
|
||||
matriz.remove(clave);
|
||||
}
|
||||
|
||||
if (dispositivos.isNotEmpty) {
|
||||
await _guardarMapa(
|
||||
prefs,
|
||||
_keyPresetsPorDispositivo,
|
||||
presetsPorDispositivo,
|
||||
);
|
||||
}
|
||||
if (nombresAPurgar.isNotEmpty) {
|
||||
await _guardarMapaStrings(prefs, _keyNombresDispositivos, nombres);
|
||||
}
|
||||
if (matrizAPurgar.isNotEmpty) {
|
||||
await _guardarMapa(prefs, _keyPresetsMatriz, matriz);
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes every trace of [deviceId] from the three device-keyed SP maps.
|
||||
///
|
||||
/// Shared by the guarded migrations and by [eliminarDispositivo]; each map is
|
||||
|
||||
Reference in New Issue
Block a user