diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ffd62e..6a6cb44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Enhancements - AudioDeviceChangeListener is now optional parameter when calling `AudioSwitch.start(listener: AudioDeviceChangeListener? = null)` - Added `AudioSwitch.setAudioDeviceChangeListener(listener: AudioDeviceChangeListener?)` +- BluetoothHeadsetManager now checks for permissions every time it is called, creates an instance if null and permissions granted ### 1.2.0 (June 3, 2024) diff --git a/audioswitch/src/main/java/com/twilio/audioswitch/AudioSwitch.kt b/audioswitch/src/main/java/com/twilio/audioswitch/AudioSwitch.kt index e0e11a7..f6cbb5a 100644 --- a/audioswitch/src/main/java/com/twilio/audioswitch/AudioSwitch.kt +++ b/audioswitch/src/main/java/com/twilio/audioswitch/AudioSwitch.kt @@ -2,7 +2,7 @@ package com.twilio.audioswitch import android.Manifest import android.annotation.SuppressLint -import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothManager import android.content.Context import android.content.pm.PackageManager.PERMISSION_GRANTED import android.media.AudioManager @@ -40,7 +40,7 @@ private const val PERMISSION_ERROR_MESSAGE = "Bluetooth unsupported, permissions * @property availableAudioDevices Retrieves the current list of available [AudioDevice]s. **/ class AudioSwitch { - + private val context: Context private var logger: Logger = ProductionLogger() private val audioDeviceManager: AudioDeviceManager private val wiredHeadsetReceiver: WiredHeadsetReceiver @@ -145,13 +145,15 @@ class AudioSwitch { ), wiredHeadsetReceiver: WiredHeadsetReceiver = WiredHeadsetReceiver(context, logger), permissionsCheckStrategy: PermissionsCheckStrategy = DefaultPermissionsCheckStrategy(context), + bluetoothManager: BluetoothManager? = context.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager, bluetoothHeadsetManager: BluetoothHeadsetManager? = BluetoothHeadsetManager.newInstance( context, logger, - BluetoothAdapter.getDefaultAdapter(), + bluetoothManager?.adapter, audioDeviceManager, ), ) { + this.context = context this.logger = logger this.bluetoothHeadsetConnectionListener = bluetoothHeadsetConnectionListener this.audioDeviceManager = audioDeviceManager @@ -196,7 +198,7 @@ class AudioSwitch { STOPPED -> { state = STARTED enumerateDevices() - bluetoothHeadsetManager?.start(bluetoothDeviceConnectionListener) + bluetoothHeadsetManager.instance()?.start(bluetoothDeviceConnectionListener) wiredHeadsetReceiver.start(wiredDeviceConnectionListener) } else -> { @@ -263,7 +265,7 @@ class AudioSwitch { when (state) { ACTIVATED -> { state = STARTED - bluetoothHeadsetManager?.deactivate() + bluetoothHeadsetManager.instance()?.deactivate() // Restore stored audio state audioDeviceManager.restoreAudioState() @@ -294,15 +296,15 @@ class AudioSwitch { when (audioDevice) { is BluetoothHeadset -> { audioDeviceManager.enableSpeakerphone(false) - bluetoothHeadsetManager?.activate() + bluetoothHeadsetManager.instance()?.activate() } is Earpiece, is WiredHeadset -> { audioDeviceManager.enableSpeakerphone(false) - bluetoothHeadsetManager?.deactivate() + bluetoothHeadsetManager.instance()?.deactivate() } is Speakerphone -> { audioDeviceManager.enableSpeakerphone(true) - bluetoothHeadsetManager?.deactivate() + bluetoothHeadsetManager.instance()?.deactivate() } } } @@ -333,7 +335,7 @@ class AudioSwitch { * be the next valid device in the list. */ if (firstAudioDevice is BluetoothHeadset && - bluetoothHeadsetManager?.hasActivationError() == true + bluetoothHeadsetManager.instance()?.hasActivationError() == true ) { mutableAudioDevices[1] } else { @@ -365,7 +367,7 @@ class AudioSwitch { * headset name received from the ACTION_ACL_CONNECTED intent needs to be passed into this * function. */ - bluetoothHeadsetManager?.getHeadset(bluetoothHeadsetName)?.let { + bluetoothHeadsetManager.instance()?.getHeadset(bluetoothHeadsetName)?.let { mutableAudioDevices.add(it) } } @@ -405,11 +407,24 @@ class AudioSwitch { private fun closeListeners() { state = STOPPED - bluetoothHeadsetManager?.stop() + bluetoothHeadsetManager.instance()?.stop() wiredHeadsetReceiver.stop() audioDeviceChangeListener = null } + private fun BluetoothHeadsetManager?.instance(): BluetoothHeadsetManager? { + if (this == null && hasPermissions()) { + val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager + return BluetoothHeadsetManager.newInstance( + context, + logger, + bluetoothManager?.adapter, + audioDeviceManager, + ) + } + return this + } + internal fun hasPermissions() = permissionsRequestStrategy.hasPermissions() internal class DefaultPermissionsCheckStrategy(private val context: Context) : PermissionsCheckStrategy { diff --git a/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchJavaTest.java b/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchJavaTest.java index 82fca0e..9af9d1a 100644 --- a/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchJavaTest.java +++ b/audioswitch/src/test/java/com/twilio/audioswitch/AudioSwitchJavaTest.java @@ -41,6 +41,7 @@ public void setUp() { getAudioDeviceManager$audioswitch_debug(), getWiredHeadsetReceiver$audioswitch_debug(), getPermissionsStrategyProxy$audioswitch_debug(), + getBluetoothManager$audioswitch_debug(), getHeadsetManager$audioswitch_debug()); } @@ -139,6 +140,7 @@ public void shouldAllowChangingThePreferredDeviceList() { getAudioDeviceManager$audioswitch_debug(), getWiredHeadsetReceiver$audioswitch_debug(), getPermissionsStrategyProxy$audioswitch_debug(), + getBluetoothManager$audioswitch_debug(), getHeadsetManager$audioswitch_debug()); startAudioSwitch(); diff --git a/audioswitch/src/test/java/com/twilio/audioswitch/BaseTest.kt b/audioswitch/src/test/java/com/twilio/audioswitch/BaseTest.kt index 34e88fe..7a5c9a3 100644 --- a/audioswitch/src/test/java/com/twilio/audioswitch/BaseTest.kt +++ b/audioswitch/src/test/java/com/twilio/audioswitch/BaseTest.kt @@ -4,6 +4,7 @@ import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothClass import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothHeadset +import android.bluetooth.BluetoothManager import android.bluetooth.BluetoothProfile import android.content.Context import android.content.Intent @@ -33,7 +34,9 @@ open class BaseTest { internal val bluetoothListener = mock() internal val logger = UnitTestLogger() internal val audioManager = setupAudioManagerMock() + internal val bluetoothManager = mock() internal val bluetoothAdapter = mock() + internal val audioDeviceChangeListener = mock() internal val buildWrapper = mock() internal val audioFocusRequest = mock() @@ -76,6 +79,7 @@ open class BaseTest { audioFocusChangeListener = defaultAudioFocusChangeListener, preferredDeviceList = preferredDeviceList, permissionsCheckStrategy = permissionsStrategyProxy, + bluetoothManager = bluetoothManager, bluetoothHeadsetManager = headsetManager, )