Reputation: 473
I am using the following code to scan all the nearby BLE devices
here is the code.
package com.btexample;
import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import java.util.ArrayList;
import java.util.List;
public class MyBluetooth extends ReactContextBaseJavaModule implements ActivityEventListener {
private ReactApplicationContext context;
private static final int PERMISSION_REQUEST_CODE = 200;
private static final int REQUEST_ENABLE_BT = 10112;
private String[] permissions = new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN};
private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScanner bluetoothLeScanner;
private Callback scanSuccessCallBack,scanFailedCallBack;
public MyBluetooth(ReactApplicationContext reactContext) {
super(reactContext);
context = reactContext;
bluetoothManager =
(BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
}
@NonNull
@Override
public String getName() {
return "MyBluetooth";
}
@ReactMethod
public void turnOnBluetooth() {
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
getCurrentActivity().startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
private WritableMap convertJsonToMap(String name) {
WritableMap map = new WritableNativeMap();
map.putString("device_name", name);
return map;
}
@ReactMethod
private void requestGPSPermissions() {
boolean permissionCheck =
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&&
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED;
if (permissionCheck) {
ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION
},
PERMISSION_REQUEST_CODE);
}
}
private boolean mScanning = false,isInvoked;
private Handler handler;
@ReactMethod
public void scanLeDevice(int scanSeconds,Callback scanSuccessCallBack,Callback scanFailedCallBack) {
if (!mScanning) {
// Stops scanning after a pre-defined scan period.
this.scanSuccessCallBack = scanSuccessCallBack;
this.scanFailedCallBack = scanFailedCallBack;
isInvoked = false;
handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if(mScanning) {
bluetoothLeScanner.stopScan(leScanCallback);
mScanning = false;
if(!isInvoked){
scanFailedCallBack.invoke("No results found");
}
}
}
}, scanSeconds*1000);
mScanning = true;
List<ScanFilter> filters = new ArrayList<ScanFilter>();
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(1000)
.build();
bluetoothLeScanner.startScan(filters, settings, leScanCallback);
} else {
mScanning = false;
bluetoothLeScanner.stopScan(leScanCallback);
scanFailedCallBack.invoke("Already scanning");
}
}
private ScanCallback leScanCallback = new ScanCallback() {
@Override
public void onBatchScanResults(List<ScanResult> results) {
if(results.size() > 0 && mScanning) {
mScanning = false;
if(!isInvoked){
scanSuccessCallBack.invoke("results found");
isInvoked = true;
bluetoothLeScanner.stopScan(leScanCallback);
handler.removeCallbacksAndMessages(null);
}
}
}
@Override
public void onScanFailed(int errorCode) {
scanFailedCallBack.invoke("Scan failed");
}
};
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
}
@Override
public void onNewIntent(Intent intent) {
}
}
Now,
As of now I don't have any peripheral device so I am using an app as a simulatore link: https://play.google.com/store/apps/details?id=com.ble.peripheral.sim&hl=en_IN&gl=US
Now Scanning with another App https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner&hl=en_IN&gl=US on another device I am receiving correct results
Now scanning with my app again receiving the results.
Now the main problem is.
when I turn off the simulator app and also uninstall it, I am still getting it as the result in my app but not in that scanner App.
Also my app is receiving random devices with deviceName=null (also a lot of things are null) and these random devices don't Appear in that Scanner App.
when I remove the filters and the settings from starScan();
e.g.
bluetoothLeScanner.startScan(filters, settings, leScanCallback);
the call back for onBatchScanResults() never recived.
NOTE : I am checking the results in the debugger of android studio
Please Help me thanks in Advance
Upvotes: 1
Views: 749
Reputation: 473
I am not sure what exactly worked for me but these are the things I have changed.
Added one more permission in both manifests and asked at run time ACCESS_BACKGROUND_LOCATION
stoped using android.bluetooth.le and now I am using this library to do so https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library codes for both are same we just have to import classes from this package.
In the question I forgot to mention that I was not getting the callback of onScanResult and Even after doing this I was not able to get the scan results
so I just removed the scan filter and scan settings from startScan and then used onScanResult to get the devices.
Here is the final code
import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import java.util.ArrayList;
import java.util.List;
import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
import no.nordicsemi.android.support.v18.scanner.ScanCallback;
import no.nordicsemi.android.support.v18.scanner.ScanFilter;
import no.nordicsemi.android.support.v18.scanner.ScanResult;
import no.nordicsemi.android.support.v18.scanner.ScanSettings;
public class BleModule extends ReactContextBaseJavaModule {
private ReactApplicationContext context;
private static final int PERMISSION_REQUEST_CODE = 200;
private static final int REQUEST_ENABLE_BT = 10112;
private BluetoothManager bluetoothManager;
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScannerCompat scanner;
private Callback scanFailedCallBack;
private LocationManager lm;
/**
* constructor to initialize other important instances with the received context
*
* @param reactContext
*/
public BleModule(ReactApplicationContext reactContext) {
super(reactContext);
context = reactContext;
bluetoothManager =
(BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
bluetoothAdapter = bluetoothManager.getAdapter();
scanner = BluetoothLeScannerCompat.getScanner();
lm = (LocationManager) context.getSystemService(context.LOCATION_SERVICE);
}
/**
* overriding the method to return a name of module as string to be used inside react native codes to use this module.
* this name should be same in both android and iOS
*
* @return
*/
@NonNull
@Override
public String getName() {
return "BleModule";
}
/**
* this method is to check if the GPS is enabled and
* invoking the react native callback with true or false
*
* @param callback
*/
@ReactMethod
public void checkGPS(Callback callback) {
if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER))
callback.invoke(false);
else
callback.invoke(true);
}
/**
* this method is to launch GPS settings to enable
*/
@ReactMethod
public void enableGPS() {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
getCurrentActivity().startActivity(intent);
}
/**
* this method is launch the bluetooth enable dialog.
*/
@ReactMethod
public void turnOnBluetooth() {
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
getCurrentActivity().startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
/**
* this method is to ask the Location permission from the user
*/
@ReactMethod
private void requestGPSPermissions() {
boolean permissionCheck =
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&&
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
&&
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
!= PackageManager.PERMISSION_GRANTED;
if (permissionCheck) {
ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION
},
PERMISSION_REQUEST_CODE);
}
}
private boolean mScanning = false, isInvoked;
private Handler handler;
private WritableArray reactResults;
private ArrayList<String> duplicateChecker = new ArrayList<>();
/**
* this method is to scan the nearby ble devices
*
* @param scanSeconds to run the scan till.
* @param scanSuccessCallBack to be invoked with the result of the detected devices.
* @param scanFailedCallBack to be invoked when any failure occurs with the reason as a string.
*/
@ReactMethod
public void scanLeDevice(int scanSeconds, Callback scanSuccessCallBack, Callback scanFailedCallBack) {
if (!mScanning) {
reactResults = new WritableNativeArray();
duplicateChecker.clear();
// Stops scanning after a pre-defined scan period.
this.scanFailedCallBack = scanFailedCallBack;
isInvoked = false;
handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (mScanning) {
scanner.stopScan(leScanCallback);
mScanning = false;
if (reactResults.size() > 0 )
scanSuccessCallBack.invoke(reactResults);
else scanFailedCallBack.invoke("No results found");
}
}
}, scanSeconds * 1000);
mScanning = true;
scanner.startScan(leScanCallback);
} else {
mScanning = false;
scanner.stopScan(leScanCallback);
scanFailedCallBack.invoke("Already scanning");
}
}
/**
* this ScanCallback instance overriding two methods
* <p>
* this object is passed in startScan method inside the scanLeDevice method.
*/
private ScanCallback leScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, @NonNull ScanResult device) {
String address = device.getDevice().getAddress();
if(duplicateChecker.contains(address))
return;
else duplicateChecker.add(address);
WritableMap map = new WritableNativeMap();
if (device.getDevice() != null)
map.putString("device_name", device.getDevice().getName());
else map.putString("device_name", device.getScanRecord().getDeviceName());
map.putString("device_address", address);
reactResults.pushMap(map);
}
/**
* this will be triggered if the scan failed. then trigger react native's scanFailedCallBack.
* @param errorCode
*/
@Override
public void onScanFailed(int errorCode) {
scanFailedCallBack.invoke("Scan failed");
}
};
}
Upvotes: 0
Reputation: 504
1-2. As soon as your scan filters are empty you will get all the scan results, so it could be that in this app there is different scan filters. Because of that you can get different scan results.
I think you start receiving scan results in callback method onScanResult
, which is not realised in your code.
It could be some BLE devices around you - like laptops, mouses, TVs, etc.
Upvotes: 2