API Low Bluetooth compatible code> = 21 AND API <21

I am developing an application that should connect to a BLE device. In my code, I want to use the new Scan and ScanCallback for BLE, implemented from API 21 (Android 5), but I have to support compatibility with Android 4.3 and higher.

So, I wrote the code, for example, as follows:

  if (Build.VERSION.SDK_INT >= 21) { mLEScanner.startScan(filters, settings, mScanCallback); } else { btAdapter.startLeScan(leScanCallback); } 

And I defined 2 callbacks, one for API 21 and above, and one for API 18-20:

  //API 21 private ScanCallback mScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { BluetoothDevice btDevice = result.getDevice(); connectToDevice(btDevice); } public void connectToDevice(BluetoothDevice device) { if (mGatt == null) { mGatt = device.connectGatt(context, false, btleGattCallback); if (Build.VERSION.SDK_INT < 21) { btAdapter.stopLeScan(leScanCallback); } else { mLEScanner.stopScan(mScanCallback); } } } }; //API 18 to 20 private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) { btAdapter.stopLeScan(leScanCallback); runOnUiThread(new Runnable() { @Override public void run() { mBluetoothGatt = device.connectGatt(context, false, btleGattCallback); } }); } }; 

I also added annotation

 @TargetApi(21) 

but when I run the application on Android 4.x, it immediately reports an error that the ScanCallback class cannot find (one that is designed to be used only with Android 5 and higher).

How can i solve this?

Many thanks. Daniela

+5
source share
3 answers

After reading a few posts, I did the following. Just in case, here is the Android documentation on BluetoothLe

First

Create two methods: one scanLeDevice21 and scanLeDevice18 . On scanLeDevice21 add the @RequiresApi(21) annotation, which reads:

Indicates that an annotated element should only be called at a given API level or higher. This is similar to the older @TargetApi annotation, but more clearly expresses that this is a requirement for the caller, rather than being used to โ€œsuppressโ€ warnings in a method that exceeds minSdkVersion.

Second

Implement each method, here is my code.

 @RequiresApi(21) private void scanLeDevice21(final boolean enable) { ScanCallback mLeScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); BluetoothDevice bluetoothDevice = result.getDevice(); if (!bluetoothDeviceList.contains(bluetoothDevice)) { Log.d("DEVICE", bluetoothDevice.getName() + "[" + bluetoothDevice.getAddress() + "]"); bluetoothDeviceArrayAdapter.add(bluetoothDevice); bluetoothDeviceArrayAdapter.notifyDataSetChanged(); } } @Override public void onBatchScanResults(List<ScanResult> results) { super.onBatchScanResults(results); } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); } }; final BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); if (enable) { // Stops scanning after a pre-defined scan period. mHandler.postDelayed(() -> { mScanning = false; swipeRefreshLayout.setRefreshing(false); bluetoothLeScanner.stopScan(mLeScanCallback); }, SCAN_PERIOD); mScanning = true; bluetoothLeScanner.startScan(mLeScanCallback); } else { mScanning = false; bluetoothLeScanner.stopScan(mLeScanCallback); } } /** * Scan BLE devices on Android API 18 to 20 * * @param enable Enable scan */ private void scanLeDevice18(boolean enable) { BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice bluetoothDevice, int rssi, byte[] scanRecord) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { bluetoothDeviceArrayAdapter.add(bluetoothDevice); bluetoothDeviceArrayAdapter.notifyDataSetChanged(); } }); } }; if (enable) { // Stops scanning after a pre-defined scan period. mHandler.postDelayed(() -> { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); }, SCAN_PERIOD); mScanning = true; mBluetoothAdapter.startLeScan(mLeScanCallback); } else { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } } 

Third

Every time you need to scan devices, you surround your code asking what version you are. For example, I have a RefreshLayout to display a list of devices. Here is the result:

  /** * Refresh listener */ private void refreshScan() { if (!hasFineLocationPermissions()) { swipeRefreshLayout.setRefreshing(false); //Up to marshmallow you need location permissions to scan bluetooth devices, this method is not here since is up to you to implement it and it is out of scope of this question. requestFineLocationPermission(); } else { swipeRefreshLayout.setRefreshing(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { scanLeDevice21(true); } else { scanLeDevice18(true); } } } 

What is it.

Forget about extensions, subclass classes that you really don't need, like ulusoyca's answer.

+4
source
  • Create the AbstractBluetoothLe and IBleScanCallback . IBleScanCallback - Marker interface. In other words, an interface without methods. You can also add methods for the interface if you need. These methods will perform the same functions for all types of scanCallbacks, i.e. getListOfFoundBleDevices (), clearListOfFoundBleDevices (), etc.
  • Create the BluetootLeLollipop and BluetoothLeJellyBean classes that extend the AbstractBluetoothLe class. Create also the BluetootLeMarshmallow class, which extends the BluetootLeLollipop class. AbstractBluetoothLe class has a mIBleScanCallback protected field, which is an IBleScanCallback object.
  • Create a BleScanCallbackBase class that implements IBleScanCallback .
  • Create a LollipopScanCallback class that extends the ScanCallback class and implements the IBleScanCallback interface .. This class has a ScanCallback protected field that will be created as a BleScanCallbackBase object. Create also the MarshmallowScanCallback class, which extends the LollipopScanCallback class.
  • Create a JellyBeanScanCallback class that extends BleScanCallbackBase and implements BluetoothAdapter.LeScanCallback
  • In BleScanCallbackBase override the method: onScanCallback(...)
  • In LollipoScanCallback redefine onScanResult(int callbackType, ScanResult result) and, inside this method, call the onScanCallback(...) method of the onScanCallback(...) object.
  • In JellyBeanScanCallback override onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) and call onScanCallback(...) inside this method
  • Finally, do whatever you need to do when the device is found in the onScanCallback(...) method of the BleScanCallbackBase class.

Briefly read about composition over inheritance - I know this is not the answer to your question, but it is the exact way of what you want to achieve in the end. Here is the class diagram: enter image description here

+3
source

Your code crashes because it creates an anonymous inner class. Therefore, at runtime, it does not find the sCanCallback class.

Try to do it below and split the result. Before attempting to do this, be sure to comment on the callback (ScanCallback).

  if (Build.VERSION.SDK_INT >= 21) { mLEScanner.startScan(filters, settings, new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { BluetoothDevice btDevice = result.getDevice(); connectToDevice(btDevice); } public void connectToDevice(BluetoothDevice device) { if (mGatt == null) { mGatt = device.connectGatt(context, false, btleGattCallback); if (Build.VERSION.SDK_INT < 21) { btAdapter.stopLeScan(leScanCallback); } else { mLEScanner.stopScan(mScanCallback); } } } }; } else { btAdapter.startLeScan(leScanCallback); } 
0
source

All Articles