From f9accb8f62da95a7bee78dad21ca51c541ea8c45 Mon Sep 17 00:00:00 2001 From: adamhammer Date: Mon, 28 Mar 2022 12:26:47 -0700 Subject: [PATCH 1/7] Fix audio cutout on bluetooth when rotating Also fixes speaker->bluetooth jumping on rotating --- .../manager/AudioSessionManager.kt | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt index 7ccefc618e..09d1fad494 100644 --- a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt +++ b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt @@ -53,6 +53,7 @@ internal class AudioSessionManager( private var previousAudioDeviceSelectionStatus: AudioDeviceSelectionStatus? = null private var priorToBluetoothAudioSelectionStatus: AudioDeviceSelectionStatus? = null + private var initialized = false private val btAdapter: BluetoothAdapter? get() { val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager @@ -70,9 +71,13 @@ internal class AudioSessionManager( context.registerReceiver(this@AudioSessionManager, filter) - initializeAudioDeviceState() - updateBluetoothStatus() - updateHeadphoneStatus() + // We only want to run this on first-launch + if (!initialized) { + initializeAudioDeviceState() + updateBluetoothStatus() + updateHeadphoneStatus() + } + initialized = true store.getStateFlow().collect { if (previousAudioDeviceSelectionStatus == null || @@ -242,9 +247,11 @@ internal class AudioSessionManager( } private fun enableBluetooth() { - audioManager.startBluetoothSco() - audioManager.isBluetoothScoOn = true - audioManager.isSpeakerphoneOn = false + if (!audioManager.isBluetoothScoOn) { + audioManager.startBluetoothSco() + audioManager.isBluetoothScoOn = true + audioManager.isSpeakerphoneOn = false + } } fun stop() { From 62eb304bef2c88a2ef21a6e29f2060b65bd771ca Mon Sep 17 00:00:00 2001 From: adamhammer Date: Mon, 28 Mar 2022 12:36:01 -0700 Subject: [PATCH 2/7] comments and cleanup --- .../presentation/manager/AudioSessionManager.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt index 09d1fad494..1f29f7f51a 100644 --- a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt +++ b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt @@ -62,22 +62,23 @@ internal class AudioSessionManager( suspend fun start() { + // On first launch we need to init the redux-state, check Bluetooth and Headset status + if (!initialized) { + initializeAudioDeviceState() + updateBluetoothStatus() + updateHeadphoneStatus() + } + initialized = true + + // Listeners we need to rebind with Activity (Bluetooth, Headset, State Updates) btAdapter?.run { getProfileProxy(context, this@AudioSessionManager, BluetoothProfile.HEADSET) } val filter = IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED) filter.addAction(AudioManager.ACTION_HEADSET_PLUG) - context.registerReceiver(this@AudioSessionManager, filter) - // We only want to run this on first-launch - if (!initialized) { - initializeAudioDeviceState() - updateBluetoothStatus() - updateHeadphoneStatus() - } - initialized = true store.getStateFlow().collect { if (previousAudioDeviceSelectionStatus == null || From 42f08569f7d105aeae913c1eff23408a6af3ccbb Mon Sep 17 00:00:00 2001 From: adamhammer Date: Mon, 28 Mar 2022 13:00:10 -0700 Subject: [PATCH 3/7] remove redundant audioManager declaration --- .../communication/ui/presentation/manager/AudioSessionManager.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt index 1f29f7f51a..bb3610a42f 100644 --- a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt +++ b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt @@ -159,7 +159,6 @@ internal class AudioSessionManager( } private fun isHeadsetActive(): Boolean { - val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS) for (deviceInfo in audioDevices) { From 5f556e0f0746e7d632c5abf604f026abb26fc842 Mon Sep 17 00:00:00 2001 From: adamhammer Date: Mon, 28 Mar 2022 13:52:04 -0700 Subject: [PATCH 4/7] removed AudioManager dependency (can fetch from context) --- .../communication/ui/di/DependencyInjectionContainerImpl.kt | 1 - .../ui/presentation/manager/AudioSessionManager.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/di/DependencyInjectionContainerImpl.kt b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/di/DependencyInjectionContainerImpl.kt index 6ca7755534..494a89f05e 100644 --- a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/di/DependencyInjectionContainerImpl.kt +++ b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/di/DependencyInjectionContainerImpl.kt @@ -79,7 +79,6 @@ internal class DependencyInjectionContainerImpl( AudioSessionManager( appStore, applicationContext, - applicationContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager ) } diff --git a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt index bb3610a42f..44f37fd1ea 100644 --- a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt +++ b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt @@ -28,9 +28,9 @@ import com.azure.android.communication.ui.redux.state.PermissionStatus internal class AudioSessionManager( private val store: Store, private val context: Context, - private val audioManager: AudioManager, ) : BluetoothProfile.ServiceListener, BroadcastReceiver() { + private val audioManager by lazy { context.getSystemService(Context.AUDIO_SERVICE) as AudioManager } private var bluetoothAudioProxy: BluetoothHeadset? = null private val isBluetoothScoAvailable From 7ab28c54b1466aa80ca8167f82b3b6cca6a66726 Mon Sep 17 00:00:00 2001 From: adamhammer Date: Mon, 28 Mar 2022 13:53:47 -0700 Subject: [PATCH 5/7] format --- .../communication/ui/di/DependencyInjectionContainerImpl.kt | 1 - .../communication/ui/presentation/manager/AudioSessionManager.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/di/DependencyInjectionContainerImpl.kt b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/di/DependencyInjectionContainerImpl.kt index 494a89f05e..6a02a7d5c1 100644 --- a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/di/DependencyInjectionContainerImpl.kt +++ b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/di/DependencyInjectionContainerImpl.kt @@ -4,7 +4,6 @@ package com.azure.android.communication.ui.di import android.content.Context -import android.media.AudioManager import com.azure.android.communication.ui.configuration.AppLocalizationProvider import com.azure.android.communication.ui.configuration.CallCompositeConfiguration import com.azure.android.communication.ui.configuration.LocalizationProvider diff --git a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt index 44f37fd1ea..1a0dfc149e 100644 --- a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt +++ b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt @@ -79,7 +79,6 @@ internal class AudioSessionManager( filter.addAction(AudioManager.ACTION_HEADSET_PLUG) context.registerReceiver(this@AudioSessionManager, filter) - store.getStateFlow().collect { if (previousAudioDeviceSelectionStatus == null || previousAudioDeviceSelectionStatus != it.localParticipantState.audioState.device From 48bf22afed3f920bdd530a5b21d1b7c2c49ff7e9 Mon Sep 17 00:00:00 2001 From: adamhammer Date: Mon, 28 Mar 2022 15:14:39 -0700 Subject: [PATCH 6/7] clean up logic of updateBluetooth and initDevices --- .../manager/AudioSessionManager.kt | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt index 1a0dfc149e..f290a4d04f 100644 --- a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt +++ b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt @@ -32,6 +32,7 @@ internal class AudioSessionManager( private val audioManager by lazy { context.getSystemService(Context.AUDIO_SERVICE) as AudioManager } private var bluetoothAudioProxy: BluetoothHeadset? = null + private var initialized = false private val isBluetoothScoAvailable get() = try { @@ -53,7 +54,6 @@ internal class AudioSessionManager( private var previousAudioDeviceSelectionStatus: AudioDeviceSelectionStatus? = null private var priorToBluetoothAudioSelectionStatus: AudioDeviceSelectionStatus? = null - private var initialized = false private val btAdapter: BluetoothAdapter? get() { val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager @@ -63,12 +63,7 @@ internal class AudioSessionManager( suspend fun start() { // On first launch we need to init the redux-state, check Bluetooth and Headset status - if (!initialized) { - initializeAudioDeviceState() - updateBluetoothStatus() - updateHeadphoneStatus() - } - initialized = true + initializeAudioDeviceState() // Listeners we need to rebind with Activity (Bluetooth, Headset, State Updates) btAdapter?.run { @@ -116,10 +111,15 @@ internal class AudioSessionManager( // When disconnected revert to "Speaker" // When disconnected (and not selected), just update availability private fun updateBluetoothStatus() { + val audioState = store.getCurrentState().localParticipantState.audioState + + // Bluetooth is no longer available + // Fallback to previous device selection if (!isBluetoothScoAvailable && - store.getCurrentState().localParticipantState.audioState.bluetoothState.available && - store.getCurrentState().localParticipantState.audioState.device == AudioDeviceSelectionStatus.BLUETOOTH_SCO_SELECTED + audioState.bluetoothState.available && + audioState.device == AudioDeviceSelectionStatus.BLUETOOTH_SCO_SELECTED ) { + // Request the Previous Device store.dispatch( LocalParticipantAction.AudioDeviceChangeRequested( when (priorToBluetoothAudioSelectionStatus) { @@ -128,11 +128,11 @@ internal class AudioSessionManager( } ) ) - // If bluetooth dropped, go back to last device } - if (isBluetoothScoAvailable && !store.getCurrentState().localParticipantState.audioState.bluetoothState.available) { - // If Bluetooth has been connected and wasn't, switch to it + // Auto-Connect to Bluetooth if it wasn't available but now is + if (isBluetoothScoAvailable && !audioState.bluetoothState.available) { + store.dispatch( LocalParticipantAction.AudioDeviceBluetoothSCOAvailable( isBluetoothScoAvailable, @@ -146,15 +146,16 @@ internal class AudioSessionManager( AudioDeviceSelectionStatus.BLUETOOTH_SCO_REQUESTED ) ) - } else { - // If bluetooth wasn't selected and is just being disconnected, just do the flag - store.dispatch( - LocalParticipantAction.AudioDeviceBluetoothSCOAvailable( - isBluetoothScoAvailable, - bluetoothDeviceName - ) - ) } + + // Update the Bluetooth Status in the store + store.dispatch( + LocalParticipantAction.AudioDeviceBluetoothSCOAvailable( + isBluetoothScoAvailable, + bluetoothDeviceName + )) + + } private fun isHeadsetActive(): Boolean { @@ -175,6 +176,11 @@ internal class AudioSessionManager( } private fun initializeAudioDeviceState() { + if (initialized) return + initialized = true + + updateHeadphoneStatus() + when { audioManager.isSpeakerphoneOn -> store.dispatch( @@ -195,6 +201,7 @@ internal class AudioSessionManager( ) ) } + } private fun onAudioDeviceStateChange(audioDeviceSelectionStatus: AudioDeviceSelectionStatus) { From ceb4cc87433b62ee1090450aae75b6f01d9f79f7 Mon Sep 17 00:00:00 2001 From: adamhammer Date: Tue, 29 Mar 2022 10:17:53 -0700 Subject: [PATCH 7/7] Ktlintformat --- .../ui/presentation/manager/AudioSessionManager.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt index f290a4d04f..186c502ca0 100644 --- a/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt +++ b/azure-communication-ui/azure-communication-ui/src/main/java/com/azure/android/communication/ui/presentation/manager/AudioSessionManager.kt @@ -153,9 +153,8 @@ internal class AudioSessionManager( LocalParticipantAction.AudioDeviceBluetoothSCOAvailable( isBluetoothScoAvailable, bluetoothDeviceName - )) - - + ) + ) } private fun isHeadsetActive(): Boolean { @@ -201,7 +200,6 @@ internal class AudioSessionManager( ) ) } - } private fun onAudioDeviceStateChange(audioDeviceSelectionStatus: AudioDeviceSelectionStatus) {