audio: Let EC decide alone what communication device to use

This commit is contained in:
Valere 2026-04-17 11:52:07 +02:00
parent a82e3425be
commit 468eb9bfe8

View file

@ -113,23 +113,7 @@ class WebViewAudioManager(
@get:RequiresApi(Build.VERSION_CODES.S) @get:RequiresApi(Build.VERSION_CODES.S)
private val commsDeviceChangedListener by lazy { private val commsDeviceChangedListener by lazy {
AudioManager.OnCommunicationDeviceChangedListener { device -> AudioManager.OnCommunicationDeviceChangedListener { device ->
if (device != null && device.id == expectedNewCommunicationDeviceId) { Timber.d("Audio device changed, type: ${device?.id}")
expectedNewCommunicationDeviceId = null
Timber.d("Audio device changed, type: ${device.type}")
updateSelectedAudioDeviceInWebView(device.id.toString())
} else if (device != null && device.id != expectedNewCommunicationDeviceId) {
// We were expecting a device change but it didn't happen, so we should retry
val expectedDeviceId = expectedNewCommunicationDeviceId
if (expectedDeviceId != null) {
// Remove the expected id so we only retry once
expectedNewCommunicationDeviceId = null
audioManager.selectAudioDevice(expectedDeviceId.toString())
}
} else {
Timber.d("Audio device cleared")
expectedNewCommunicationDeviceId = null
audioManager.selectAudioDevice(null)
}
} }
} }
@ -144,39 +128,20 @@ class WebViewAudioManager(
// We need to calculate the available devices ourselves, since calling `listAudioDevices` will return an outdated list // We need to calculate the available devices ourselves, since calling `listAudioDevices` will return an outdated list
val audioDevices = (listAudioDevices() + validNewDevices).distinctBy { it.id }.sortedWith(audioDeviceComparator) val audioDevices = (listAudioDevices() + validNewDevices).distinctBy { it.id }.sortedWith(audioDeviceComparator)
setAvailableAudioDevices(audioDevices.map(SerializableAudioDevice::fromAudioDeviceInfo)) setAvailableAudioDevices(audioDevices.map(SerializableAudioDevice::fromAudioDeviceInfo))
// This should automatically switch to a new device if it has a higher priority than the current one
selectDefaultAudioDevice(audioDevices)
} }
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>?) { override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>?) {
// Update the available devices // Update the available devices
// Element Call will then decide to switch devices if needed
setAvailableAudioDevices() setAvailableAudioDevices()
// Unless the removed device is the current one, we don't need to do anything else
val removedCurrentDevice = removedDevices.orEmpty().any { it.id == currentDeviceId }
if (!removedCurrentDevice) return
val previousDevice = previousSelectedDevice
if (previousDevice != null) {
previousSelectedDevice = null
// If we have a previous device, we should select it again
audioManager.selectAudioDevice(previousDevice.id.toString())
} else {
// If we don't have a previous device, we should select the default one
selectDefaultAudioDevice()
}
} }
} }
//
/** //
* The currently used audio device id. // /**
*/ // * When a new audio device is selected but not yet set as the communication device by the OS, this id is used to check if the device is the expected one.
private var currentDeviceId: Int? = null // */
// private var expectedNewCommunicationDeviceId: Int? = null
/**
* When a new audio device is selected but not yet set as the communication device by the OS, this id is used to check if the device is the expected one.
*/
private var expectedNewCommunicationDeviceId: Int? = null
/** /**
* Previously selected device, used to restore the selection when the selected device is removed. * Previously selected device, used to restore the selection when the selected device is removed.
@ -330,23 +295,6 @@ class WebViewAudioManager(
}) })
} }
/**
* Selects the default audio device based on the sorted available devices.
*
* @param availableDevices The list of available audio devices to select from. If not provided, it will use the current list of audio devices.
*/
private fun selectDefaultAudioDevice(availableDevices: List<AudioDeviceInfo> = listAudioDevices()) {
val selectedDevice = availableDevices.firstOrNull()
expectedNewCommunicationDeviceId = selectedDevice?.id
audioManager.selectAudioDevice(selectedDevice)
selectedDevice?.let {
updateSelectedAudioDeviceInWebView(it.id.toString())
} ?: run {
Timber.w("Audio: unable to select default audio device")
}
}
/** /**
* Updates the WebView's UI to reflect the selected audio device. * Updates the WebView's UI to reflect the selected audio device.
* *
@ -381,14 +329,14 @@ class WebViewAudioManager(
* *
* @param device The info of the audio device to select, or none to clear the selected device. * @param device The info of the audio device to select, or none to clear the selected device.
*/ */
@Suppress("DEPRECATION")
private fun AudioManager.selectAudioDevice(device: AudioDeviceInfo?) { private fun AudioManager.selectAudioDevice(device: AudioDeviceInfo?) {
currentDeviceId = device?.id
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (device != null) { if (device != null) {
runCatchingExceptions { runCatchingExceptions {
Timber.d("Setting communication device: ${device.id} - ${deviceName(device.type, device.productName.toString())}") Timber.d("Setting communication device: ${device.id} - ${deviceName(device.type, device.productName.toString())}")
setCommunicationDevice(device) if (!setCommunicationDevice(device)) {
Timber.w("Failed to setCommunication device")
}
}.onFailure { }.onFailure {
Timber.e(it, "Could not set communication device.") Timber.e(it, "Could not set communication device.")
} }
@ -410,16 +358,16 @@ class WebViewAudioManager(
return return
} }
setAudioEnabled(true) setAudioEnabled(true)
@Suppress("DEPRECATION")
isSpeakerphoneOn = device.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER isSpeakerphoneOn = device.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
isBluetoothScoOn = device.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO isBluetoothScoOn = device.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO
} else { } else {
@Suppress("DEPRECATION")
isSpeakerphoneOn = false isSpeakerphoneOn = false
isBluetoothScoOn = false isBluetoothScoOn = false
} }
} }
expectedNewCommunicationDeviceId = null
coroutineScope.launch { coroutineScope.launch {
proximitySensorMutex.withLock { proximitySensorMutex.withLock {
@Suppress("WakeLock", "WakeLockTimeout") @Suppress("WakeLock", "WakeLockTimeout")