Implement ScanFilter (#17)

* Implement ScanFilter

* Add windows implementation

* Fix Linux and cleanups

* Update Readme, Changelog and Version

* Minor fixes from AI comments

* Fix windows crash and update pigeon

* Fix UUID parsing

* Apply suggestions from code review

Co-authored-by: Foti Dim <foti@navideck.com>

---------

Co-authored-by: Foti Dim <foti@navideck.com>
This commit is contained in:
Rohit Sangwan
2024-03-05 22:37:14 +05:30
committed by GitHub
parent 7e47be374b
commit 1f7ba287ef
28 changed files with 546 additions and 118 deletions
+3 -1
View File
@@ -1,5 +1,6 @@
{
"cSpell.words": [
"bluez",
"BTLE",
"bytevc",
"connnection",
@@ -25,7 +26,8 @@
"unwatch",
"uuidstr",
"winrt",
"WPARAM"
"WPARAM",
"ZUUID"
],
"cmake.configureOnOpen": false,
"files.associations": {
+5
View File
@@ -1,3 +1,8 @@
## 0.9.3
* Add scan filter (withServices:) in `startScan()`
* Add service UUIDs from advertisements in `BleScanResult`
* Fix Windows release build compilation
## 0.9.2
* Add command queue
* Improve error handling on Android, iOS, macOS, Windows, Linux and web
+7
View File
@@ -60,6 +60,13 @@ UniversalBle.onScanResult = (scanResult) {
// Perform a scan
UniversalBle.startScan();
// Or optionally add a scan filter
UniversalBle.startScan(
scanFilter: ScanFilter(
withServices: ["SERVICE_UUID"],
)
);
// Stop scanning
UniversalBle.stopScan();
```
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v16.0.5), do not edit directly.
// Autogenerated from Pigeon (v17.1.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
package com.navideck.universal_ble
@@ -53,7 +53,8 @@ data class UniversalBleScanResult (
val isPaired: Boolean? = null,
val rssi: Long? = null,
val manufacturerData: ByteArray? = null,
val manufacturerDataHead: ByteArray? = null
val manufacturerDataHead: ByteArray? = null,
val services: List<String?>? = null
) {
companion object {
@@ -65,7 +66,8 @@ data class UniversalBleScanResult (
val rssi = list[3].let { if (it is Int) it.toLong() else it as Long? }
val manufacturerData = list[4] as ByteArray?
val manufacturerDataHead = list[5] as ByteArray?
return UniversalBleScanResult(deviceId, name, isPaired, rssi, manufacturerData, manufacturerDataHead)
val services = list[6] as List<String?>?
return UniversalBleScanResult(deviceId, name, isPaired, rssi, manufacturerData, manufacturerDataHead, services)
}
}
fun toList(): List<Any?> {
@@ -76,6 +78,7 @@ data class UniversalBleScanResult (
rssi,
manufacturerData,
manufacturerDataHead,
services,
)
}
}
@@ -124,6 +127,25 @@ data class UniversalBleCharacteristic (
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class UniversalScanFilter (
val withServices: List<String?>
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): UniversalScanFilter {
val withServices = list[0] as List<String?>
return UniversalScanFilter(withServices)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
withServices,
)
}
}
@Suppress("UNCHECKED_CAST")
private object UniversalBlePlatformChannelCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
@@ -143,6 +165,11 @@ private object UniversalBlePlatformChannelCodec : StandardMessageCodec() {
UniversalBleService.fromList(it)
}
}
131.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalScanFilter.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
@@ -160,6 +187,10 @@ private object UniversalBlePlatformChannelCodec : StandardMessageCodec() {
stream.write(130)
writeValue(stream, value.toList())
}
is UniversalScanFilter -> {
stream.write(131)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@@ -173,7 +204,7 @@ private object UniversalBlePlatformChannelCodec : StandardMessageCodec() {
interface UniversalBlePlatformChannel {
fun getBluetoothAvailabilityState(callback: (Result<Long>) -> Unit)
fun enableBluetooth(callback: (Result<Boolean>) -> Unit)
fun startScan()
fun startScan(filter: UniversalScanFilter?)
fun stopScan()
fun connect(deviceId: String)
fun disconnect(deviceId: String)
@@ -234,10 +265,12 @@ interface UniversalBlePlatformChannel {
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val filterArg = args[0] as UniversalScanFilter?
var wrapped: List<Any?>
try {
api.startScan()
api.startScan(filterArg)
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
@@ -13,7 +13,9 @@ import android.bluetooth.le.ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED
import android.bluetooth.le.ScanCallback.SCAN_FAILED_INTERNAL_ERROR
import android.bluetooth.le.ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES
import android.bluetooth.le.ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
import android.os.ParcelUuid
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder
@@ -61,6 +63,15 @@ enum class CharacteristicProperty(val value: Long) {
ExtendedProperties(7)
}
fun String.validFullUUID(): String {
return when (this.count()) {
4 -> "0000$this-0000-1000-8000-00805F9B34FB"
8 -> "$this-0000-1000-8000-00805F9B34FB"
else -> this
}
}
fun String.toBluetoothGatt(): BluetoothGatt {
return knownGatts.find { it.device.address == this }
?: throw FlutterError("IllegalArgument", "Unknown deviceId: $this", null)
@@ -132,6 +143,32 @@ fun Int.parseGattErrorCode(): String? {
}
}
fun UniversalScanFilter.toScanFilters(): List<ScanFilter> {
var scanFilters: List<ScanFilter> = emptyList()
// Add withServices Filter
for (service in this.withServices) {
try {
val serviceUUID = service?.validFullUUID()
serviceUUID?.let {
val parcelUUId = ParcelUuid.fromString(it)
scanFilters = scanFilters.plus(
ScanFilter.Builder().setServiceUuid(parcelUUId).build()
)
Log.e(TAG, "scanFilters: $parcelUUId")
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
throw FlutterError(
"illegalIllegalArgument",
"Invalid serviceId: $service",
e.toString()
)
}
}
return scanFilters
}
val ScanResult.manufacturerDataHead: ByteArray?
get() {
@@ -6,7 +6,9 @@ import android.bluetooth.*
import android.bluetooth.BluetoothDevice.BOND_BONDED
import android.bluetooth.BluetoothDevice.BOND_NONE
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Context.RECEIVER_EXPORTED
@@ -15,6 +17,7 @@ import android.content.IntentFilter
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.ParcelUuid
import android.util.Log
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
@@ -24,6 +27,7 @@ import java.util.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
private const val TAG = "UniversalBlePlugin"
@SuppressLint("MissingPermission")
@@ -95,12 +99,24 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
bluetoothEnableRequestFuture = callback
}
override fun startScan() {
override fun startScan(filter: UniversalScanFilter?) {
if (!isBluetoothAvailable()) throw FlutterError(
"BluetoothNotEnabled",
"Bluetooth not enabled",
)
bluetoothManager.adapter.bluetoothLeScanner?.startScan(scanCallback)
val builder = ScanSettings.Builder()
if (Build.VERSION.SDK_INT >= 26) {
builder.setPhy(ScanSettings.PHY_LE_ALL_SUPPORTED)
builder.setLegacy(false)
}
val settings = builder.build()
bluetoothManager.adapter.bluetoothLeScanner?.startScan(
filter?.toScanFilters() ?: emptyList<ScanFilter>(),
settings,
scanCallback
)
}
override fun stopScan() {
@@ -656,7 +672,9 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
deviceId = result.device.address,
isPaired = result.device.bondState == BOND_BONDED,
manufacturerDataHead = result.manufacturerDataHead,
rssi = result.rssi.toLong()
rssi = result.rssi.toLong(),
services = result.device.uuids?.map { it.uuid?.toString() ?: "" }
?: emptyList()
)
) {}
}
+34 -5
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v16.0.5), do not edit directly.
// Autogenerated from Pigeon (v17.1.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
import Foundation
@@ -51,6 +51,7 @@ struct UniversalBleScanResult {
var rssi: Int64? = nil
var manufacturerData: FlutterStandardTypedData? = nil
var manufacturerDataHead: FlutterStandardTypedData? = nil
var services: [String?]? = nil
static func fromList(_ list: [Any?]) -> UniversalBleScanResult? {
let deviceId = list[0] as! String
@@ -59,6 +60,7 @@ struct UniversalBleScanResult {
let rssi: Int64? = isNullish(list[3]) ? nil : (list[3] is Int64? ? list[3] as! Int64? : Int64(list[3] as! Int32))
let manufacturerData: FlutterStandardTypedData? = nilOrValue(list[4])
let manufacturerDataHead: FlutterStandardTypedData? = nilOrValue(list[5])
let services: [String?]? = nilOrValue(list[6])
return UniversalBleScanResult(
deviceId: deviceId,
@@ -66,7 +68,8 @@ struct UniversalBleScanResult {
isPaired: isPaired,
rssi: rssi,
manufacturerData: manufacturerData,
manufacturerDataHead: manufacturerDataHead
manufacturerDataHead: manufacturerDataHead,
services: services
)
}
func toList() -> [Any?] {
@@ -77,6 +80,7 @@ struct UniversalBleScanResult {
rssi,
manufacturerData,
manufacturerDataHead,
services,
]
}
}
@@ -125,6 +129,24 @@ struct UniversalBleCharacteristic {
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalScanFilter {
var withServices: [String?]
static func fromList(_ list: [Any?]) -> UniversalScanFilter? {
let withServices = list[0] as! [String?]
return UniversalScanFilter(
withServices: withServices
)
}
func toList() -> [Any?] {
return [
withServices
]
}
}
private class UniversalBlePlatformChannelCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? {
switch type {
@@ -134,6 +156,8 @@ private class UniversalBlePlatformChannelCodecReader: FlutterStandardReader {
return UniversalBleScanResult.fromList(self.readValue() as! [Any?])
case 130:
return UniversalBleService.fromList(self.readValue() as! [Any?])
case 131:
return UniversalScanFilter.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
}
@@ -151,6 +175,9 @@ private class UniversalBlePlatformChannelCodecWriter: FlutterStandardWriter {
} else if let value = value as? UniversalBleService {
super.writeByte(130)
super.writeValue(value.toList())
} else if let value = value as? UniversalScanFilter {
super.writeByte(131)
super.writeValue(value.toList())
} else {
super.writeValue(value)
}
@@ -177,7 +204,7 @@ class UniversalBlePlatformChannelCodec: FlutterStandardMessageCodec {
protocol UniversalBlePlatformChannel {
func getBluetoothAvailabilityState(completion: @escaping (Result<Int64, Error>) -> Void)
func enableBluetooth(completion: @escaping (Result<Bool, Error>) -> Void)
func startScan() throws
func startScan(filter: UniversalScanFilter?) throws
func stopScan() throws
func connect(deviceId: String) throws
func disconnect(deviceId: String) throws
@@ -230,9 +257,11 @@ class UniversalBlePlatformChannelSetup {
}
let startScanChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
startScanChannel.setMessageHandler { _, reply in
startScanChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let filterArg: UniversalScanFilter? = nilOrValue(args[0])
do {
try api.startScan()
try api.startScan(filter: filterArg)
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
+14
View File
@@ -141,6 +141,20 @@ public extension CBPeripheral {
}
}
extension String {
var validFullUUID: String {
let uuidLength = self.count
if uuidLength == 4 || uuidLength == 8 {
let baseUuid = "00000000-0000-1000-8000-00805F9B34FB"
let start = baseUuid.startIndex
let range = baseUuid.index(start, offsetBy: 4-uuidLength)..<baseUuid.index(start, offsetBy: 4)
return baseUuid.replacingCharacters(in: range, with: self)
} else {
return self
}
}
}
// Future classes
class CharacteristicReadFuture {
let deviceId: String
+17 -3
View File
@@ -56,8 +56,20 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
completion(Result.failure(FlutterError(code: "NotSupported", message: nil, details: nil)))
}
func startScan() throws {
manager.scanForPeripherals(withServices: nil)
func startScan(filter: UniversalScanFilter?) throws {
// Apply services filter
var withServices: [CBUUID] = []
for service in filter?.withServices ?? [] {
if let service = service {
if UUID(uuidString: service.validFullUUID) == nil {
throw FlutterError(code: "IllegalArgument", message: "Invalid service UUID:\(service)", details: nil)
}
withServices.append(CBUUID(string: service))
}
}
manager.scanForPeripherals(withServices: withServices)
}
func stopScan() throws {
@@ -228,12 +240,14 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
public func centralManager(_: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
discoveredPeripherals[peripheral.uuid.uuidString] = peripheral
let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data
let services = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID]
callbackChannel.onScanResult(result: UniversalBleScanResult(
deviceId: peripheral.uuid.uuidString,
name: peripheral.name,
isPaired: nil,
rssi: RSSI as? Int64,
manufacturerData: FlutterStandardTypedData(bytes: manufacturerData ?? Data())
manufacturerData: FlutterStandardTypedData(bytes: manufacturerData ?? Data()),
services: services?.map { $0.uuidStr }
)) { _ in }
}
+4 -1
View File
@@ -23,7 +23,10 @@ class MockUniversalBle extends UniversalBlePlatform {
]);
@override
Future<void> startScan({WebRequestOptionsBuilder? webRequestOptions}) async {
Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
ScanFilter? scanFilter,
}) async {
onScanResult?.call(_mockBleScanResult);
}
+4 -1
View File
@@ -62,6 +62,7 @@ class _MyAppState extends State<MyApp> {
};
UniversalBle.onScanResult = (result) {
// debugPrint("ScanResult: ${result.name} ${result.services}");
// debugPrint("${result.name} ${result.manufacturerData}");
int index = _scanResults.indexWhere((e) => e.deviceId == result.deviceId);
if (index == -1) {
@@ -110,7 +111,9 @@ class _MyAppState extends State<MyApp> {
});
try {
await UniversalBle.startScan(
webRequestOptions: _requestOptions);
webRequestOptions: _requestOptions,
// scanFilter: ScanFilter(withServices: ['180f']),
);
} catch (e) {
setState(() {
_isScanning = false;
+2
View File
@@ -7,6 +7,7 @@ class BleScanResult {
Uint8List? manufacturerDataHead;
Uint8List? manufacturerData;
int? rssi;
List<String> services;
BleScanResult({
required this.name,
@@ -15,6 +16,7 @@ class BleScanResult {
this.isPaired,
Uint8List? manufacturerData,
Uint8List? manufacturerDataHead,
this.services = const [],
}) {
this.manufacturerDataHead = manufacturerDataHead ?? Uint8List.fromList([]);
this.manufacturerData = manufacturerData ?? manufacturerDataHead;
+2
View File
@@ -1,3 +1,5 @@
export 'package:universal_ble/src/models/uuid.dart';
export 'package:universal_ble/src/models/scan_filter.dart';
export 'package:universal_ble/src/models/web_request_options_builder.dart';
export 'package:universal_ble/src/models/ble_property.dart';
export 'package:universal_ble/src/models/ble_service.dart';
+7
View File
@@ -0,0 +1,7 @@
class ScanFilter {
final List<String> withServices;
ScanFilter({
this.withServices = const [],
});
}
+34
View File
@@ -0,0 +1,34 @@
import 'package:bluez/bluez.dart';
class UUID {
late String value;
UUID(String uuid) {
// To validate the short UUID
if (uuid.length <= 4) {
try {
int shortValue = int.parse(uuid, radix: 16);
value = BlueZUUID.short(shortValue).toString();
return;
} catch (_) {}
}
// To validate the UUID
BlueZUUID.fromString(uuid);
value = uuid;
}
factory UUID.fromShort(int short) {
BlueZUUID blueZUUID = BlueZUUID.short(short);
return UUID(blueZUUID.toString());
}
@override
String toString() {
return value;
}
}
/// Parse a list of strings to a list of UUIDs
extension StringListToUUID on List<String> {
List<String> toValidUUIDList() => map((e) => UUID(e).value).toList();
}
+29 -15
View File
@@ -1,20 +1,21 @@
import 'dart:typed_data';
import 'package:flutter_web_bluetooth/flutter_web_bluetooth.dart';
import 'package:universal_ble/universal_ble.dart';
class WebRequestOptionsBuilder {
final bool _acceptAllDevices;
final List<WebRequestFilterBuilder> _requestFilters;
final List<WebRequestFilterBuilder>? _exclusionFilters;
final List<String>? _optionalServices;
List<String>? _optionalServices;
WebRequestOptionsBuilder(
List<WebRequestFilterBuilder> requestFilters, {
List<WebRequestFilterBuilder>? exclusionFilters,
List<String>? optionalServices,
}) : _requestFilters = requestFilters,
}) : _optionalServices = optionalServices,
_requestFilters = requestFilters,
_exclusionFilters = exclusionFilters,
_acceptAllDevices = false,
_optionalServices = optionalServices {
_acceptAllDevices = false {
if (_requestFilters.isEmpty) {
throw StateError(
'No filters have been set, consider using '
@@ -26,25 +27,38 @@ class WebRequestOptionsBuilder {
/// To accept all devices
WebRequestOptionsBuilder.acceptAllDevices({
List<String>? optionalServices,
}) : _acceptAllDevices = true,
}) : _optionalServices = optionalServices,
_acceptAllDevices = true,
_requestFilters = [],
_exclusionFilters = null,
_optionalServices = optionalServices;
_exclusionFilters = null;
/// For internal use
/// To convert UniversalBleRequestOptions to FlutterWebBluetoothRequestOptions
RequestOptionsBuilder toRequestOptionsBuilder() {
RequestOptionsBuilder toRequestOptionsBuilder({ScanFilter? scanFilter}) {
if (scanFilter != null) _applyScanFilter(scanFilter);
if (_acceptAllDevices) {
return RequestOptionsBuilder.acceptAllDevices(
optionalServices: _optionalServices,
);
} else {
return RequestOptionsBuilder(
_requestFilters.map((e) => e.getRequestFilterBuilder()).toList(),
exclusionFilters:
_exclusionFilters?.map((e) => e.getRequestFilterBuilder()).toList(),
optionalServices: _optionalServices,
);
}
return RequestOptionsBuilder(
_requestFilters.map((e) => e.getRequestFilterBuilder()).toList(),
exclusionFilters:
_exclusionFilters?.map((e) => e.getRequestFilterBuilder()).toList(),
optionalServices: _optionalServices,
);
}
/// To apply scan filter
void _applyScanFilter(ScanFilter scanFilter) {
List<String> filterServices = scanFilter.withServices;
_optionalServices ??= filterServices.toValidUUIDList();
for (var requestFilter in _requestFilters) {
// Apply services filter
requestFilter.services ??= filterServices.toValidUUIDList();
}
}
+6 -1
View File
@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:universal_ble/src/models/scan_filter.dart';
import 'package:universal_ble/src/universal_ble_linux/universal_ble_linux.dart';
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart';
import 'package:universal_ble/src/universal_ble_web/universal_ble_web.dart';
@@ -45,9 +46,13 @@ class UniversalBle {
/// `webRequestOptions` supported on Web only
static Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
ScanFilter? scanFilter,
}) async {
return await _executeCommand(
() => _platform.startScan(webRequestOptions: webRequestOptions),
() => _platform.startScan(
webRequestOptions: webRequestOptions,
scanFilter: scanFilter,
),
timeout: null,
);
}
@@ -55,9 +55,14 @@ class UniversalBleLinux extends UniversalBlePlatform {
@override
Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
ScanFilter? scanFilter,
}) async {
await _ensureInitialized();
if (_activeAdapter?.discovering != true) {
// Add services filter
_activeAdapter?.setDiscoveryFilter(
uuids: scanFilter?.withServices.toValidUUIDList(),
);
await _activeAdapter?.startDiscovery();
_client.devices.forEach(_onDeviceAdd);
}
@@ -455,11 +460,8 @@ extension BlueZDeviceExtension on BlueZDevice {
int companyId = sorted.first.key.id;
List<int> manufacturerDataValue = sorted.first.value;
var byteData = ByteData(2);
byteData.setInt16(
0,
companyId,
Endian
.host); // TODO: Verify that this works regardless of the endianess
// TODO: Verify that this works regardless of the endianess
byteData.setInt16(0, companyId, Endian.host);
List<int> bytes = byteData.buffer.asUint8List();
return Uint8List.fromList(bytes + manufacturerDataValue);
} catch (e) {
@@ -479,6 +481,7 @@ extension BlueZDeviceExtension on BlueZDevice {
manufacturerData: manufacturerDataHead,
manufacturerDataHead: manufacturerDataHead,
rssi: rssi,
services: uuids.map((e) => e.toString()).toList(),
);
}
}
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v16.0.5), do not edit directly.
// Autogenerated from Pigeon (v17.1.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
@@ -33,6 +33,7 @@ class UniversalBleScanResult {
this.rssi,
this.manufacturerData,
this.manufacturerDataHead,
this.services,
});
String deviceId;
@@ -47,6 +48,8 @@ class UniversalBleScanResult {
Uint8List? manufacturerDataHead;
List<String?>? services;
Object encode() {
return <Object?>[
deviceId,
@@ -55,6 +58,7 @@ class UniversalBleScanResult {
rssi,
manufacturerData,
manufacturerDataHead,
services,
];
}
@@ -67,6 +71,7 @@ class UniversalBleScanResult {
rssi: result[3] as int?,
manufacturerData: result[4] as Uint8List?,
manufacturerDataHead: result[5] as Uint8List?,
services: (result[6] as List<Object?>?)?.cast<String?>(),
);
}
}
@@ -123,6 +128,27 @@ class UniversalBleCharacteristic {
}
}
class UniversalScanFilter {
UniversalScanFilter({
required this.withServices,
});
List<String?> withServices;
Object encode() {
return <Object?>[
withServices,
];
}
static UniversalScanFilter decode(Object result) {
result as List<Object?>;
return UniversalScanFilter(
withServices: (result[0] as List<Object?>?)!.cast<String?>(),
);
}
}
class _UniversalBlePlatformChannelCodec extends StandardMessageCodec {
const _UniversalBlePlatformChannelCodec();
@override
@@ -136,6 +162,9 @@ class _UniversalBlePlatformChannelCodec extends StandardMessageCodec {
} else if (value is UniversalBleService) {
buffer.putUint8(130);
writeValue(buffer, value.encode());
} else if (value is UniversalScanFilter) {
buffer.putUint8(131);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
@@ -150,6 +179,8 @@ class _UniversalBlePlatformChannelCodec extends StandardMessageCodec {
return UniversalBleScanResult.decode(readValue(buffer)!);
case 130:
return UniversalBleService.decode(readValue(buffer)!);
case 131:
return UniversalScanFilter.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
}
@@ -221,7 +252,7 @@ class UniversalBlePlatformChannel {
}
}
Future<void> startScan() async {
Future<void> startScan(UniversalScanFilter? filter) async {
const String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan';
final BasicMessageChannel<Object?> __pigeon_channel = BasicMessageChannel<Object?>(
__pigeon_channelName,
@@ -229,7 +260,7 @@ class UniversalBlePlatformChannel {
binaryMessenger: __pigeon_binaryMessenger,
);
final List<Object?>? __pigeon_replyList =
await __pigeon_channel.send(null) as List<Object?>?;
await __pigeon_channel.send(<Object?>[filter]) as List<Object?>?;
if (__pigeon_replyList == null) {
throw _createConnectionError(__pigeon_channelName);
} else if (__pigeon_replyList.length > 1) {
@@ -30,8 +30,12 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
@override
Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
}) =>
_channel.startScan();
ScanFilter? scanFilter,
}) async {
await _channel.startScan(
scanFilter.toUniversalScanFilter(),
);
}
@override
Future<void> stopScan() => _channel.stopScan();
@@ -192,6 +196,23 @@ extension _UniversalBleScanResultExtension on UniversalBleScanResult {
manufacturerData: mnfData,
manufacturerDataHead: mnfDataHead,
rssi: rssi,
services: services
?.where((e) => e != null)
.map((e) => UUID(e!).toString())
.toList() ??
[],
);
}
}
extension _ScanFilterExtension on ScanFilter? {
UniversalScanFilter? toUniversalScanFilter() {
// Windows crashes if it's null, so we need to pass empty scan filter in this case
if (this == null && Platform.isWindows) {
return UniversalScanFilter(withServices: []);
}
return UniversalScanFilter(
withServices: this?.withServices.toValidUUIDList() ?? [],
);
}
}
@@ -1,6 +1,5 @@
import 'dart:developer';
import 'dart:typed_data';
import 'package:universal_ble/universal_ble.dart';
abstract class UniversalBlePlatform {
@@ -10,6 +9,7 @@ abstract class UniversalBlePlatform {
Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
ScanFilter? scanFilter,
});
Future<void> stopScan();
@@ -110,10 +110,21 @@ class UniversalBleWeb extends UniversalBlePlatform {
@override
Future<void> startScan({
WebRequestOptionsBuilder? webRequestOptions,
ScanFilter? scanFilter,
}) async {
RequestOptionsBuilder requestFilterBuilder;
if (webRequestOptions != null) {
requestFilterBuilder = webRequestOptions.toRequestOptionsBuilder(
scanFilter: scanFilter,
);
} else if (scanFilter != null) {
requestFilterBuilder = scanFilter.toRequestOptionsBuilder();
} else {
requestFilterBuilder = RequestOptionsBuilder.acceptAllDevices();
}
BluetoothDevice device = await FlutterWebBluetooth.instance.requestDevice(
webRequestOptions?.toRequestOptionsBuilder() ??
RequestOptionsBuilder.acceptAllDevices(),
requestFilterBuilder,
);
// Update local device list
@@ -137,6 +148,7 @@ class UniversalBleWeb extends UniversalBlePlatform {
device.toBleScanResult(
rssi: event.rssi,
manufacturerDataMap: event.manufacturerData,
services: event.uuids,
),
);
});
@@ -346,6 +358,7 @@ extension _BluetoothDeviceExtension on BluetoothDevice {
BleScanResult toBleScanResult({
int? rssi,
UnmodifiableMapView<int, ByteData>? manufacturerDataMap,
List<String> services = const [],
}) {
return BleScanResult(
name: name,
@@ -353,6 +366,7 @@ extension _BluetoothDeviceExtension on BluetoothDevice {
manufacturerData: manufacturerDataMap?.toUint8List(),
manufacturerDataHead: manufacturerDataMap?.toUint8List(),
rssi: rssi,
services: services,
);
}
}
@@ -372,3 +386,12 @@ extension _UnmodifiableMapViewExtension on UnmodifiableMapView<int, ByteData> {
return result;
}
}
extension ScanFilterExtension on ScanFilter {
RequestOptionsBuilder toRequestOptionsBuilder() {
return RequestOptionsBuilder(
[RequestFilterBuilder(services: withServices.toValidUUIDList())],
optionalServices: withServices.toValidUUIDList(),
);
}
}
+9 -1
View File
@@ -27,7 +27,7 @@ abstract class UniversalBlePlatformChannel {
@async
bool enableBluetooth();
void startScan();
void startScan(UniversalScanFilter? filter);
void stopScan();
@@ -105,6 +105,7 @@ class UniversalBleScanResult {
final int? rssi;
final Uint8List? manufacturerData;
final Uint8List? manufacturerDataHead;
final List<String?>? services;
UniversalBleScanResult({
required this.name,
@@ -113,6 +114,7 @@ class UniversalBleScanResult {
required this.rssi,
required this.manufacturerData,
required this.manufacturerDataHead,
required this.services,
});
}
@@ -127,3 +129,9 @@ class UniversalBleCharacteristic {
List<int?> properties;
UniversalBleCharacteristic(this.uuid, this.properties);
}
class UniversalScanFilter {
final List<String?> withServices;
UniversalScanFilter(this.withServices);
}
+2 -2
View File
@@ -1,6 +1,6 @@
name: universal_ble
description: A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE) plugin for Flutter
version: 0.9.2
version: 0.9.3
homepage: https://navideck.com
repository: https://github.com/Navideck/universal_ble
issue_tracker: https://github.com/Navideck/universal_ble/issues
@@ -32,7 +32,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
pigeon: ^16.0.5
pigeon: ^17.1.1
flutter:
plugin:
+117 -60
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v16.0.5), do not edit directly.
// Autogenerated from Pigeon (v17.1.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#undef _HAS_EXCEPTIONS
@@ -39,13 +39,15 @@ UniversalBleScanResult::UniversalBleScanResult(
const bool* is_paired,
const int64_t* rssi,
const std::vector<uint8_t>* manufacturer_data,
const std::vector<uint8_t>* manufacturer_data_head)
const std::vector<uint8_t>* manufacturer_data_head,
const EncodableList* services)
: device_id_(device_id),
name_(name ? std::optional<std::string>(*name) : std::nullopt),
is_paired_(is_paired ? std::optional<bool>(*is_paired) : std::nullopt),
rssi_(rssi ? std::optional<int64_t>(*rssi) : std::nullopt),
manufacturer_data_(manufacturer_data ? std::optional<std::vector<uint8_t>>(*manufacturer_data) : std::nullopt),
manufacturer_data_head_(manufacturer_data_head ? std::optional<std::vector<uint8_t>>(*manufacturer_data_head) : std::nullopt) {}
manufacturer_data_head_(manufacturer_data_head ? std::optional<std::vector<uint8_t>>(*manufacturer_data_head) : std::nullopt),
services_(services ? std::optional<EncodableList>(*services) : std::nullopt) {}
const std::string& UniversalBleScanResult::device_id() const {
return device_id_;
@@ -121,15 +123,29 @@ void UniversalBleScanResult::set_manufacturer_data_head(const std::vector<uint8_
}
const EncodableList* UniversalBleScanResult::services() const {
return services_ ? &(*services_) : nullptr;
}
void UniversalBleScanResult::set_services(const EncodableList* value_arg) {
services_ = value_arg ? std::optional<EncodableList>(*value_arg) : std::nullopt;
}
void UniversalBleScanResult::set_services(const EncodableList& value_arg) {
services_ = value_arg;
}
EncodableList UniversalBleScanResult::ToEncodableList() const {
EncodableList list;
list.reserve(6);
list.reserve(7);
list.push_back(EncodableValue(device_id_));
list.push_back(name_ ? EncodableValue(*name_) : EncodableValue());
list.push_back(is_paired_ ? EncodableValue(*is_paired_) : EncodableValue());
list.push_back(rssi_ ? EncodableValue(*rssi_) : EncodableValue());
list.push_back(manufacturer_data_ ? EncodableValue(*manufacturer_data_) : EncodableValue());
list.push_back(manufacturer_data_head_ ? EncodableValue(*manufacturer_data_head_) : EncodableValue());
list.push_back(services_ ? EncodableValue(*services_) : EncodableValue());
return list;
}
@@ -156,6 +172,10 @@ UniversalBleScanResult UniversalBleScanResult::FromEncodableList(const Encodable
if (!encodable_manufacturer_data_head.IsNull()) {
decoded.set_manufacturer_data_head(std::get<std::vector<uint8_t>>(encodable_manufacturer_data_head));
}
auto& encodable_services = list[6];
if (!encodable_services.IsNull()) {
decoded.set_services(std::get<EncodableList>(encodable_services));
}
return decoded;
}
@@ -251,6 +271,33 @@ UniversalBleCharacteristic UniversalBleCharacteristic::FromEncodableList(const E
return decoded;
}
// UniversalScanFilter
UniversalScanFilter::UniversalScanFilter(const EncodableList& with_services)
: with_services_(with_services) {}
const EncodableList& UniversalScanFilter::with_services() const {
return with_services_;
}
void UniversalScanFilter::set_with_services(const EncodableList& value_arg) {
with_services_ = value_arg;
}
EncodableList UniversalScanFilter::ToEncodableList() const {
EncodableList list;
list.reserve(1);
list.push_back(EncodableValue(with_services_));
return list;
}
UniversalScanFilter UniversalScanFilter::FromEncodableList(const EncodableList& list) {
UniversalScanFilter decoded(
std::get<EncodableList>(list[0]));
return decoded;
}
UniversalBlePlatformChannelCodecSerializer::UniversalBlePlatformChannelCodecSerializer() {}
@@ -264,6 +311,8 @@ EncodableValue UniversalBlePlatformChannelCodecSerializer::ReadValueOfType(
return CustomEncodableValue(UniversalBleScanResult::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
case 130:
return CustomEncodableValue(UniversalBleService::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
case 131:
return CustomEncodableValue(UniversalScanFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
default:
return flutter::StandardCodecSerializer::ReadValueOfType(type, stream);
}
@@ -288,6 +337,11 @@ void UniversalBlePlatformChannelCodecSerializer::WriteValue(
WriteValue(EncodableValue(std::any_cast<UniversalBleService>(*custom_value).ToEncodableList()), stream);
return;
}
if (custom_value->type() == typeid(UniversalScanFilter)) {
stream->WriteByte(131);
WriteValue(EncodableValue(std::any_cast<UniversalScanFilter>(*custom_value).ToEncodableList()), stream);
return;
}
}
flutter::StandardCodecSerializer::WriteValue(value, stream);
}
@@ -302,9 +356,9 @@ void UniversalBlePlatformChannel::SetUp(
flutter::BinaryMessenger* binary_messenger,
UniversalBlePlatformChannel* api) {
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getBluetoothAvailabilityState", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getBluetoothAvailabilityState", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
api->GetBluetoothAvailabilityState([reply](ErrorOr<int64_t>&& output) {
if (output.has_error()) {
@@ -320,13 +374,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.enableBluetooth", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.enableBluetooth", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
api->EnableBluetooth([reply](ErrorOr<bool>&& output) {
if (output.has_error()) {
@@ -342,15 +396,18 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
std::optional<FlutterError> output = api->StartScan();
const auto& args = std::get<EncodableList>(message);
const auto& encodable_filter_arg = args.at(0);
const auto* filter_arg = &(std::any_cast<const UniversalScanFilter&>(std::get<CustomEncodableValue>(encodable_filter_arg)));
std::optional<FlutterError> output = api->StartScan(filter_arg);
if (output.has_value()) {
reply(WrapError(output.value()));
return;
@@ -363,13 +420,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopScan", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopScan", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
std::optional<FlutterError> output = api->StopScan();
if (output.has_value()) {
@@ -384,13 +441,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.connect", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.connect", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_device_id_arg = args.at(0);
@@ -412,13 +469,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disconnect", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disconnect", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_device_id_arg = args.at(0);
@@ -440,13 +497,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setNotifiable", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setNotifiable", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_device_id_arg = args.at(0);
@@ -486,13 +543,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.discoverServices", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.discoverServices", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_device_id_arg = args.at(0);
@@ -515,13 +572,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readValue", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readValue", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_device_id_arg = args.at(0);
@@ -556,13 +613,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestMtu", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestMtu", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_device_id_arg = args.at(0);
@@ -591,13 +648,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.writeValue", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.writeValue", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_device_id_arg = args.at(0);
@@ -644,13 +701,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPaired", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPaired", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_device_id_arg = args.at(0);
@@ -673,13 +730,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.pair", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.pair", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_device_id_arg = args.at(0);
@@ -701,13 +758,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.unPair", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.unPair", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_device_id_arg = args.at(0);
@@ -729,13 +786,13 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
{
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectedDevices", &GetCodec());
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectedDevices", &GetCodec());
if (api != nullptr) {
channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
try {
const auto& args = std::get<EncodableList>(message);
const auto& encodable_with_services_arg = args.at(0);
@@ -758,7 +815,7 @@ void UniversalBlePlatformChannel::SetUp(
}
});
} else {
channel->SetMessageHandler(nullptr);
channel.SetMessageHandler(nullptr);
}
}
}
@@ -819,11 +876,11 @@ void UniversalBleCallbackChannel::OnAvailabilityChanged(
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error) {
const std::string channel_name = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged";
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger_, channel_name, &GetCodec());
BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec());
EncodableValue encoded_api_arguments = EncodableValue(EncodableList{
EncodableValue(state_arg),
});
channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) {
channel.Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) {
std::unique_ptr<EncodableValue> response = GetCodec().DecodeMessage(reply, reply_size);
const auto& encodable_return_value = *response;
const auto* list_return_value = std::get_if<EncodableList>(&encodable_return_value);
@@ -846,13 +903,13 @@ void UniversalBleCallbackChannel::OnPairStateChange(
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error) {
const std::string channel_name = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange";
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger_, channel_name, &GetCodec());
BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec());
EncodableValue encoded_api_arguments = EncodableValue(EncodableList{
EncodableValue(device_id_arg),
EncodableValue(is_paired_arg),
error_arg ? EncodableValue(*error_arg) : EncodableValue(),
});
channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) {
channel.Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) {
std::unique_ptr<EncodableValue> response = GetCodec().DecodeMessage(reply, reply_size);
const auto& encodable_return_value = *response;
const auto* list_return_value = std::get_if<EncodableList>(&encodable_return_value);
@@ -873,11 +930,11 @@ void UniversalBleCallbackChannel::OnScanResult(
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error) {
const std::string channel_name = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult";
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger_, channel_name, &GetCodec());
BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec());
EncodableValue encoded_api_arguments = EncodableValue(EncodableList{
CustomEncodableValue(result_arg),
});
channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) {
channel.Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) {
std::unique_ptr<EncodableValue> response = GetCodec().DecodeMessage(reply, reply_size);
const auto& encodable_return_value = *response;
const auto* list_return_value = std::get_if<EncodableList>(&encodable_return_value);
@@ -900,13 +957,13 @@ void UniversalBleCallbackChannel::OnValueChanged(
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error) {
const std::string channel_name = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged";
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger_, channel_name, &GetCodec());
BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec());
EncodableValue encoded_api_arguments = EncodableValue(EncodableList{
EncodableValue(device_id_arg),
EncodableValue(characteristic_id_arg),
EncodableValue(value_arg),
});
channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) {
channel.Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) {
std::unique_ptr<EncodableValue> response = GetCodec().DecodeMessage(reply, reply_size);
const auto& encodable_return_value = *response;
const auto* list_return_value = std::get_if<EncodableList>(&encodable_return_value);
@@ -928,12 +985,12 @@ void UniversalBleCallbackChannel::OnConnectionChanged(
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error) {
const std::string channel_name = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged";
auto channel = std::make_unique<BasicMessageChannel<>>(binary_messenger_, channel_name, &GetCodec());
BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec());
EncodableValue encoded_api_arguments = EncodableValue(EncodableList{
EncodableValue(device_id_arg),
EncodableValue(state_arg),
});
channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) {
channel.Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) {
std::unique_ptr<EncodableValue> response = GetCodec().DecodeMessage(reply, reply_size);
const auto& encodable_return_value = *response;
const auto* list_return_value = std::get_if<EncodableList>(&encodable_return_value);
+31 -3
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v16.0.5), do not edit directly.
// Autogenerated from Pigeon (v17.1.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#ifndef PIGEON_UNIVERSAL_BLE_G_H_
@@ -70,7 +70,8 @@ class UniversalBleScanResult {
const bool* is_paired,
const int64_t* rssi,
const std::vector<uint8_t>* manufacturer_data,
const std::vector<uint8_t>* manufacturer_data_head);
const std::vector<uint8_t>* manufacturer_data_head,
const flutter::EncodableList* services);
const std::string& device_id() const;
void set_device_id(std::string_view value_arg);
@@ -95,6 +96,10 @@ class UniversalBleScanResult {
void set_manufacturer_data_head(const std::vector<uint8_t>* value_arg);
void set_manufacturer_data_head(const std::vector<uint8_t>& value_arg);
const flutter::EncodableList* services() const;
void set_services(const flutter::EncodableList* value_arg);
void set_services(const flutter::EncodableList& value_arg);
private:
static UniversalBleScanResult FromEncodableList(const flutter::EncodableList& list);
@@ -109,6 +114,7 @@ class UniversalBleScanResult {
std::optional<int64_t> rssi_;
std::optional<std::vector<uint8_t>> manufacturer_data_;
std::optional<std::vector<uint8_t>> manufacturer_data_head_;
std::optional<flutter::EncodableList> services_;
};
@@ -172,6 +178,28 @@ class UniversalBleCharacteristic {
};
// Generated class from Pigeon that represents data sent in messages.
class UniversalScanFilter {
public:
// Constructs an object setting all fields.
explicit UniversalScanFilter(const flutter::EncodableList& with_services);
const flutter::EncodableList& with_services() const;
void set_with_services(const flutter::EncodableList& value_arg);
private:
static UniversalScanFilter FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBlePlatformChannelCodecSerializer;
friend class UniversalBleCallbackChannel;
friend class UniversalBleCallbackChannelCodecSerializer;
flutter::EncodableList with_services_;
};
class UniversalBlePlatformChannelCodecSerializer : public flutter::StandardCodecSerializer {
public:
UniversalBlePlatformChannelCodecSerializer();
@@ -201,7 +229,7 @@ class UniversalBlePlatformChannel {
virtual ~UniversalBlePlatformChannel() {}
virtual void GetBluetoothAvailabilityState(std::function<void(ErrorOr<int64_t> reply)> result) = 0;
virtual void EnableBluetooth(std::function<void(ErrorOr<bool> reply)> result) = 0;
virtual std::optional<FlutterError> StartScan() = 0;
virtual std::optional<FlutterError> StartScan(const UniversalScanFilter* filter) = 0;
virtual std::optional<FlutterError> StopScan() = 0;
virtual std::optional<FlutterError> Connect(const std::string& device_id) = 0;
virtual std::optional<FlutterError> Disconnect(const std::string& device_id) = 0;
+29 -2
View File
@@ -91,7 +91,7 @@ namespace universal_ble
} });
}
std::optional<FlutterError> UniversalBlePlugin::StartScan()
std::optional<FlutterError> UniversalBlePlugin::StartScan(const UniversalScanFilter *filter)
{
if (bluetoothRadio && bluetoothRadio.State() == RadioState::On)
{
@@ -99,6 +99,8 @@ namespace universal_ble
{
bluetoothLEWatcher = BluetoothLEAdvertisementWatcher();
bluetoothLEWatcher.ScanningMode(BluetoothLEScanningMode::Active);
if (filter != nullptr)
ApplyScanFilter(filter, bluetoothLEWatcher);
bluetoothLEWatcherReceivedToken = bluetoothLEWatcher.Received({this, &UniversalBlePlugin::BluetoothLEWatcher_Received});
}
bluetoothLEWatcher.Start();
@@ -116,6 +118,20 @@ namespace universal_ble
}
};
void UniversalBlePlugin::ApplyScanFilter(const UniversalScanFilter *filter, BluetoothLEAdvertisementWatcher &bluetoothWatcher)
{
// Apply Services filter
const auto &services = filter->with_services();
if (!services.empty())
{
for (const auto &uuid : services)
{
std::string uuid_str = std::get<std::string>(uuid);
bluetoothWatcher.AdvertisementFilter().Advertisement().ServiceUuids().Append(uuid_to_guid(uuid_str));
}
}
}
std::optional<FlutterError> UniversalBlePlugin::StopScan()
{
@@ -516,6 +532,11 @@ namespace universal_ble
scanResult.set_manufacturer_data_head(existingScanResult.manufacturer_data_head());
shouldUpdate = true;
}
if (scanResult.services() == nullptr && existingScanResult.services() != nullptr)
{
scanResult.set_services(existingScanResult.services());
shouldUpdate = true;
}
// if nothing to update then return
if (!shouldUpdate)
@@ -629,6 +650,7 @@ namespace universal_ble
int16_t rssi = rssiPropertyValue.GetInt16();
universalScanResult.set_rssi(rssi);
}
pushUniversalScanResult(universalScanResult);
}
}
@@ -668,6 +690,12 @@ namespace universal_ble
universalScanResult.set_manufacturer_data_head(manufacturerData);
universalScanResult.set_rssi(args.RawSignalStrengthInDBm());
// Add services
flutter::EncodableList services = flutter::EncodableList();
for (auto &&uuid : args.Advertisement().ServiceUuids())
services.push_back(guid_to_uuid(uuid));
universalScanResult.set_services(services);
// check if this device already discovered in deviceWatcher
auto it = deviceWatcherDevices.find(deviceId);
if (it != deviceWatcherDevices.end())
@@ -686,7 +714,6 @@ namespace universal_ble
universalScanResult.set_name(winrt::to_string(deviceInfo.Name()));
}
// Cache connectable status
pushUniversalScanResult(universalScanResult);
}
catch (...)
+2 -1
View File
@@ -123,11 +123,12 @@ namespace universal_ble
winrt::fire_and_forget WriteAsync(GattCharacteristic characteristic, GattWriteOption writeOption,
const std::vector<uint8_t> &value,
std::function<void(std::optional<FlutterError> reply)> result);
void ApplyScanFilter(const UniversalScanFilter *filter, BluetoothLEAdvertisementWatcher &bluetoothWatcher);
// UniversalBlePlatformChannel implementation.
void GetBluetoothAvailabilityState(std::function<void(ErrorOr<int64_t> reply)> result) override;
void EnableBluetooth(std::function<void(ErrorOr<bool> reply)> result) override;
std::optional<FlutterError> StartScan() override;
std::optional<FlutterError> StartScan(const UniversalScanFilter *filter) override;
std::optional<FlutterError> StopScan() override;
std::optional<FlutterError> Connect(const std::string &device_id) override;
std::optional<FlutterError> Disconnect(const std::string &device_id) override;