From d43c7b3bf4b742aa54eb064697364853ca740b68 Mon Sep 17 00:00:00 2001 From: ocarevs Date: Wed, 24 Apr 2024 11:27:46 +0300 Subject: [PATCH] VBLOCKS-2801 bluetooth listener (#159) * BluetoothHeadsetConnectionListener can now be added to monitor bluetooth state * Update CHANGELOG.md and README.md * Update tests * Spotless check * Listen to Bluetooth sco state changes and add onBluetoothScoStateChanged * Update tests --- CHANGELOG.md | 6 +++ README.md | 1 + .../AudioSwitchIntegrationTest.kt | 4 +- .../AutomaticDeviceSelectionTest.kt | 29 ++++++++++ .../java/com.twilio.audioswitch/TestUtil.kt | 3 ++ .../com/twilio/audioswitch/AudioSwitch.kt | 17 +++++- .../BluetoothHeadsetConnectionListener.kt | 24 ++++++++- .../bluetooth/BluetoothHeadsetManager.kt | 54 +++++++++++++++++-- .../audioswitch/AudioSwitchJavaTest.java | 2 + .../com/twilio/audioswitch/AudioSwitchTest.kt | 10 ++++ .../java/com/twilio/audioswitch/BaseTest.kt | 3 ++ .../java/com/twilio/audioswitch/TestUtil.kt | 2 +- .../bluetooth/BluetoothHeadsetManagerTest.kt | 8 +-- 13 files changed, 148 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 361e208c..b2721b22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ Enhancements - Updated gradle plugin to 8.3.1 +### 1.1.10 (March 21, 2024) + +Enhancements + +- BluetoothHeadsetConnectionListener now can be added to AudioSwitch to notify when bluetooth device has connected or failed to connect. + ### 1.1.9 (July 13, 2023) Enhancements diff --git a/README.md b/README.md index cc7d8ae0..58c0c0b3 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,7 @@ Multiple connected bluetooth headsets are supported. - The library will accurately display the up to date active bluetooth headset within the `AudioSwitch` `availableAudioDevices` and `selectedAudioDevice` functions. - Other connected headsets are not stored by the library at this moment. - In the event of a failure to connecting audio to a bluetooth headset, the library will revert the selected audio device (this is usually the Earpiece on a phone). + - Additionally [BluetoothHeadsetConnectionListener](audioswitch/src/main/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetConnectionListener.kt) can be provided to AudioSwitch constructor to monitor state changes and connection failures. - If a user would like to switch between multiple Bluetooth headsets, then they need to switch the active bluetooth headset from the system Bluetooth settings. - The newly activated headset will be propagated to the `AudioSwitch` `availableAudioDevices` and `selectedAudioDevice` functions. diff --git a/audioswitch/src/androidTest/java/com.twilio.audioswitch/AudioSwitchIntegrationTest.kt b/audioswitch/src/androidTest/java/com.twilio.audioswitch/AudioSwitchIntegrationTest.kt index 410382d8..ecc95f7b 100644 --- a/audioswitch/src/androidTest/java/com.twilio.audioswitch/AudioSwitchIntegrationTest.kt +++ b/audioswitch/src/androidTest/java/com.twilio.audioswitch/AudioSwitchIntegrationTest.kt @@ -87,7 +87,7 @@ class AudioSwitchIntegrationTest : AndroidTestBase() { } } InstrumentationRegistry.getInstrumentation().runOnMainSync { - val audioSwitch = AudioSwitch(getTargetContext(), true, audioFocusChangeListener) + val audioSwitch = AudioSwitch(getTargetContext(), null, true, audioFocusChangeListener) audioSwitch.start { _, _ -> } audioSwitch.activate() } @@ -117,7 +117,7 @@ class AudioSwitchIntegrationTest : AndroidTestBase() { val audioFocusUtil = AudioFocusUtil(audioManager, audioFocusChangeListener) audioFocusUtil.requestFocus() - val audioSwitch = AudioSwitch(getTargetContext(), true) + val audioSwitch = AudioSwitch(getTargetContext(), null, true) InstrumentationRegistry.getInstrumentation().runOnMainSync { audioSwitch.start { _, _ -> } audioSwitch.activate() diff --git a/audioswitch/src/androidTest/java/com.twilio.audioswitch/AutomaticDeviceSelectionTest.kt b/audioswitch/src/androidTest/java/com.twilio.audioswitch/AutomaticDeviceSelectionTest.kt index 8dfbaa8f..cd786275 100644 --- a/audioswitch/src/androidTest/java/com.twilio.audioswitch/AutomaticDeviceSelectionTest.kt +++ b/audioswitch/src/androidTest/java/com.twilio.audioswitch/AutomaticDeviceSelectionTest.kt @@ -6,10 +6,14 @@ import androidx.test.filters.LargeTest import com.twilio.audioswitch.AudioDevice.Earpiece import com.twilio.audioswitch.AudioDevice.Speakerphone import com.twilio.audioswitch.AudioDevice.WiredHeadset +import com.twilio.audioswitch.bluetooth.BluetoothHeadsetConnectionListener +import junit.framework.TestCase.assertTrue import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.MatcherAssert.assertThat import org.junit.Test import org.junit.runner.RunWith +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit @RunWith(AndroidJUnit4::class) @LargeTest @@ -29,6 +33,31 @@ class AutomaticDeviceSelectionTest : AndroidTestBase() { audioSwitch.stop() } + @UiThreadTest + @Test + fun `it_should_notify_callback_when_bluetooth_connects`() { + val context = getInstrumentationContext() + val bluetoothConnectedLatch = CountDownLatch(1) + val bluetoothListener = object : BluetoothHeadsetConnectionListener { + override fun onBluetoothHeadsetStateChanged(headsetName: String?, state: Int) { + bluetoothConnectedLatch.countDown() + } + + override fun onBluetoothScoStateChanged(state: Int) {} + + override fun onBluetoothHeadsetActivationError() {} + } + val (audioSwitch, bluetoothHeadsetReceiver, wiredHeadsetReceiver) = setupFakeAudioSwitch(context, bluetoothListener = bluetoothListener) + + audioSwitch.start { _, _ -> } + simulateBluetoothSystemIntent(context, bluetoothHeadsetReceiver) + simulateWiredHeadsetSystemIntent(context, wiredHeadsetReceiver) + + assertThat(audioSwitch.selectedAudioDevice!! is AudioDevice.BluetoothHeadset, equalTo(true)) + assertTrue(bluetoothConnectedLatch.await(5, TimeUnit.SECONDS)) + audioSwitch.stop() + } + @UiThreadTest @Test fun `it_should_select_the_wired_headset_by_default`() { diff --git a/audioswitch/src/androidTest/java/com.twilio.audioswitch/TestUtil.kt b/audioswitch/src/androidTest/java/com.twilio.audioswitch/TestUtil.kt index 69aea1c9..162310c1 100644 --- a/audioswitch/src/androidTest/java/com.twilio.audioswitch/TestUtil.kt +++ b/audioswitch/src/androidTest/java/com.twilio.audioswitch/TestUtil.kt @@ -14,6 +14,7 @@ import com.twilio.audioswitch.android.DEVICE_NAME import com.twilio.audioswitch.android.FakeBluetoothIntentProcessor import com.twilio.audioswitch.android.HEADSET_NAME import com.twilio.audioswitch.android.ProductionLogger +import com.twilio.audioswitch.bluetooth.BluetoothHeadsetConnectionListener import com.twilio.audioswitch.bluetooth.BluetoothHeadsetManager import com.twilio.audioswitch.wired.INTENT_STATE import com.twilio.audioswitch.wired.STATE_PLUGGED @@ -29,6 +30,7 @@ internal fun setupFakeAudioSwitch( Earpiece::class.java, Speakerphone::class.java, ), + bluetoothListener: BluetoothHeadsetConnectionListener? = null, ): Triple { val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager val logger = ProductionLogger(true) @@ -56,6 +58,7 @@ internal fun setupFakeAudioSwitch( return Triple( AudioSwitch( context, + bluetoothListener, logger, {}, preferredDevicesList, diff --git a/audioswitch/src/main/java/com/twilio/audioswitch/AudioSwitch.kt b/audioswitch/src/main/java/com/twilio/audioswitch/AudioSwitch.kt index 473bf5b0..889815ae 100644 --- a/audioswitch/src/main/java/com/twilio/audioswitch/AudioSwitch.kt +++ b/audioswitch/src/main/java/com/twilio/audioswitch/AudioSwitch.kt @@ -27,6 +27,8 @@ private const val TAG = "AudioSwitch" * accessed from a single application thread. Accessing an instance from multiple threads may cause * synchronization problems. * + * @property bluetoothHeadsetConnectionListener Listener to notify if Bluetooth device state has + * changed (connect, disconnect, audio connect, audio disconnect) or failed to connect. Null by default. * @property loggingEnabled A property to configure AudioSwitch logging behavior. AudioSwitch logging is disabled by * default. * @property selectedAudioDevice Retrieves the selected [AudioDevice] from [AudioSwitch.selectDevice]. @@ -44,6 +46,7 @@ class AudioSwitch { private val mutableAudioDevices = ArrayList() private var bluetoothHeadsetManager: BluetoothHeadsetManager? = null private val preferredDeviceList: List> + private var bluetoothHeadsetConnectionListener: BluetoothHeadsetConnectionListener? = null internal var state: State = STOPPED internal enum class State { @@ -51,13 +54,19 @@ class AudioSwitch { } internal val bluetoothDeviceConnectionListener = object : BluetoothHeadsetConnectionListener { - override fun onBluetoothHeadsetStateChanged(headsetName: String?) { + override fun onBluetoothHeadsetStateChanged(headsetName: String?, state: Int) { enumerateDevices(headsetName) + bluetoothHeadsetConnectionListener?.onBluetoothHeadsetStateChanged(headsetName, state) + } + + override fun onBluetoothScoStateChanged(state: Int) { + bluetoothHeadsetConnectionListener?.onBluetoothScoStateChanged(state) } override fun onBluetoothHeadsetActivationError() { if (userSelectedDevice is BluetoothHeadset) userSelectedDevice = null enumerateDevices() + bluetoothHeadsetConnectionListener?.onBluetoothHeadsetActivationError() } } @@ -85,6 +94,8 @@ class AudioSwitch { /** * Constructs a new AudioSwitch instance. * - [context] - An Android Context. + * - [bluetoothHeadsetConnectionListener] - A listener to notify if Bluetooth device state has + * changed (connect, disconnect, audio connect, audio disconnect) or failed to connect. Null by default * - [loggingEnabled] - Toggle whether logging is enabled. This argument is false by default. * - [audioFocusChangeListener] - A listener that is invoked when the system audio focus is updated. * Note that updates are only sent to the listener after [activate] has been called. @@ -101,11 +112,13 @@ class AudioSwitch { @JvmOverloads constructor( context: Context, + bluetoothHeadsetConnectionListener: BluetoothHeadsetConnectionListener? = null, loggingEnabled: Boolean = false, audioFocusChangeListener: OnAudioFocusChangeListener = OnAudioFocusChangeListener {}, preferredDeviceList: List> = defaultPreferredDeviceList, ) : this( context.applicationContext, + bluetoothHeadsetConnectionListener, ProductionLogger(loggingEnabled), audioFocusChangeListener, preferredDeviceList, @@ -114,6 +127,7 @@ class AudioSwitch { @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) internal constructor( context: Context, + bluetoothHeadsetConnectionListener: BluetoothHeadsetConnectionListener?, logger: Logger, audioFocusChangeListener: OnAudioFocusChangeListener, preferredDeviceList: List>, @@ -132,6 +146,7 @@ class AudioSwitch { ), ) { this.logger = logger + this.bluetoothHeadsetConnectionListener = bluetoothHeadsetConnectionListener this.audioDeviceManager = audioDeviceManager this.wiredHeadsetReceiver = wiredHeadsetReceiver this.bluetoothHeadsetManager = headsetManager diff --git a/audioswitch/src/main/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetConnectionListener.kt b/audioswitch/src/main/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetConnectionListener.kt index 30fb48e2..2163fbbf 100644 --- a/audioswitch/src/main/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetConnectionListener.kt +++ b/audioswitch/src/main/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetConnectionListener.kt @@ -1,6 +1,26 @@ package com.twilio.audioswitch.bluetooth -internal interface BluetoothHeadsetConnectionListener { - fun onBluetoothHeadsetStateChanged(headsetName: String? = null) +import android.bluetooth.BluetoothHeadset +import android.media.AudioManager + +/** + * Notifies if Bluetooth device state has changed (connect, disconnect, audio connect, audio disconnect) or failed to connect. + */ +interface BluetoothHeadsetConnectionListener { + + /** + * @param headsetName name of the headset + * @param state provided by [BluetoothHeadset] + */ + fun onBluetoothHeadsetStateChanged(headsetName: String? = null, state: Int = 0) + + /** + * @param state provided by [AudioManager] + */ + fun onBluetoothScoStateChanged(state: Int = 0) + + /** + * Triggered when Bluetooth SCO job has timed out. + */ fun onBluetoothHeadsetActivationError() } diff --git a/audioswitch/src/main/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetManager.kt b/audioswitch/src/main/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetManager.kt index 632160d1..60fc8923 100644 --- a/audioswitch/src/main/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetManager.kt +++ b/audioswitch/src/main/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetManager.kt @@ -17,6 +17,11 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager.PERMISSION_GRANTED +import android.media.AudioManager +import android.media.AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED +import android.media.AudioManager.SCO_AUDIO_STATE_CONNECTED +import android.media.AudioManager.SCO_AUDIO_STATE_CONNECTING +import android.media.AudioManager.SCO_AUDIO_STATE_DISCONNECTED import android.os.Handler import android.os.Looper import androidx.annotation.VisibleForTesting @@ -123,7 +128,7 @@ internal constructor( "Bluetooth headset $bluetoothDevice connected", ) connect() - headsetListener?.onBluetoothHeadsetStateChanged(bluetoothDevice.name) + headsetListener?.onBluetoothHeadsetStateChanged(bluetoothDevice.name, STATE_CONNECTED) } STATE_DISCONNECTED -> { logger.d( @@ -131,13 +136,13 @@ internal constructor( "Bluetooth headset $bluetoothDevice disconnected", ) disconnect() - headsetListener?.onBluetoothHeadsetStateChanged() + headsetListener?.onBluetoothHeadsetStateChanged(bluetoothDevice.name, STATE_DISCONNECTED) } STATE_AUDIO_CONNECTED -> { logger.d(TAG, "Bluetooth audio connected on device $bluetoothDevice") enableBluetoothScoJob.cancelBluetoothScoJob() headsetState = AudioActivated - headsetListener?.onBluetoothHeadsetStateChanged() + headsetListener?.onBluetoothHeadsetStateChanged(bluetoothDevice.name, STATE_AUDIO_CONNECTED) } STATE_AUDIO_DISCONNECTED -> { logger.d(TAG, "Bluetooth audio disconnected on device $bluetoothDevice") @@ -150,12 +155,47 @@ internal constructor( enableBluetoothScoJob.executeBluetoothScoJob() } - headsetListener?.onBluetoothHeadsetStateChanged() + headsetListener?.onBluetoothHeadsetStateChanged(bluetoothDevice.name, STATE_AUDIO_DISCONNECTED) } else -> {} } } } + intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, SCO_AUDIO_STATE_DISCONNECTED).let { state -> + when (state) { + SCO_AUDIO_STATE_CONNECTING -> { + logger.d( + TAG, + "Bluetooth SCO connecting", + ) + + headsetListener?.onBluetoothScoStateChanged( + SCO_AUDIO_STATE_CONNECTING, + ) + } + SCO_AUDIO_STATE_CONNECTED -> { + logger.d( + TAG, + "Bluetooth SCO connected", + ) + + headsetListener?.onBluetoothScoStateChanged( + SCO_AUDIO_STATE_CONNECTED, + ) + } + SCO_AUDIO_STATE_DISCONNECTED -> { + logger.d( + TAG, + "Bluetooth SCO disconnected", + ) + + headsetListener?.onBluetoothScoStateChanged( + SCO_AUDIO_STATE_DISCONNECTED, + ) + } + else -> {} + } + } } } @@ -177,6 +217,10 @@ internal constructor( this, IntentFilter(ACTION_AUDIO_STATE_CHANGED), ) + context.registerReceiver( + this, + IntentFilter(ACTION_SCO_AUDIO_STATE_UPDATED), + ) hasRegisteredReceivers = true } } else { @@ -243,7 +287,7 @@ internal constructor( } private fun isCorrectIntentAction(intentAction: String?) = - intentAction == ACTION_CONNECTION_STATE_CHANGED || intentAction == ACTION_AUDIO_STATE_CHANGED + intentAction == ACTION_CONNECTION_STATE_CHANGED || intentAction == ACTION_AUDIO_STATE_CHANGED || intentAction == ACTION_SCO_AUDIO_STATE_UPDATED private fun connect() { if (!hasActiveHeadset()) headsetState = Connected diff --git a/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchJavaTest.java b/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchJavaTest.java index e0b074d3..b2111a57 100644 --- a/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchJavaTest.java +++ b/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchJavaTest.java @@ -34,6 +34,7 @@ public void setUp() { javaAudioSwitch = new AudioSwitch( getContext$audioswitch_debug(), + null, new UnitTestLogger(false), getDefaultAudioFocusChangeListener$audioswitch_debug(), getPreferredDeviceList$audioswitch_debug(), @@ -130,6 +131,7 @@ public void shouldAllowChangingThePreferredDeviceList() { javaAudioSwitch = new AudioSwitch( getContext$audioswitch_debug(), + getBluetoothListener$audioswitch_debug(), getLogger$audioswitch_debug(), getDefaultAudioFocusChangeListener$audioswitch_debug(), preferredDeviceList, diff --git a/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchTest.kt b/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchTest.kt index ce12b332..3aa0cd59 100644 --- a/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchTest.kt +++ b/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchTest.kt @@ -94,6 +94,7 @@ class AudioSwitchTest : BaseTest() { fun `start should not start the HeadsetManager if it is null`() { audioSwitch = AudioSwitch( context = context, + bluetoothHeadsetConnectionListener = bluetoothListener, logger = logger, audioDeviceManager = audioDeviceManager, wiredHeadsetReceiver = wiredHeadsetReceiver, @@ -201,6 +202,7 @@ class AudioSwitchTest : BaseTest() { fun `stop should not stop the BluetoothHeadsetManager if it is null and if transitioning from the started state`() { audioSwitch = AudioSwitch( context = context, + bluetoothHeadsetConnectionListener = bluetoothListener, logger = logger, audioDeviceManager = audioDeviceManager, wiredHeadsetReceiver = wiredHeadsetReceiver, @@ -219,6 +221,7 @@ class AudioSwitchTest : BaseTest() { fun `stop should not stop the BluetoothHeadsetManager if it is null and if transitioning from the activated state`() { audioSwitch = AudioSwitch( context = context, + bluetoothHeadsetConnectionListener = bluetoothListener, logger = logger, audioDeviceManager = audioDeviceManager, wiredHeadsetReceiver = wiredHeadsetReceiver, @@ -441,6 +444,7 @@ class AudioSwitchTest : BaseTest() { fun `constructor should throw an IllegalArgumentException given duplicate preferred devices`() { audioSwitch = AudioSwitch( context = context, + bluetoothHeadsetConnectionListener = bluetoothListener, logger = logger, audioDeviceManager = audioDeviceManager, wiredHeadsetReceiver = wiredHeadsetReceiver, @@ -460,6 +464,7 @@ class AudioSwitchTest : BaseTest() { // Switch selection order so BT isn't automatically selected audioSwitch = AudioSwitch( context = context, + bluetoothHeadsetConnectionListener = bluetoothListener, logger = logger, audioDeviceManager = audioDeviceManager, wiredHeadsetReceiver = wiredHeadsetReceiver, @@ -501,6 +506,7 @@ class AudioSwitchTest : BaseTest() { ) { audioSwitch = AudioSwitch( context = context, + bluetoothHeadsetConnectionListener = bluetoothListener, logger = logger, audioDeviceManager = audioDeviceManager, wiredHeadsetReceiver = wiredHeadsetReceiver, @@ -525,6 +531,7 @@ class AudioSwitchTest : BaseTest() { ) { audioSwitch = AudioSwitch( context = context, + bluetoothHeadsetConnectionListener = bluetoothListener, logger = logger, audioDeviceManager = audioDeviceManager, wiredHeadsetReceiver = wiredHeadsetReceiver, @@ -550,6 +557,7 @@ class AudioSwitchTest : BaseTest() { ) { audioSwitch = AudioSwitch( context = context, + bluetoothHeadsetConnectionListener = bluetoothListener, logger = logger, audioDeviceManager = audioDeviceManager, wiredHeadsetReceiver = wiredHeadsetReceiver, @@ -574,6 +582,7 @@ class AudioSwitchTest : BaseTest() { ) { audioSwitch = AudioSwitch( context = context, + bluetoothHeadsetConnectionListener = bluetoothListener, logger = logger, audioDeviceManager = audioDeviceManager, wiredHeadsetReceiver = wiredHeadsetReceiver, @@ -609,6 +618,7 @@ class AudioSwitchTest : BaseTest() { ) { audioSwitch = AudioSwitch( context = context, + bluetoothHeadsetConnectionListener = bluetoothListener, logger = logger, audioDeviceManager = audioDeviceManager, wiredHeadsetReceiver = wiredHeadsetReceiver, diff --git a/audioswitch/src/test/java/com/twilio/audioswitch/BaseTest.kt b/audioswitch/src/test/java/com/twilio/audioswitch/BaseTest.kt index c26d3e6a..eaa067a7 100644 --- a/audioswitch/src/test/java/com/twilio/audioswitch/BaseTest.kt +++ b/audioswitch/src/test/java/com/twilio/audioswitch/BaseTest.kt @@ -12,6 +12,7 @@ import com.twilio.audioswitch.AudioDevice.Earpiece import com.twilio.audioswitch.AudioDevice.Speakerphone import com.twilio.audioswitch.AudioDevice.WiredHeadset import com.twilio.audioswitch.android.BuildWrapper +import com.twilio.audioswitch.bluetooth.BluetoothHeadsetConnectionListener import com.twilio.audioswitch.bluetooth.BluetoothHeadsetManager import com.twilio.audioswitch.wired.WiredHeadsetReceiver import org.hamcrest.CoreMatchers @@ -29,6 +30,7 @@ open class BaseTest { whenever(mock.bluetoothClass).thenReturn(bluetoothClass) } internal val context = mock() + internal val bluetoothListener = mock() internal val logger = UnitTestLogger() internal val audioManager = setupAudioManagerMock() internal val bluetoothAdapter = mock() @@ -68,6 +70,7 @@ open class BaseTest { internal var audioSwitch = AudioSwitch( context = context, + bluetoothHeadsetConnectionListener = bluetoothListener, logger = logger, audioDeviceManager = audioDeviceManager, wiredHeadsetReceiver = wiredHeadsetReceiver, diff --git a/audioswitch/src/test/java/com/twilio/audioswitch/TestUtil.kt b/audioswitch/src/test/java/com/twilio/audioswitch/TestUtil.kt index 971891e4..6591f263 100644 --- a/audioswitch/src/test/java/com/twilio/audioswitch/TestUtil.kt +++ b/audioswitch/src/test/java/com/twilio/audioswitch/TestUtil.kt @@ -43,7 +43,7 @@ internal fun BaseTest.assertBluetoothHeadsetSetup() { headsetManager, BluetoothProfile.HEADSET, ) - verify(context, times(2)).registerReceiver(eq(headsetManager), isA()) + verify(context, times(3)).registerReceiver(eq(headsetManager), isA()) } internal fun setupPermissionsCheckStrategy() = diff --git a/audioswitch/src/test/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetManagerTest.kt b/audioswitch/src/test/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetManagerTest.kt index e2282962..3fcf4081 100644 --- a/audioswitch/src/test/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetManagerTest.kt +++ b/audioswitch/src/test/java/com/twilio/audioswitch/bluetooth/BluetoothHeadsetManagerTest.kt @@ -205,7 +205,7 @@ class BluetoothHeadsetManagerTest : BaseTest() { headsetManager.onReceive(context, intent) val invocationCount = if (isNewDeviceConnected) 1 else 0 - verify(headsetListener, times(invocationCount)).onBluetoothHeadsetStateChanged(DEVICE_NAME) + verify(headsetListener, times(invocationCount)).onBluetoothHeadsetStateChanged(DEVICE_NAME, BluetoothHeadset.STATE_CONNECTED) } @Parameters(method = "parameters") @@ -225,15 +225,15 @@ class BluetoothHeadsetManagerTest : BaseTest() { headsetManager.onReceive(context, intent) val invocationCount = if (isDeviceDisconnected) 1 else 0 - verify(headsetListener, times(invocationCount)).onBluetoothHeadsetStateChanged() + verify(headsetListener, times(invocationCount)).onBluetoothHeadsetStateChanged(headsetName = "Bluetooth") } @Test - fun `onReceive should not register a new device when an ACL connected event is received with a null bluetooth device`() { + fun `onReceive should trigger once for sco disconnect when an ACL connected event is received with a null bluetooth device`() { whenever(expectedBluetoothDevice.bluetoothClass).thenReturn(null) simulateNewBluetoothHeadsetConnection() - verifyNoInteractions(headsetListener) + verify(headsetListener, times(1)).onBluetoothScoStateChanged(0) } @Test