diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcUtil.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcUtil.java index 6f20f1eaf4..3c29c85e5c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcUtil.java @@ -35,7 +35,7 @@ public final class WebRtcUtil { public static @NonNull LockManager.PhoneState getInCallPhoneState(@NonNull Context context) { AudioManagerCompat audioManager = ApplicationDependencies.getAndroidCallAudioManager(); - if (audioManager.isSpeakerphoneOn() || audioManager.isBluetoothScoOn() || audioManager.isWiredHeadsetOn()) { + if (audioManager.isSpeakerphoneOn() || audioManager.isBluetoothConnected() || audioManager.isWiredHeadsetOn()) { return LockManager.PhoneState.IN_HANDS_FREE_CALL; } else { return LockManager.PhoneState.IN_CALL; 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 new file mode 100644 index 0000000000..787bf145f2 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioDeviceMapping.kt @@ -0,0 +1,29 @@ +package org.thoughtcrime.securesms.webrtc.audio + +import android.media.AudioDeviceInfo +import androidx.annotation.RequiresApi + +@RequiresApi(31) +object AudioDeviceMapping { + + private val systemDeviceTypeMap: Map> = mapOf( + SignalAudioManager.AudioDevice.BLUETOOTH to listOf(AudioDeviceInfo.TYPE_BLUETOOTH_SCO, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, AudioDeviceInfo.TYPE_BLE_HEADSET), + SignalAudioManager.AudioDevice.EARPIECE to listOf(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE), + SignalAudioManager.AudioDevice.SPEAKER_PHONE to listOf(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE), + SignalAudioManager.AudioDevice.WIRED_HEADSET to listOf(AudioDeviceInfo.TYPE_WIRED_HEADSET, AudioDeviceInfo.TYPE_USB_HEADSET), + SignalAudioManager.AudioDevice.NONE to emptyList() + ) + + @JvmStatic + fun getEquivalentPlatformTypes(audioDevice: SignalAudioManager.AudioDevice): List { + return systemDeviceTypeMap[audioDevice]!! + } + + @JvmStatic + fun fromPlatformType(type: Int): SignalAudioManager.AudioDevice { + for (kind in SignalAudioManager.AudioDevice.values()) { + if (getEquivalentPlatformTypes(kind).contains(type)) return kind + } + return SignalAudioManager.AudioDevice.NONE + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java index 61a6ab11ae..d89def3bbb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java @@ -52,6 +52,15 @@ public abstract class AudioManagerCompat { audioManager.stopBluetoothSco(); } + public boolean isBluetoothConnected() { + if (Build.VERSION.SDK_INT >= 31) { + final SignalAudioManager.AudioDevice audioDevice = AudioDeviceMapping.fromPlatformType(audioManager.getCommunicationDevice().getType()); + return SignalAudioManager.AudioDevice.BLUETOOTH == audioDevice; + } else { + return isBluetoothScoOn(); + } + } + public boolean isBluetoothScoOn() { return audioManager.isBluetoothScoOn(); } 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 064074ad79..65c3489245 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 @@ -18,7 +18,7 @@ import org.thoughtcrime.securesms.recipients.RecipientId class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener?) : SignalAudioManager(context, eventListener) { private val TAG = Log.tag(FullSignalAudioManagerApi31::class.java) - private var defaultDevice = AudioDevice.NONE + private var defaultDevice = AudioDevice.EARPIECE private val deviceCallback = object : AudioDeviceCallback() { override fun onAudioDevicesAdded(addedDevices: Array) { @@ -26,7 +26,7 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener if (state == State.RUNNING) { // Switch to any new audio devices immediately. Log.i(TAG, "onAudioDevicesAdded $addedDevices") - val firstNewlyAddedDevice = addedDevices.firstNotNullOf { fromPlatformType(it.type) } + val firstNewlyAddedDevice = addedDevices.firstNotNullOf { AudioDeviceMapping.fromPlatformType(it.type) } selectAudioDevice(null, firstNewlyAddedDevice) } } @@ -34,33 +34,11 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener override fun onAudioDevicesRemoved(removedDevices: Array) { super.onAudioDevicesRemoved(removedDevices) if (state == State.RUNNING) { - val currentDevice = androidAudioManager.communicationDevice - if (currentDevice != null && removedDevices.map { it.address }.contains(currentDevice.address)) { - selectAudioDevice(null, defaultDevice) - } + audioDeviceChangedCallback() } } } - private val systemDeviceTypeMap: Map> = mapOf( - AudioDevice.BLUETOOTH to listOf(AudioDeviceInfo.TYPE_BLUETOOTH_SCO, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, AudioDeviceInfo.TYPE_BLE_HEADSET), - AudioDevice.EARPIECE to listOf(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE), - AudioDevice.SPEAKER_PHONE to listOf(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE), - AudioDevice.WIRED_HEADSET to listOf(AudioDeviceInfo.TYPE_WIRED_HEADSET, AudioDeviceInfo.TYPE_USB_HEADSET), - AudioDevice.NONE to emptyList() - ) - - private fun getEquivalentPlatformTypes(audioDevice: AudioDevice): List { - return systemDeviceTypeMap[audioDevice]!! - } - - private fun fromPlatformType(type: Int): AudioDevice { - for (kind in AudioDevice.values()) { - if (getEquivalentPlatformTypes(kind).contains(type)) return kind - } - return AudioDevice.NONE - } - override fun setDefaultAudioDevice(recipientId: RecipientId?, newDefaultDevice: AudioDevice, clearUserEarpieceSelection: Boolean) { defaultDevice = newDefaultDevice } @@ -83,9 +61,9 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener handler.postDelayed({ androidAudioManager.requestCallAudioFocus() }, 500) } - if (androidAudioManager.availableCommunicationDevices.any { getEquivalentPlatformTypes(AudioDevice.BLUETOOTH).contains(it.type) }) { + if (androidAudioManager.availableCommunicationDevices.any { AudioDeviceMapping.getEquivalentPlatformTypes(AudioDevice.BLUETOOTH).contains(it.type) }) { selectAudioDevice(null, AudioDevice.BLUETOOTH) - } else if (androidAudioManager.availableCommunicationDevices.any { getEquivalentPlatformTypes(AudioDevice.WIRED_HEADSET).contains(it.type) }) { + } else if (androidAudioManager.availableCommunicationDevices.any { AudioDeviceMapping.getEquivalentPlatformTypes(AudioDevice.WIRED_HEADSET).contains(it.type) }) { selectAudioDevice(null, AudioDevice.WIRED_HEADSET) } @@ -112,11 +90,11 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener val devices: List = androidAudioManager.availableCommunicationDevices try { - val chosenDevice: AudioDeviceInfo = devices.first { getEquivalentPlatformTypes(device).contains(it.type) } + val chosenDevice: AudioDeviceInfo = devices.first { AudioDeviceMapping.getEquivalentPlatformTypes(device).contains(it.type) } val result = androidAudioManager.setCommunicationDevice(chosenDevice) if (result) { Log.i(TAG, "Set active device to ID ${chosenDevice.id}, type ${chosenDevice.type}") - eventListener?.onAudioDeviceChanged(activeDevice = device, devices = devices.map { fromPlatformType(it.type) }.toSet()) + eventListener?.onAudioDeviceChanged(activeDevice = device, devices = devices.map { AudioDeviceMapping.fromPlatformType(it.type) }.toSet()) } else { Log.w(TAG, "Setting device $chosenDevice failed.") } @@ -125,13 +103,18 @@ class FullSignalAudioManagerApi31(context: Context, eventListener: EventListener } } + private fun audioDeviceChangedCallback() { + val activeDevice: AudioDevice = AudioDeviceMapping.fromPlatformType(androidAudioManager.communicationDevice.type) + val devices: Set = androidAudioManager.availableCommunicationDevices.map { AudioDeviceMapping.fromPlatformType(it.type) }.toSet() + eventListener?.onAudioDeviceChanged(activeDevice, devices) + } + override fun startIncomingRinger(ringtoneUri: Uri?, vibrate: Boolean) { Log.i(TAG, "startIncomingRinger(): uri: ${if (ringtoneUri != null) "present" else "null"} vibrate: $vibrate") androidAudioManager.mode = AudioManager.MODE_RINGTONE if (androidAudioManager.isMicrophoneMute) { androidAudioManager.isMicrophoneMute = false } - setDefaultAudioDevice(null, AudioDevice.SPEAKER_PHONE, false) incomingRinger.start(ringtoneUri, vibrate) }