Jim
Jim

Reputation: 43

Scanning for Bluetooth devices with startDiscovery but no devices found

I writing in Kotlin and am able to set up a receiver to handle the ACTION_DISCOVERY_STARTED and ACTION_DISCOVERY_FINISHED events caused by startDiscovery (I print a message to a text view and see that they work).

However, the ACTION_FOUND never gets invoked in the receiver (I don't ever see the text view change that I update in that routine when a device should be found).

I have what should be all the permissions (and more) in the manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.test_ble">
<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"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.Test_BLE">
    <activity
        android:name=".MainActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

I have two text views. text_view1 is used to report status of the scanning and text_view2 is used to place the list of scanned devices. The devices are (crudely for now) just concatenated to a string and then assigned to the text_view2.text. However, I never see that list get updated.

I know there are other Bluetooth devices advertising in the room, because I can see them on the Asus Nexus 7 tablet (Android 6.1) that I'm using to test the code under the device settings for Bluetooth.

The minSdk should be low enough at 21. Here's the buildgradle Module script:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}
android {
    compileSdk 31
    defaultConfig {
        applicationId "com.example.test_ble"
        minSdk 21
        targetSdk 31
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    buildFeatures{
        viewBinding = true
    }
}
dependencies {
    implementation 'androidx.core:core-ktx:1.6.0'
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

Here's the MainActivity.kt file.

package com.example.test_ble

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.test_ble.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var btAdapter: BluetoothAdapter
    private var deviceList: String = ""
    // *****************************************************
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        // Get the BluetoothAdapter
        btAdapter = BluetoothAdapter.getDefaultAdapter()
        if (btAdapter == null) {
            binding.textView1.text = "No Bt Adapter!"
        } else if (!btAdapter.isEnabled) {
            binding.textView1.text = "Bt Adapter is NOT ENABLED!"
        } else {
            binding.textView1.text = "Bt Adapter is ENABLED!"

            // Build receiver for Device Discovery
            val receiver = object : BroadcastReceiver() {
                override fun onReceive(context: Context, intent: Intent) {
                    when (intent.action) {
                        BluetoothAdapter.ACTION_DISCOVERY_STARTED -> {
                            // Device discovery started, show loading indication
                            binding.textView1.text = "Device discovery started"
                        }
                        BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
                            // unregister receiver and hide loading indication
                            binding.textView1.text = "Device discovery finished"
                        }
                        BluetoothDevice.ACTION_FOUND -> {
                            binding.textView1.text = "ACTION_FOUND" // I never see this message
                            val device: BluetoothDevice? =
                                intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
                            val deviceName = device?.name
                            val deviceHardwareAddress = device?.address // MAC address
                            deviceList =
                                deviceList + "\n" + deviceName.toString() + " " + deviceHardwareAddress.toString()
                            binding.textView2.text = deviceList // The device list is never updated
                        }
                    }
                }
            }
            // Register for broadcasts when a device is discovered.
            val intentFilter = IntentFilter().apply {
                addAction(BluetoothDevice.ACTION_FOUND)
                addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
                addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
            }
            if (btAdapter.isDiscovering()) {
                btAdapter.cancelDiscovery();
            }
            registerReceiver(receiver, intentFilter)
            btAdapter.startDiscovery()
        }
    }
}

Can anyone see what I'm missing or doing incorrectly? I can't see why I'm not getting any devices detected when I do see that the start and finish for the discovery do occur. I'm out of ideas after searching and trying everything I can find on the web. Help greatly appreciated.

Jim

Upvotes: 0

Views: 2112

Answers (1)

Vikas Choudhary
Vikas Choudhary

Reputation: 554

You have declared the permissions in the Manifest but have you asked user to allow the ACCESS_FINE_LOCATION & ACCESS_COARSE_LOCATION permissions ? Or can you allow these permissions by going to app info in settings and then try to discover the devices. If your app is able to discover then you might need to ask runtime permissions from the user.

Upvotes: 1

Related Questions