From d40878e3be3e49ce7164b164b9d132d3f3d0dbcc Mon Sep 17 00:00:00 2001 From: gavine99 Date: Fri, 18 Apr 2025 12:04:58 +1000 Subject: [PATCH] changes to do audio channel switcheroo to bluetooth only after sco conn is available Signed-off-by: gavine99 --- .../nextcloud/talk/activities/CallActivity.kt | 2 +- .../talk/webrtc/WebRtcAudioManager.java | 84 +++++++++++-------- 2 files changed, 50 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt index 8681d6333..8b7460725 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt @@ -836,7 +836,7 @@ class CallActivity : CallBaseActivity() { fun setDefaultAudioOutputChannel(selectedAudioDevice: AudioDevice?) { if (audioManager != null) { - audioManager!!.selectDefaultAudioDevice(selectedAudioDevice) + audioManager!!.setDefaultAudioDevice(selectedAudioDevice) updateAudioOutputButton(audioManager!!.currentAudioDevice) } } diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebRtcAudioManager.java b/app/src/main/java/com/nextcloud/talk/webrtc/WebRtcAudioManager.java index a77aadfd7..4ea8f3b91 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebRtcAudioManager.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebRtcAudioManager.java @@ -61,6 +61,8 @@ public class WebRtcAudioManager { private Set audioDevices = new HashSet<>(); + private Set internalAudioDevices = new HashSet<>(); + private final BroadcastReceiver wiredHeadsetReceiver; private AudioManager.OnAudioFocusChangeListener audioFocusChangeListener; @@ -218,6 +220,7 @@ public class WebRtcAudioManager { currentAudioDevice = AudioDevice.NONE; defaultAudioDevice = AudioDevice.NONE; audioDevices.clear(); + internalAudioDevices.clear(); startBluetoothManager(); @@ -297,9 +300,9 @@ public class WebRtcAudioManager { } /** - * Changes selection of the currently active audio device. + * Sets the default audio device to use if selection algo has no other option */ - public void selectDefaultAudioDevice(AudioDevice device) { + public void setDefaultAudioDevice(AudioDevice device) { ThreadUtils.checkIsOnMainThread(); if (!audioDevices.contains(device)) { Log.e(TAG, "Can not select default " + device + " from available " + audioDevices); @@ -308,6 +311,9 @@ public class WebRtcAudioManager { updateAudioDeviceState(); } + /** + * Changes selection of the currently active audio device. + */ public void selectAudioDevice(AudioDevice device) { ThreadUtils.checkIsOnMainThread(); if (!audioDevices.contains(device)) { @@ -403,7 +409,9 @@ public class WebRtcAudioManager { + "wired headset=" + hasWiredHeadset + ", " + "BT state=" + bluetoothManager.getState()); Log.d(TAG, "Device status: " - + "available=" + audioDevices + ", " + + "internally available=" + internalAudioDevices + ", " + + "externally available=" + audioDevices + ", " + + "default=" + defaultAudioDevice + ", " + "current=" + currentAudioDevice + ", " + "user selected=" + userSelectedAudioDevice); @@ -413,28 +421,28 @@ public class WebRtcAudioManager { bluetoothManager.updateDevice(); } - Set newAudioDevices = new HashSet<>(); + Set newInternalAudioDevices = new HashSet<>(); if (bluetoothManager.getState() == WebRtcBluetoothManager.State.SCO_CONNECTED || bluetoothManager.getState() == WebRtcBluetoothManager.State.SCO_CONNECTING || bluetoothManager.getState() == WebRtcBluetoothManager.State.HEADSET_AVAILABLE) { - newAudioDevices.add(AudioDevice.BLUETOOTH); + newInternalAudioDevices.add(AudioDevice.BLUETOOTH); + } + + if (bluetoothManager.getState() == WebRtcBluetoothManager.State.SCO_CONNECTED) { + newInternalAudioDevices.add(AudioDevice.BLUETOOTH_SCO); } if (hasWiredHeadset) { // If a wired headset is connected, then it is the only possible option. - newAudioDevices.add(AudioDevice.WIRED_HEADSET); + newInternalAudioDevices.add(AudioDevice.WIRED_HEADSET); } else { - newAudioDevices.add(AudioDevice.SPEAKER_PHONE); + newInternalAudioDevices.add(AudioDevice.SPEAKER_PHONE); if (hasEarpiece()) { - newAudioDevices.add(AudioDevice.EARPIECE); + newInternalAudioDevices.add(AudioDevice.EARPIECE); } } - boolean audioDeviceSetUpdated = !audioDevices.equals(newAudioDevices); - audioDevices = newAudioDevices; - - // Correct user selected audio devices if needed. if (userSelectedAudioDevice == AudioDevice.BLUETOOTH && bluetoothManager.getState() == WebRtcBluetoothManager.State.HEADSET_UNAVAILABLE) { @@ -450,14 +458,14 @@ public class WebRtcAudioManager { // Need to start Bluetooth if it is available and user either selected it explicitly or // user did not select any output device. - boolean needBluetoothAudioStart = + boolean needBluetoothScoStart = bluetoothManager.getState() == WebRtcBluetoothManager.State.HEADSET_AVAILABLE && (userSelectedAudioDevice == AudioDevice.NONE || userSelectedAudioDevice == AudioDevice.BLUETOOTH); // Need to stop Bluetooth audio if user selected different device and // Bluetooth SCO connection is established or in the process. - boolean needBluetoothAudioStop = + boolean needBluetoothScoStop = (bluetoothManager.getState() == WebRtcBluetoothManager.State.SCO_CONNECTED || bluetoothManager.getState() == WebRtcBluetoothManager.State.SCO_CONNECTING) && (userSelectedAudioDevice != AudioDevice.NONE @@ -466,44 +474,48 @@ public class WebRtcAudioManager { if (bluetoothManager.getState() == WebRtcBluetoothManager.State.HEADSET_AVAILABLE || bluetoothManager.getState() == WebRtcBluetoothManager.State.SCO_CONNECTING || bluetoothManager.getState() == WebRtcBluetoothManager.State.SCO_CONNECTED) { - Log.d(TAG, "Need BT audio: start=" + needBluetoothAudioStart + ", " - + "stop=" + needBluetoothAudioStop + ", " + Log.d(TAG, "Need BT audio: start=" + needBluetoothScoStart + ", " + + "stop=" + needBluetoothScoStop + ", " + "BT state=" + bluetoothManager.getState()); } // Start or stop Bluetooth SCO connection given states set earlier. - if (needBluetoothAudioStop) { + if (needBluetoothScoStop) { bluetoothManager.stopScoAudio(); bluetoothManager.updateDevice(); + } else if (needBluetoothScoStart && !bluetoothManager.startScoAudio()) { + // Remove BLUETOOTH and BLUETOOTH_SCO from list of available devices since SCO start has + // reported no longer available or too many failed attempts. + newInternalAudioDevices.remove(AudioDevice.BLUETOOTH); + newInternalAudioDevices.remove(AudioDevice.BLUETOOTH_SCO); } - // Attempt to start Bluetooth SCO audio (takes a few second to start). - if (needBluetoothAudioStart && - !needBluetoothAudioStop && - !bluetoothManager.startScoAudio()) { - // Remove BLUETOOTH from list of available devices since SCO failed. - audioDevices.remove(AudioDevice.BLUETOOTH); - audioDeviceSetUpdated = true; - } + boolean audioDeviceSetUpdated = !internalAudioDevices.equals(newInternalAudioDevices); + internalAudioDevices = newInternalAudioDevices; + // BLUETOOTH_SCO isn't allowed to be in the externally accessible list of devices + audioDevices = new HashSet<>(internalAudioDevices); + audioDevices.remove(AudioDevice.BLUETOOTH_SCO); // Update selected audio device. AudioDevice newCurrentAudioDevice; - if (bluetoothManager.getState() == WebRtcBluetoothManager.State.SCO_CONNECTED) { - // If a Bluetooth is connected, then it should be used as output audio - // device. Note that it is not sufficient that a headset is available; - // an active SCO channel must also be up and running. + if ((bluetoothManager.getState() == WebRtcBluetoothManager.State.SCO_CONNECTED) + && newInternalAudioDevices.contains(AudioDevice.BLUETOOTH_SCO)) + { + // If Bluetooth SCO is connected and available to use, then it has been selected by user or + // auto-selected and it should be used as output audio device. newCurrentAudioDevice = AudioDevice.BLUETOOTH; } else if (hasWiredHeadset) { - // If a wired headset is connected, but Bluetooth is not, then wired headset is used as + // If a wired headset is connected, but Bluetooth SCO is not, then wired headset is used as // audio device. newCurrentAudioDevice = AudioDevice.WIRED_HEADSET; } else { - // No wired headset and no Bluetooth, hence the audio-device list can contain speaker + // No wired headset and no Bluetooth SCO, hence the audio-device list can contain speaker // phone (on a tablet), or speaker phone and earpiece (on mobile phone). - // |defaultAudioDevice| contains either AudioDevice.SPEAKER_PHONE or AudioDevice.EARPIECE - // depending on the user's selection. + // |userSelectedAudioDevice| may contain either AudioDevice.SPEAKER_PHONE or AudioDevice.EARPIECE + // depending on the user's selection. |defaultAudioDevice|, which is set in code depending on + // call is audio only or video, to be used if user hasn't made an explicit selection if ((userSelectedAudioDevice == AudioDevice.NONE) && (defaultAudioDevice != AudioDevice.NONE)) newCurrentAudioDevice = defaultAudioDevice; else @@ -514,7 +526,8 @@ public class WebRtcAudioManager { // Do the required device switch. setAudioDeviceInternal(newCurrentAudioDevice); Log.d(TAG, "New device status: " - + "available=" + audioDevices + ", " + + "internally available=" + internalAudioDevices + ", " + + "externally available=" + audioDevices + ", " + "current(new)=" + newCurrentAudioDevice); if (audioManagerListener != null) { // Notify a listening client that audio device has been changed. @@ -528,7 +541,8 @@ public class WebRtcAudioManager { * AudioDevice is the names of possible audio devices that we currently support. */ public enum AudioDevice { - SPEAKER_PHONE, WIRED_HEADSET, EARPIECE, BLUETOOTH, NONE + SPEAKER_PHONE, WIRED_HEADSET, EARPIECE, BLUETOOTH, NONE, + BLUETOOTH_SCO // BLUETOOTH_SCO is only valid internal to this class } /**