skip to Main Content

i’ve been stuck for a week, i want to make an app that can connect to other android device via bluetooth but cannot discover any devices as it just return empty array.

This is code that i use to discover other android bluetooth devices:

public class BluetoothScannerModule extends ReactContextBaseJavaModule {
    private BluetoothAdapter bluetoothAdapter;
    private List<BluetoothDevice> discoveredDevices = new ArrayList<>();

    public BluetoothScannerModule(ReactApplicationContext reactContext) {
        super(reactContext);
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        reactContext.registerReceiver(receiver, filter);
    }

    @NonNull
    @Override
    public String getName() {
        return "BluetoothScannerModule";
    }

    private final BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (device != null && !discoveredDevices.contains(device)) {
                    discoveredDevices.add(device);
                }
            }
        }
    };

    @ReactMethod
    public void scanDevices(Callback callback) {
        discoveredDevices.clear();
        if (bluetoothAdapter == null) {
            callback.invoke("Bluetooth not supported");
            return;
        }
        if (!bluetoothAdapter.isEnabled()) {
            callback.invoke("Bluetooth not enabled");
            return;
        }
        if (ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{Manifest.permission.BLUETOOTH_SCAN}, 2);
                return;
            }
        }
        bluetoothAdapter.startDiscovery();
        try {
            Thread.sleep(5000); // scan for 5 seconds
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bluetoothAdapter.cancelDiscovery();
        WritableArray devicesArray = Arguments.createArray();
        for (BluetoothDevice device : discoveredDevices) {
            WritableMap deviceMap = Arguments.createMap();
            deviceMap.putString("name", device.getName());
            deviceMap.putString("address", device.getAddress());
            deviceMap.putInt("bondState", device.getBondState());
            devicesArray.pushMap(deviceMap);
        }
        callback.invoke(null, devicesArray);
    }
}

and log it into react native like this to see the list of devices:

BluetoothScannerModule.scanDevices((error, devices) => {
          if (error) {
            console.error(error);
            return;
          }
          console.log(devices);
        })

EDITED CODE

In the Native Module:

@ReactMethod
    public void scanDevices(Callback callback) {
        discoveredDevices.clear();
        if (bluetoothAdapter == null) {
            callback.invoke("Bluetooth not supported");
            return;
        }
        if (!bluetoothAdapter.isEnabled()) {
            callback.invoke("Bluetooth not enabled");
            return;
        }
        if (ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{Manifest.permission.BLUETOOTH_SCAN}, 2);
                return;
            }
        }
        bluetoothAdapter.startDiscovery();

        callback.invoke(null,"Discovery started");
    }

    @ReactMethod
    public void stopScan(Callback callback) {
        if (ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{Manifest.permission.BLUETOOTH_SCAN}, 2);
                return;
            }
        }
        bluetoothAdapter.cancelDiscovery();
        WritableArray devicesArray = Arguments.createArray();
        for (BluetoothDevice device : discoveredDevices) {
            WritableMap deviceMap = Arguments.createMap();
            deviceMap.putString("name", device.getName());
            deviceMap.putString("address", device.getAddress());
            deviceMap.putInt("bondState", device.getBondState());
            devicesArray.pushMap(deviceMap);
        }
        callback.invoke(null, devicesArray);
    }

In React Native:

type Device = {
  name: string | null;
  address: string;
  bondState: number;
};

const [devices, setDevices] = useState<Device[]>([]);

const startDiscovery = () => {
    BluetoothScannerModule.scanDevices((error: string | null, result: string) => {
    if (error) {
      console.log(error);
    } else {
      console.log(result);
    }
  });
};

const cancelDiscovery = () => {
  BluetoothScannerModule.stopScan((error: string | null, result: Device[]) => {
    if (error) {
      console.log(error);
    } else {
      setDevices(result);
    }
  });
};

const renderDevice = ({ item }: { item: Device }) => (
  <View>
    <Text>{item.name ?? 'Unnamed device'}</Text>
    <Text>{item.address}</Text>
    <Text>Bond state: {item.bondState}</Text>
  </View>
);

...

<Button
   title='Start Scan'
   color="#841584"
   accessibilityLabel="Learn more about this purple button"
   onPress={startDiscovery}
/>
<Button
   title='Stop Scan'
   color="#841584"
   accessibilityLabel="Learn more about this purple button"
   onPress={cancelDiscovery}
/>
<FlatList data={devices} renderItem={renderDevice} />

Android Manifest

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

2

Answers


  1. The startDiscovery() documentation says the discovery process usually takes 12 seconds. But you are calling cancelDiscovery() after 5 seconds. So this cannot work.

    Also when you call Thread.sleep(5000) in these 5s the onReceive of the BroadcastReceiver cannot be called. Because onReceive runs on the main thread. you have to remove Thread.sleep(5000).

    Also you may need premissions ACCESS_FINE_LOCATION and BLUETOOTH_ADMIN. Also you maybe have to set usesPermissionFlags="neverForLocation". Please read the doc.

    Update:
    Request permissions like this (add ACCESS_FINE_LOCATION and BLUETOOTH_CONNECT). Also don’t do a SDK version check (it’s not necessary):

    if (ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
        || ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED
        || ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED)
    {
      ActivityCompat.requestPermissions(getCurrentActivity(), new String[] {
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.BLUETOOTH_CONNECT,
        Manifest.permission.BLUETOOTH_SCAN }, 2);
      return;
    }
    
    Login or Signup to reply.
  2. Have you enabled/added android permissions in the manifest file? requesting to grand a permission and having that permission declared in the manifest file are both important. permissions like BLUETOOTH_SCAN, BLUETOOTH_ADMIN and location permissions like ACCESS_FINE_LOCATION, location permissions are important for discovering devices nearby. Also the the phone’s location and Bluetooth should also be enabled before starting scan. if you want to connect to a device then you need BLUETOOTH_CONNECT,BLUETOOTH_ADVERTISE also.

    I would recommend stop scan on a button press because in most apps you want to scan devices until you connect to one. and scan for a while like 10 to 15 seconds.

    Try putting these permissions in your AndroidManifest.xml file on top after the line where your package name is. and keep the code intended otherwise it won’t work.

        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
        <uses-permission android:name="android.permission.BLUETOOTH"  />
        <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
        <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
        <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
        <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
        <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    
        <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search