dotintent / react-native-ble-plx

React Native BLE library
Apache License 2.0
3.08k stars 515 forks source link

🐛 Scanning devices, it only finds my ble beacon on Android 11 (it doesn't on iOS 17.5.1 and Android 14) #1223

Closed Feffe01 closed 4 months ago

Feffe01 commented 4 months ago

Prerequisites

Expected Behavior

The app finds my ble beacon. When it is found the scan stops and gives a warning.

Current Behavior

The app doesn't find my ble beacon.

Library version

^3.2.0

Device

Android 14 and iOS 17.5.1

Environment info

System:
  OS: macOS 14.5
  CPU: (8) arm64 Apple M2
  Memory: 78.36 MB / 8.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 20.15.0
    path: ~/.nvm/versions/node/v20.15.0/bin/node
  Yarn: Not Found
  npm:
    version: 10.8.1
    path: ~/.nvm/versions/node/v20.15.0/bin/npm
  Watchman:
    version: 2024.06.17.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.5
      - iOS 17.5
      - macOS 14.5
      - tvOS 17.5
      - visionOS 1.2
      - watchOS 10.5
  Android SDK: Not Found
IDEs:
  Android Studio: 2024.1 AI-241.15989.150.2411.11948838
  Xcode:
    version: 15.4/15F31d
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 22.0.1
    path: /usr/bin/javac
  Ruby:
    version: 2.6.10
    path: /usr/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.74.3
    wanted: 0.74.3
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

Steps to reproduce

  1. npx create-expo-app issue --template blank;
  2. cd issue;
  3. npm install react-native-ble-plx;
  4. Copy and paste the code I give you in the app.js;
  5. Change the device.id to find to the one you're using to test (or change it with device.name)
  6. Inside app.json add "plugins": ["react-native-ble-plx"];
  7. npx expo prebuild;
  8. I don't know if I could do this but: go to /android/gradle/wrapper/gradle-wrapper.properties and change distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip to distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip ( I do this because I use Java 22.0.1 );
  9. Connect an iOS device or and Android 14 (I think the problem can be found on Android 12 and 13 too, but I'm not sure);
  10. npx expo run:android or npx expo run:ios --device --configuration Release

Formatted code sample or link to a repository

import { StatusBar } from 'expo-status-bar';
import { useEffect, useState } from 'react';
import { StyleSheet, Text, View, PermissionsAndroid, Platform } from 'react-native';
import { BleManager, ScanMode } from 'react-native-ble-plx';

const manager = new BleManager();

async function requestPermissions() {
  if (Platform.OS === 'android') {
    const apiLevel = Platform.Version;
    const permissions = [
      PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
      PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
      PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
    ];
    const granted = await PermissionsAndroid.requestMultiple(
      apiLevel >= 31 ? permissions : [PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION]
    );

    return (
      granted[PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION] === PermissionsAndroid.RESULTS.GRANTED &&
      (apiLevel < 31 ||
        (granted[PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN] === PermissionsAndroid.RESULTS.GRANTED &&
        granted[PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT] === PermissionsAndroid.RESULTS.GRANTED))
    );
  }
  return true;
}

export default function App() {
  const [text, setText] = useState("Initial state");
    let i = 0;

  const startScan = async () => {
    const permissionGranted = await requestPermissions();
    if (!permissionGranted) {
      setText("Permission not granted");
      return;
    }

    manager.startDeviceScan(
            null,
            {
                allowDuplicates: true,
                scanMode: ScanMode.LowLatency,
            },
            (error, device) => {
      if (error) {
        console.error(error);
        setText(`Error: ${error.message}`);
        return;
      }
            if (device.id === "CD:4E:63:E7:78:63")
            {
                console.warn("found: ", device.id);
                setText("found: ", device.id);
                manager.stopDeviceScan();
            }
            else
            {
                i++;
                console.log(device.id);
            }
            setText("Scanned: " + i);
    });
  };

  useEffect(() => {
    const subscription = manager.onStateChange((state) => {
            if (state === 'PoweredOn') {
                    startScan();
                    subscription.remove();
            }
    }, true);

    return () => {
            subscription.remove();
            manager.stopDeviceScan();
    };
  }, []);

  return (
    <View style={styles.container}>
      <Text>{text}</Text>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

Relevant log output

Don't know how. I'm a beginner, I'm sorry.

Additional information

This app is a simple version of a component I've built on another app. In both apps the scan does start but doesn't find the beacon.id I'm searching. The real problem that is making me crazy and not knowing where to turn, is that on Android 11 both apps find the beacon I'm searching. I know about the permission differences between Android ≤ 11 and Android ≥ 12, and I think this is not the problem because the scan shouldn't start at all if it was.

I've tried different scanMode(s), allowDuplicates: true/false and legacyScan: true/false. No success.

I've tried these apps on Android 11, Android 14 and iOS 17.5.1. The beacon I'm using: amazon.it link

intent-kacper-cyranowski commented 4 months ago

Hi @Feffe01 I did some research and it turns out that beacons won’t work on iOS with this library as it have different API. I’m not sure what’s happening with android 14, you might try https://github.com/Driversnote-Dev/react-native-kontaktio/issues/121#issuecomment-2098884380. Overall I would recommend moving to different library that focuses on beacons like react-native-beacons-manager or react-native-kontaktio

Feffe01 commented 4 months ago

Thank you very much! This solved the problem.

For those having the same problem, the solution is modifying the permission of bluetooth scan like this: <uses-permission android:name="android.permission.BLUETOOTH_SCAN" tools:remove="android:usesPermissionFlags" /> Because without it, android:usesPermissionFlags="neverForLocation" is set causing some beacons not to be found.

In addition, if you have still problems, it's better to set: import { BleManager, ScanMode } from 'react-native-ble-plx'; and { allowDuplicates: true, scanMode: ScanMode.LowLatency, } to the settings of the scan. These allow to scan more beacons but with higher battery consumption.