Skip to content

Commit

Permalink
Scan filters with mac addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
Zyr00 committed May 4, 2022
1 parent 3cacdf5 commit 580d55a
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 47 deletions.
17 changes: 13 additions & 4 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.boskokg.flutter_blue_plus">
xmlns:tools="http://schemas.android.com/tools"
package="com.boskokg.flutter_blue_plus">

<!-- New Bluetooth permissions in Android 12
https://developer.android.com/about/versions/12/features/bluetooth-permissions
-->
<!-- Include "neverForLocation" only if you can strongly assert that
your app never derives physical location from Bluetooth scan results. -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<!-- Request legacy Bluetooth permissions on older devices. -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import java.util.UUID;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

Expand Down Expand Up @@ -861,21 +862,33 @@ private void startScan21(Protos.ScanSettings proto) throws IllegalStateException
BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
if(scanner == null) throw new IllegalStateException("getBluetoothLeScanner() is null. Is the Adapter on?");
int scanMode = proto.getAndroidScanMode();
int count = proto.getServiceUuidsCount();
List<ScanFilter> filters = new ArrayList<>(count);
for(int i = 0; i < count; i++) {
String uuid = proto.getServiceUuids(i);
ScanFilter f = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(uuid)).build();
List<ScanFilter> filters = fetchFilters(proto);
ScanSettings settings = new ScanSettings.Builder().setScanMode(scanMode).build();
scanner.startScan(filters, settings, getScanCallback21());
}

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private List<ScanFilter> fetchFilters(Protos.ScanSettings proto) {
List<ScanFilter> filters;

int macCount = proto.getMacAddressesCount();
int serviceCount = proto.getServiceUuidsCount();
int count = macCount > 0 ? macCount : serviceCount;
filters = new ArrayList<>(count);

for (int i = 0; i < count; i++) {
ScanFilter f;
if (macCount == count) {
String macAddress = proto.getMacAddresses(i);
f = new ScanFilter.Builder().setDeviceAddress(macAddress).build();
} else {
String uuid = proto.getServiceUuids(i);
f = new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(uuid)).build();
}
filters.add(f);
}
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(scanMode)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
.setReportDelay(0L)
.build();
scanner.startScan(filters, settings, getScanCallback21());

return filters;
}

@TargetApi(21)
Expand Down
8 changes: 8 additions & 0 deletions lib/gen/flutterblueplus.pb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ class ScanSettings extends $pb.GeneratedMessage {
..a<$core.int>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'androidScanMode', $pb.PbFieldType.O3)
..pPS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'serviceUuids')
..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'allowDuplicates')
..pPS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'macAddresses')
..hasRequiredFields = false
;

