/*
|
* Copyright (C) 2015 Joe Rogers
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
package com.blakequ.bluetooth_manager_lib.scan.bluetoothcompat;
|
|
import android.annotation.TargetApi;
|
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.le.ScanCallback;
|
import android.bluetooth.le.ScanFilter;
|
import android.bluetooth.le.ScanResult;
|
import android.bluetooth.le.ScanSettings;
|
import android.os.Build;
|
import androidx.annotation.NonNull;
|
import androidx.annotation.Nullable;
|
import androidx.annotation.RequiresPermission;
|
import androidx.collection.SimpleArrayMap;
|
|
import com.blakequ.bluetooth_manager_lib.scan.BluetoothScanManager;
|
|
import java.lang.ref.WeakReference;
|
import java.util.ArrayList;
|
import java.util.List;
|
|
|
public class BluetoothLeScannerCompat {
|
|
private static final String TAG = "BLeScannerCompat";
|
|
@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
|
public static void flushPendingScanResults(@NonNull BluetoothAdapter adapter, @NonNull ScanCallbackCompat callbackCompat) {
|
IMPL.flushPendingScanResults(adapter, callbackCompat);
|
}
|
|
@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
|
public static void startScan(@NonNull BluetoothAdapter adapter, @Nullable List<ScanFilterCompat> filters, @NonNull ScanSettingsCompat settings, @NonNull ScanCallbackCompat callbackCompat) {
|
IMPL.startScan(adapter, filters, settings, callbackCompat);
|
}
|
|
@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
|
public static void startScan(@NonNull BluetoothAdapter adapter, @NonNull ScanCallbackCompat callbackCompat) {
|
IMPL.startScan(adapter, callbackCompat);
|
}
|
|
@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
|
public static void stopScan(@NonNull BluetoothAdapter adapter, @NonNull ScanCallbackCompat callbackCompat) {
|
IMPL.stopScan(adapter, callbackCompat);
|
}
|
|
static final BluetoothLeScannerCompatImpl IMPL;
|
|
static {
|
final int version = Build.VERSION.SDK_INT;
|
if (BluetoothScanManager.isAPI21ScanningDisabled()){
|
IMPL = new API18BluetoothLeScannerCompatImpl();
|
}else{
|
if (version >= Build.VERSION_CODES.LOLLIPOP) {
|
IMPL = new API21BluetoothLeScannerCompatImpl();
|
}else {
|
IMPL = new API18BluetoothLeScannerCompatImpl();
|
}
|
}
|
}
|
|
interface BluetoothLeScannerCompatImpl {
|
void flushPendingScanResults(BluetoothAdapter adapter, ScanCallbackCompat callbackCompat);
|
|
void startScan(BluetoothAdapter adapter, List<ScanFilterCompat> filters, ScanSettingsCompat settings, ScanCallbackCompat callbackCompat);
|
|
void startScan(BluetoothAdapter adapter, ScanCallbackCompat callbackCompat);
|
|
void stopScan(BluetoothAdapter adapter, ScanCallbackCompat callbackCompat);
|
}
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
static class API21BluetoothLeScannerCompatImpl implements BluetoothLeScannerCompatImpl {
|
static final SimpleArrayMap<ScanCallbackCompat, API21ScanCallback> callbackMap = new SimpleArrayMap<>();
|
|
@Override
|
public void flushPendingScanResults(BluetoothAdapter adapter, ScanCallbackCompat callbackCompat) {
|
API21ScanCallback result = callbackMap.get(callbackCompat);
|
if (result == null) {
|
return;
|
}
|
adapter.getBluetoothLeScanner().flushPendingScanResults(result);
|
}
|
|
@Override
|
public void startScan(BluetoothAdapter adapter, List<ScanFilterCompat> filters, ScanSettingsCompat settings, ScanCallbackCompat callbackCompat) {
|
|
List<ScanFilter> scanFilters = null;
|
if (filters != null) {
|
scanFilters = new ArrayList<>(filters.size());
|
|
for (ScanFilterCompat filter : filters) {
|
scanFilters.add(filter.toApi21());
|
}
|
}
|
if (settings == null) {
|
throw new IllegalStateException("Scan settings are null");
|
}
|
ScanSettings scanSettings = settings.toApi21();
|
adapter.getBluetoothLeScanner().startScan(scanFilters, scanSettings, registerCallback(callbackCompat));
|
}
|
|
@Override
|
public void startScan(BluetoothAdapter adapter, ScanCallbackCompat callbackCompat) {
|
adapter.getBluetoothLeScanner().startScan(registerCallback(callbackCompat));
|
}
|
|
@Override
|
public void stopScan(BluetoothAdapter adapter, ScanCallbackCompat callbackCompat) {
|
API21ScanCallback result = callbackMap.remove(callbackCompat);
|
if (result == null) {
|
return;
|
}
|
adapter.getBluetoothLeScanner().stopScan(result);
|
}
|
|
private API21ScanCallback registerCallback(ScanCallbackCompat callbackCompat) {
|
API21ScanCallback result = callbackMap.get(callbackCompat);
|
// Attempting to rescan, just let it fail deeper down.
|
if (result != null) {
|
return result;
|
}
|
result = new API21ScanCallback(callbackCompat);
|
callbackMap.put(callbackCompat, result);
|
return result;
|
}
|
}
|
|
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
|
static class API18BluetoothLeScannerCompatImpl implements BluetoothLeScannerCompatImpl {
|
static final SimpleArrayMap<ScanCallbackCompat, API18ScanCallback> callbackMap = new SimpleArrayMap<>();
|
|
@Override
|
public void flushPendingScanResults(BluetoothAdapter adapter, ScanCallbackCompat callbackCompat) {
|
// no matching api
|
new IllegalArgumentException("this method can not found");
|
}
|
|
|
@SuppressWarnings("deprecation")
|
@Override
|
public void startScan(BluetoothAdapter adapter, List<ScanFilterCompat> filters, ScanSettingsCompat settings, ScanCallbackCompat callbackCompat) {
|
adapter.startLeScan(registerCallback(filters, callbackCompat));
|
}
|
|
@SuppressWarnings("deprecation")
|
@Override
|
public void startScan(BluetoothAdapter adapter, ScanCallbackCompat callbackCompat) {
|
adapter.startLeScan(registerCallback(null, callbackCompat));
|
}
|
|
@SuppressWarnings("deprecation")
|
@Override
|
public void stopScan(BluetoothAdapter adapter, ScanCallbackCompat callbackCompat) {
|
API18ScanCallback callback = callbackMap.remove(callbackCompat);
|
if (callback == null) {
|
return;
|
}
|
adapter.stopLeScan(callback);
|
}
|
|
private API18ScanCallback registerCallback(List<ScanFilterCompat> filters, ScanCallbackCompat callbackCompat) {
|
API18ScanCallback result = callbackMap.get(callbackCompat);
|
// Attempting to rescan, just let it fail deeper down.
|
if (result != null) {
|
return result;
|
}
|
result = new API18ScanCallback(filters, callbackCompat);
|
callbackMap.put(callbackCompat, result);
|
return result;
|
}
|
}
|
|
static class API18ScanCallback implements BluetoothAdapter.LeScanCallback {
|
|
private final List<ScanFilterCompat> filters;
|
private final WeakReference<ScanCallbackCompat> callbackCompatRef;
|
|
API18ScanCallback(List<ScanFilterCompat> filters, ScanCallbackCompat callbackCompat) {
|
this.filters = filters;
|
this.callbackCompatRef = new WeakReference<>(callbackCompat);
|
}
|
|
@Override
|
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
|
ScanCallbackCompat callbackCompat = callbackCompatRef.get();
|
if (callbackCompat == null) return;
|
|
ScanResultCompat result = new ScanResultCompat(
|
device,
|
ScanRecordCompat.parseFromBytes(scanRecord),
|
rssi, System.currentTimeMillis());
|
|
// No filters so return any result
|
if (filters == null) {
|
callbackCompat.onScanResult(ScanSettingsCompat.CALLBACK_TYPE_ALL_MATCHES, result);
|
return;
|
}
|
|
// Filters specified, so see if there is a match.
|
for (ScanFilterCompat filter : filters) {
|
if (filter.matches(result)) {
|
callbackCompat.onScanResult(ScanSettingsCompat.CALLBACK_TYPE_ALL_MATCHES, result);
|
return;
|
}
|
}
|
}
|
}
|
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
static class API21ScanCallback extends ScanCallback {
|
|
private final WeakReference<ScanCallbackCompat> callbackCompatRef;
|
|
API21ScanCallback(ScanCallbackCompat callbackCompat) {
|
this.callbackCompatRef = new WeakReference<>(callbackCompat);
|
}
|
|
@Override
|
public void onScanResult(int callbackType, ScanResult result) {
|
ScanCallbackCompat callbackCompat = callbackCompatRef.get();
|
if (callbackCompat != null) {
|
callbackCompat.onScanResult(callbackType, new ScanResultCompat(result));
|
}
|
}
|
|
@Override
|
public void onBatchScanResults(List<ScanResult> results) {
|
ScanCallbackCompat callbackCompat = callbackCompatRef.get();
|
if (callbackCompat == null) return;
|
List<ScanResultCompat> compatResults = new ArrayList<>(results.size());
|
for (ScanResult result : results) {
|
compatResults.add(new ScanResultCompat(result));
|
}
|
callbackCompat.onBatchScanResults(compatResults);
|
}
|
|
@Override
|
public void onScanFailed(int errorCode) {
|
ScanCallbackCompat callbackCompat = callbackCompatRef.get();
|
if (callbackCompat != null) {
|
callbackCompat.onScanFailed(errorCode);
|
}
|
}
|
}
|
}
|