DEV Community

Cover image for Connecting to Bluetooth Audio Devices in Android
Olisemeka Nwaeme
Olisemeka Nwaeme

Posted on

Connecting to Bluetooth Audio Devices in Android

Introduction

Bluetooth technology has made it easier to connect to audio devices like speakers and headphones by eliminating the need for wires. Android Developers can take this a step further by enabling users to connect directly to these devices from within an app. This guide walks you through writing code to establish connections to Bluetooth audio devices without needing users to navigate to the Settings screen.

Prerequisites

  • Android Studio: Ensure you have the latest version installed, though recent versions will work too.
  • Kotlin: Examples are written in Kotlin, so familiarity with the language is assumed.
  • Android SDK: Your project should target Android SDK 31 (Android 12) or higher, as Bluetooth-related functionality in Android 12+ requires specific permissions and features.

Step 1: Setup Bluetooth in your App

To enable Bluetooth functionality, begin by setting up the necessary permissions and checking the Bluetooth state on the device.

  • Add permissions: In your AndroidManifest.xml file, include the required Bluetooth permissions
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Enter fullscreen mode Exit fullscreen mode
  • Check Bluetooth State: Confirm that the device supports Bluetooth, whether it’s enabled, and if your app has the necessary permissions.
class MainActivity : AppCompatActivity() {
    private var bluetoothAdapter: BluetoothAdapter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        bluetoothAdapter = getSystemService(BluetoothManager::class.java).adapter
        if (bluetoothAdapter == null) {
            return //Bluetooth is not supported on this device
        }

        if (!hasBluetoothPermissions) {
            requestBluetoothPermissions()
            return
        }

        if (bluetoothAdapter?.isEnabled == false) {
            enableBluetooth()
            return
        }

        //Bluetooth is ready to use
    }

    private fun hasBluetoothPermissions(): Boolean {
        return ActivityCompat.checkSelfPermission(
            this, android.Manifest.permission.BLUETOOTH_CONNECT
        ) == PackageManager.PERMISSION_GRANTED
    }

    private fun requestBluetoothPermissions() {
        requestPermissions(
            arrayOf(android.Manifest.permission.BLUETOOTH_CONNECT),
            REQUEST_BLUETOOTH_PERMISSION_CODE
        )
    }

    private fun enableBluetooth() {
        val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        startActivityForResult(intent, REQUEST_ENABLE_BLUETOOTH_CODE)
    }
}

Enter fullscreen mode Exit fullscreen mode

Step 2: Get Paired Devices

To connect to a Bluetooth audio device, retrieve a list of paired devices using the BluetoothAdapter instance.

val pairedDevices: List<BluetoothDevice>? = bluetoothAdapter?.bondedDevices?.toList() ?: emptyList()
Enter fullscreen mode Exit fullscreen mode

Step 3: Establish Connection

To connect to a Bluetooth audio device, use the BluetoothA2dp profile, which handles streaming high-quality audio. Since Android doesn’t provide a direct method for connecting to BluetoothA2dp devices, use reflection to access the hidden connect() method.

fun connectToA2dpDevice(context: Context, device: BluetoothDevice) {
    bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
        override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) {
            if (profile == BluetoothProfile.A2DP) {
                val a2dp = proxy as BluetoothA2dp
                try {
                    val connectMethod = BluetoothA2dp::class.java.getDeclaredMethod("connect", BluetoothDevice::class.java)
                    connectMethod.isAccessible
                    connectMethod.invoke(a2dp, device)
                } catch (e: Exception){
                    e.printStackTrace()
                }
            }
        }

        override fun onServiceDisconnected(profile: Int) {
            //Not needed
        }
    }, BluetoothProfile.A2DP)
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Monitor Connection State

During the connection, it’s helpful to update the UI to reflect the connection state, such as showing a loading state while connecting or a success message when connected. Use a BroadcastReceiver to listen for changes in the Bluetooth connection state, specifically with the BluetoothA2dp profile.

val bluetoothReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        when (intent.action) {
            BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED -> {
                val state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)
                val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)

                when (state) {
                    BluetoothProfile.STATE_CONNECTING -> {
                        // Show loading state
                    }

                    BluetoothProfile.STATE_CONNECTED -> {
                        // Show connection success
                    }

                    BluetoothProfile.STATE_DISCONNECTED -> {
                        // Handle disconnection
                    }
                }
            }
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Step 5: Disconnect

To disconnect from a Bluetooth audio device using the BluetoothA2dp profile, use reflection to access the hidden disconnect() method.

fun disconnectFromA2dpDevice(context: Context, device: BluetoothDevice) {
    bluetoothAdapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
        override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) {
            if (profile == BluetoothProfile.A2DP) {
                a2dp = proxy as BluetoothA2dp
                try {
                    val disconnectMethod = BluetoothA2dp::class.java.getDeclaredMethod("disconnect", BluetoothDevice::class.java)
                    disconnectMethod.isAccessible
                    disconnectMethod.invoke(a2dp, device)
                } catch (e: Exception){
                    e.printStackTrace()
                }
            }
        }

        override fun onServiceDisconnected(profile: Int) {
            //Not needed
        }
    }, BluetoothProfile.A2DP)
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

This guide demonstrated how to connect to a Bluetooth audio device using the BluetoothA2dp profile, monitor connection states, and disconnect directly from your app. These capabilities allow developers to create a seamless Bluetooth audio experience, providing users with an enhanced and intuitive interface. Happy coding, and may your Bluetooth connections always be strong!

Top comments (0)