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.
 

Cody Molisee

Lurker
Thread starter
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
 
Top