diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcAudioOutputToggleButton.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcAudioOutputToggleButton.kt index 5d88e8f97d..83d69504ed 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcAudioOutputToggleButton.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcAudioOutputToggleButton.kt @@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.webrtc.audio.AudioDeviceMapping import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager +import kotlin.math.min /** * A UI button that triggers a picker dialog/bottom sheet allowing the user to select the audio output for the ongoing call. @@ -218,6 +219,12 @@ class WebRtcAudioOutputToggleButton @JvmOverloads constructor(context: Context, inner class OutputState { private val availableOutputs: LinkedHashSet = linkedSetOf(WebRtcAudioOutput.SPEAKER) private var selectedDevice = 0 + set(value) { + if (value >= availableOutputs.size) { + throw IndexOutOfBoundsException("Index: $value, size: ${availableOutputs.size}") + } + field = value + } @Deprecated("Used only for onSaveInstanceState.") fun getBackingIndexForBackup(): Int { @@ -259,6 +266,7 @@ class WebRtcAudioOutputToggleButton @JvmOverloads constructor(context: Context, availableOutputs.add(WebRtcAudioOutput.HANDSET) } else { availableOutputs.remove(WebRtcAudioOutput.HANDSET) + selectedDevice = min(selectedDevice, availableOutputs.size - 1) } } @@ -269,6 +277,7 @@ class WebRtcAudioOutputToggleButton @JvmOverloads constructor(context: Context, availableOutputs.add(WebRtcAudioOutput.BLUETOOTH_HEADSET) } else { availableOutputs.remove(WebRtcAudioOutput.BLUETOOTH_HEADSET) + selectedDevice = min(selectedDevice, availableOutputs.size - 1) } } var isWiredHeadsetAvailable: Boolean @@ -278,6 +287,7 @@ class WebRtcAudioOutputToggleButton @JvmOverloads constructor(context: Context, availableOutputs.add(WebRtcAudioOutput.WIRED_HEADSET) } else { availableOutputs.remove(WebRtcAudioOutput.WIRED_HEADSET) + selectedDevice = min(selectedDevice, availableOutputs.size - 1) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java index 05df0fe2b9..4c0444bf62 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java @@ -669,8 +669,8 @@ public class WebRtcCallView extends ConstraintLayout { if (webRtcControls.displayAudioToggle()) { visibleViewSet.add(audioToggle); - audioToggle.setControlAvailability(webRtcControls.enableEarpieceInAudioToggle(), - webRtcControls.enableBluetoothHeadsetInAudioToggle()); + audioToggle.setControlAvailability(webRtcControls.isEarpieceAvailableForAudioToggle(), + webRtcControls.isBluetoothHeadsetAvailableForAudioToggle()); audioToggle.setAudioOutput(webRtcControls.getAudioOutput(), false); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java index c4d8b880d1..b2b51c2eb3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java @@ -153,7 +153,7 @@ public final class WebRtcControls { } boolean displayAudioToggle() { - return (isPreJoin() || isAtLeastOutgoing()) && (!isLocalVideoEnabled || enableBluetoothHeadsetInAudioToggle()); + return (isPreJoin() || isAtLeastOutgoing()) && (!isLocalVideoEnabled || isBluetoothHeadsetAvailableForAudioToggle()); } boolean displayCameraToggle() { @@ -172,11 +172,11 @@ public final class WebRtcControls { return isIncoming(); } - boolean enableEarpieceInAudioToggle() { + boolean isEarpieceAvailableForAudioToggle() { return !isLocalVideoEnabled; } - boolean enableBluetoothHeadsetInAudioToggle() { + boolean isBluetoothHeadsetAvailableForAudioToggle() { return availableDevices.contains(SignalAudioManager.AudioDevice.BLUETOOTH); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioDeviceMapping.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioDeviceMapping.kt index bb09e99f75..b8f36e96b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioDeviceMapping.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioDeviceMapping.kt @@ -6,8 +6,6 @@ import androidx.annotation.RequiresApi @RequiresApi(31) object AudioDeviceMapping { - val orderOfPreference: List = listOf(SignalAudioManager.AudioDevice.BLUETOOTH, SignalAudioManager.AudioDevice.WIRED_HEADSET, SignalAudioManager.AudioDevice.EARPIECE, SignalAudioManager.AudioDevice.SPEAKER_PHONE, SignalAudioManager.AudioDevice.NONE) - private val systemDeviceTypeMap: Map> = mapOf( SignalAudioManager.AudioDevice.BLUETOOTH to listOf(AudioDeviceInfo.TYPE_BLUETOOTH_SCO, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, AudioDeviceInfo.TYPE_BLE_HEADSET, AudioDeviceInfo.TYPE_HEARING_AID), SignalAudioManager.AudioDevice.EARPIECE to listOf(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE), diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/FullSignalAudioManagerApi31.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/FullSignalAudioManagerApi31.kt index 652918e288..4de106e3c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/FullSignalAudioManagerApi31.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/FullSignalAudioManagerApi31.kt @@ -173,7 +173,7 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener val excludedDevices = emptyList() // TODO: pull this from somewhere. Preferences? val autoSelectableDevices = availableCommunicationDevices.filterNot { excludedDevices.contains(it.address) } var candidate: AudioDeviceInfo? = null - val searchOrder: List = listOf(defaultAudioDevice) + AudioDeviceMapping.orderOfPreference.filterNot { it == defaultAudioDevice } + val searchOrder: List = listOf(AudioDevice.BLUETOOTH, defaultAudioDevice, AudioDevice.WIRED_HEADSET, AudioDevice.EARPIECE, AudioDevice.SPEAKER_PHONE, AudioDevice.NONE).distinct() for (deviceType in searchOrder) { candidate = autoSelectableDevices.find { AudioDeviceMapping.fromPlatformType(it.type) == deviceType } if (candidate != null) {