• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Apps Bluetooth discovery issue - Jetpack Compose + Kotlin

Hey all. I am new to android development. I discovered compose and kotlin working with react and figured I would learn and get good at something new.
I am trying to figure out how to use bluetooth. I followed the developer guides and have done a bunch of looking around but can't seem to solve the issue. My problem is that when I call
Code:
 startDiscovery()
I don't actually discover any devices.

in manifest :
Code:
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<permission android:name="android.permission.BLUETOOTH"/>
<permission android:name="android.permission.BLUETOOTH_ADMIN" />
<permission android:name="android.permission.ACCESS_FINE_LOCATION" />

My very unpolished code is here:
Code:
class MainActivity : ComponentActivity() {
    private val PERMISSION_CODE = 1
    private val bluetoothAdapter: BluetoothAdapter = getDefaultAdapter()

    private val activityResultLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == RESULT_OK) {
            Log.i("bluetooth", "request permission result OK")
        } else {
            Log.i("bluetooth", "request permission result CANCELED/DENIED")
        }
    }

    private fun requestBluetoothPermission() {
        val enableBluetoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        activityResultLauncher.launch(enableBluetoothIntent)
    }

    var pairedDevices: Set<BluetoothDevice> = bluetoothAdapter.bondedDevices
    var discoveredDevices: Set<BluetoothDevice> = emptySet()

    val receiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent) {
            when (intent.action) {
                BluetoothDevice.ACTION_FOUND -> {
                    val device: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
                    if (device != null) {
                        val updated = discoveredDevices.plus(device)
                        discoveredDevices = updated
                    }
                    Log.i("bluetooth", "device found")
                }
                BluetoothAdapter.ACTION_DISCOVERY_STARTED -> {
                    Log.i("bluetooth", "started discovery")
                }
                BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
                    Log.i("bluetooth", "finished discovery")
                }
            }
        }
    }

    @RequiresApi(Build.VERSION_CODES.M)
    fun scan(): Set<BluetoothDevice> {
        if (bluetoothAdapter.isDiscovering) {
            bluetoothAdapter.cancelDiscovery()
            bluetoothAdapter.startDiscovery()
        } else {
            bluetoothAdapter.startDiscovery()
        }

        Handler(Looper.getMainLooper()).postDelayed({
            bluetoothAdapter.cancelDiscovery()
        }, 10000L)
        return discoveredDevices
    }

    @RequiresApi(Build.VERSION_CODES.M)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val foundFilter = IntentFilter(BluetoothDevice.ACTION_FOUND)
        val startFilter = IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
        val endFilter = IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
        registerReceiver(receiver, foundFilter)
        registerReceiver(receiver, startFilter)
        registerReceiver(receiver, endFilter)
        Log.i("bluetooth", "filters registered")

        if (!bluetoothAdapter.isEnabled) {
            requestBluetoothPermission()
        }

        if (SDK_INT >= Build.VERSION_CODES.Q) {
            if (ContextCompat.checkSelfPermission(
                    baseContext,
                    android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
                ) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION),
                    PERMISSION_CODE
                )
            }
        }

        setContent {
            var devices: Set<BluetoothDevice> by remember {
                mutableStateOf(emptySet())
            }
            MyappTheme {
                Surface(color = MaterialTheme.colors.background) {
                    Column(
                        Modifier.fillMaxHeight()
                    ) {
                        Button(onClick = {
                            devices = scan()
                        }) {
                            Text(text = "Scan", style = MaterialTheme.typography.h3)
                        }
                        Text(text = "PAIRED DEVICES", style = MaterialTheme.typography.h4)
                        Divider(color = Color.Black)
                        pairedDevices.forEach { device ->
                            Card {
                                Column {
                                    Text(text = device.name)
                                    Text(text = device.address)
                                }
                            }
                        }
                        Text(text = "DISCOVERED DEVICES", style = MaterialTheme.typography.h4)
                        Divider(color = Color.Black)
                        devices.forEach { device ->
                            Card {
                                Column {
                                    Text(text = device.name)
                                    Text(text = device.address)
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        if (bluetoothAdapter.isDiscovering) bluetoothAdapter.cancelDiscovery()
        unregisterReceiver(receiver)
    }
}

I think the issue might be something to do with the broadcast receiver. Looking at Logcat when I hit 'scan' I get 'discovery strarted' from 'BluetoothAdapter' but I never get any of the info logs from the broadcast receiver and my device list doesn't populate with anything. Any guidance or help is much appreciated.
 
UPDATE: I got it to actually respond and log via the broadcast receiver that it was discovering devices. My first thought after taking a break and returning with a clear head was that the Broadcast receiver was returning before it ever received anything. I also refactored my code a bit and discovered that permissions for bluetooth and location are not actually getting requested properly. I will refactor and clean stuff up to make testing/proving this more productive. There still might be a need to run the broadcast receiver asynchronously via coroutine but I will start with assumption that it is just permissions for now. I will update when I figure it out. Still open to any suggestions!
 
If you are using android.permission.ACCESS_FINE_LOCATION I have found that you must also have android.permission.ACCESS_COARSE_LOCATION.
My Manifest permissions look like this
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>

Hope that helps
 
Back
Top Bottom