Expand All @@ -221,6 +222,7 @@ class ScanSettings extends $pb.GeneratedMessage {
$core.int? androidScanMode,
$core.Iterable<$core.String>? serviceUuids,
$core.bool? allowDuplicates,
$core.Iterable<$core.String>? macAddresses,
}) {
final _result = create();
if (androidScanMode != null) {
Expand All @@ -232,6 +234,9 @@ class ScanSettings extends $pb.GeneratedMessage {
if (allowDuplicates != null) {
_result.allowDuplicates = allowDuplicates;
}
if (macAddresses != null) {
_result.macAddresses.addAll(macAddresses);
}
return _result;
}
factory ScanSettings.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
Expand Down Expand Up @@ -275,6 +280,9 @@ class ScanSettings extends $pb.GeneratedMessage {
$core.bool hasAllowDuplicates() => $_has(2);
@$pb.TagNumber(3)
void clearAllowDuplicates() => clearField(3);

@$pb.TagNumber(4)
$core.List<$core.String> get macAddresses => $_getList(3);
}

class ScanResult extends $pb.GeneratedMessage {
Expand Down
3 changes: 2 additions & 1 deletion lib/gen/flutterblueplus.pbjson.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,12 @@ const ScanSettings$json = const {
const {'1': 'android_scan_mode', '3': 1, '4': 1, '5': 5, '10': 'androidScanMode'},
const {'1': 'service_uuids', '3': 2, '4': 3, '5': 9, '10': 'serviceUuids'},
const {'1': 'allow_duplicates', '3': 3, '4': 1, '5': 8, '10': 'allowDuplicates'},
const {'1': 'mac_addresses', '3': 4, '4': 3, '5': 9, '10': 'macAddresses'},
],
};

/// Descriptor for `ScanSettings`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List scanSettingsDescriptor = $convert.base64Decode('CgxTY2FuU2V0dGluZ3MSKgoRYW5kcm9pZF9zY2FuX21vZGUYASABKAVSD2FuZHJvaWRTY2FuTW9kZRIjCg1zZXJ2aWNlX3V1aWRzGAIgAygJUgxzZXJ2aWNlVXVpZHMSKQoQYWxsb3dfZHVwbGljYXRlcxgDIAEoCFIPYWxsb3dEdXBsaWNhdGVz');
final $typed_data.Uint8List scanSettingsDescriptor = $convert.base64Decode('CgxTY2FuU2V0dGluZ3MSKgoRYW5kcm9pZF9zY2FuX21vZGUYASABKAVSD2FuZHJvaWRTY2FuTW9kZRIjCg1zZXJ2aWNlX3V1aWRzGAIgAygJUgxzZXJ2aWNlVXVpZHMSKQoQYWxsb3dfZHVwbGljYXRlcxgDIAEoCFIPYWxsb3dEdXBsaWNhdGVzEiMKDW1hY19hZGRyZXNzZXMYBCADKAlSDG1hY0FkZHJlc3Nlcw==');
@$core.Deprecated('Use scanResultDescriptor instead')
const ScanResult$json = const {
'1': 'ScanResult',
Expand Down
42 changes: 13 additions & 29 deletions lib/src/flutter_blue_plus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@
part of flutter_blue_plus;

class FlutterBluePlus {
final MethodChannel _channel =
const MethodChannel('flutter_blue_plus/methods');
final EventChannel _stateChannel =
const EventChannel('flutter_blue_plus/state');
final StreamController<MethodCall> _methodStreamController =
StreamController.broadcast(); // ignore: close_sinks
Stream<MethodCall> get _methodStream => _methodStreamController
.stream; // Used internally to dispatch methods from platform.
final MethodChannel _channel = const MethodChannel('flutter_blue_plus/methods');
final EventChannel _stateChannel = const EventChannel('flutter_blue_plus/state');
final StreamController<MethodCall> _methodStreamController = StreamController.broadcast(); // ignore: close_sinks
Stream<MethodCall> get _methodStream =>
_methodStreamController.stream; // Used internally to dispatch methods from platform.

/// Singleton boilerplate
FlutterBluePlus._() {
Expand All @@ -31,8 +28,7 @@ class FlutterBluePlus {
LogLevel get logLevel => _logLevel;

/// Checks whether the device supports Bluetooth
Future<bool> get isAvailable =>
_channel.invokeMethod('isAvailable').then<bool>((d) => d);
Future<bool> get isAvailable => _channel.invokeMethod('isAvailable').then<bool>((d) => d);

/// Checks if Bluetooth functionality is turned on
Future<bool> get isOn => _channel.invokeMethod('isOn').then<bool>((d) => d);
Expand Down Expand Up @@ -62,8 +58,7 @@ class FlutterBluePlus {
final BehaviorSubject<bool> _isScanning = BehaviorSubject.seeded(false);
Stream<bool> get isScanning => _isScanning.stream;

final BehaviorSubject<List<ScanResult>> _scanResults =
BehaviorSubject.seeded([]);
final BehaviorSubject<List<ScanResult>> _scanResults = BehaviorSubject.seeded([]);

/// Returns a stream that is a list of [ScanResult] results while a scan is in progress.
///
Expand Down Expand Up @@ -117,12 +112,14 @@ class FlutterBluePlus {
ScanMode scanMode = ScanMode.lowLatency,
List<Guid> withServices = const [],
List<Guid> withDevices = const [],
List<String> macAddresses = const [],
Duration? timeout,
bool allowDuplicates = false,
}) async* {
var settings = protos.ScanSettings.create()
..androidScanMode = scanMode.value
..allowDuplicates = allowDuplicates
..macAddresses.addAll(macAddresses)
..serviceUuids.addAll(withServices.map((g) => g.toString()).toList());

if (_isScanning.value == true) {
Expand Down Expand Up @@ -242,15 +239,7 @@ enum LogLevel {
}

/// State of the bluetooth adapter.
enum BluetoothState {
unknown,
unavailable,
unauthorized,
turningOn,
on,
turningOff,
off
}
enum BluetoothState { unknown, unavailable, unauthorized, turningOn, on, turningOff, off }

class ScanMode {
const ScanMode(this.value);
Expand All @@ -272,8 +261,7 @@ class DeviceIdentifier {
int get hashCode => id.hashCode;

@override
bool operator ==(other) =>
other is DeviceIdentifier && compareAsciiLowerCase(id, other.id) == 0;
bool operator ==(other) => other is DeviceIdentifier && compareAsciiLowerCase(id, other.id) == 0;
}

class ScanResult {
Expand All @@ -288,10 +276,7 @@ class ScanResult {

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ScanResult &&
runtimeType == other.runtimeType &&
device == other.device;
identical(this, other) || other is ScanResult && runtimeType == other.runtimeType && device == other.device;

@override
int get hashCode => device.hashCode;
Expand All @@ -312,8 +297,7 @@ class AdvertisementData {

AdvertisementData.fromProto(protos.AdvertisementData p)
: localName = p.localName,
txPowerLevel =
(p.txPowerLevel.hasValue()) ? p.txPowerLevel.value : null,
txPowerLevel = (p.txPowerLevel.hasValue()) ? p.txPowerLevel.value : null,
connectable = p.connectable,
manufacturerData = p.manufacturerData,
serviceData = p.serviceData,
Expand Down
1 change: 1 addition & 0 deletions protos/flutterblueplus.proto
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ message ScanSettings {
int32 android_scan_mode = 1;
repeated string service_uuids = 2;
bool allow_duplicates = 3;
repeated string mac_addresses = 4;
}

message ScanResult {
Expand Down

0 comments on commit 580d55a

Please sign in to comment.