From 8d0cfe98d9b60ff8b754427523adf859fa999305 Mon Sep 17 00:00:00 2001 From: Rohit Sangwan Date: Thu, 20 Jun 2024 23:55:59 +0530 Subject: [PATCH] Add connecting and disconnecting states to BleConnectionState (#54) * Add connecting and disconnecting state * Replace BleConnectionState with boolean in onConnectionChanged callback * Improve Docs and Changelog * Implement GetConnectionState in Windows * Update CHANGELOG.md Co-authored-by: Foti Dim * Add code level doc of connectionState getter --------- Co-authored-by: Foti Dim --- README.md | 23 +++++++++------ .../navideck/universal_ble/UniversalBle.g.kt | 10 +++---- .../universal_ble/UniversalBleHelper.kt | 14 ++++++++- .../universal_ble/UniversalBlePlugin.kt | 29 +++++++++++-------- darwin/Classes/UniversalBle.g.swift | 16 +++++----- darwin/Classes/UniversalBleHelper.swift | 2 ++ darwin/Classes/UniversalBlePlugin.swift | 25 ++++++++++++---- example/lib/data/mock_universal_ble.dart | 6 ++-- .../peripheral_detail_page.dart | 16 +++++----- example/pubspec.lock | 2 +- lib/src/models/ble_connection_state.dart | 4 ++- lib/src/models/ble_device.dart | 9 +++--- lib/src/universal_ble.dart | 8 +++-- .../universal_ble_linux.dart | 18 +++++------- .../universal_ble_pigeon/universal_ble.g.dart | 16 +++++----- .../universal_ble_pigeon_channel.dart | 13 +++++---- lib/src/universal_ble_platform_interface.dart | 5 ++-- .../universal_ble_web/universal_ble_web.dart | 14 ++++----- pigeon/universal_ble.dart | 4 +-- windows/src/generated/universal_ble.g.cpp | 8 ++--- windows/src/generated/universal_ble.g.h | 4 +-- windows/src/universal_ble_plugin.cpp | 21 +++++++++----- windows/src/universal_ble_plugin.h | 4 +-- 23 files changed, 159 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index e21d7b3..2b131d8 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ UniversalBle.stopScan(); Before initiating a scan, ensure that Bluetooth is available: ```dart -AvailabilityState state = await UniversalBle.getBluetoothAvailabilityState() +AvailabilityState state = await UniversalBle.getBluetoothAvailabilityState(); // Start scan only if Bluetooth is powered on if (state == AvailabilityState.poweredOn) { UniversalBle.startScan(); @@ -91,18 +91,19 @@ UniversalBle.onAvailabilityChange = (state) { See the [Bluetooth Availability](#bluetooth-availability) section for more. -#### Connected Devices +#### System Devices -Already connected devices, either through previous sessions or connected through system settings, won't show up as scan results. -You can list those devices using `getSystemDevices()`. You still need to explicitly connect before using them. +Already connected devices, connected either through previous sessions, other apps or through system settings, won't show up as scan results. You can get those using `getSystemDevices()`. ```dart -// Get connected devices +// Get already connected devices // You can set `withServices` to narrow down the results +// On `Apple`, `withServices` is required to get connected devices, else [1800] service will be used as default filter. List devices = await UniversalBle.getSystemDevices(withServices: []); ``` +For each such device the `isSystemDevice` property will be `true`. -For each connected device the `isConnected` property will be `true`. +You still need to explicitly [connect](#connecting) to them before being able to use them. #### Scan Filter @@ -142,10 +143,14 @@ UniversalBle.connect(deviceId); // Disconnect from a device UniversalBle.disconnect(deviceId); -// Get connection state updates -UniversalBle.onConnectionChange = (String deviceId, BleConnectionState state) { - debugPrint('OnConnectionChange $deviceId, $state'); +// Get connection/disconnection updates +UniversalBle.onConnectionChange = (String deviceId, bool isConnected) { + debugPrint('OnConnectionChange $deviceId, $isConnected'); } + +// Get current connection state +// Can be connected, disconnected, connecting or disconnecting +BleConnectionState connectionState = await bleDevice.connectionState; ``` ### Discovering Services diff --git a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBle.g.kt b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBle.g.kt index 0098d3e..de99323 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBle.g.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBle.g.kt @@ -258,7 +258,7 @@ interface UniversalBlePlatformChannel { fun pair(deviceId: String) fun unPair(deviceId: String) fun getSystemDevices(withServices: List, callback: (Result>) -> Unit) - fun isConnected(deviceId: String): Boolean + fun getConnectionState(deviceId: String): Long companion object { /** The codec used by UniversalBlePlatformChannel. */ @@ -559,13 +559,13 @@ interface UniversalBlePlatformChannel { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isConnected$separatedMessageChannelSuffix", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectionState$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val deviceIdArg = args[0] as String val wrapped: List = try { - listOf(api.isConnected(deviceIdArg)) + listOf(api.getConnectionState(deviceIdArg)) } catch (exception: Throwable) { wrapError(exception) } @@ -680,12 +680,12 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger, } } } - fun onConnectionChanged(deviceIdArg: String, stateArg: Long, callback: (Result) -> Unit) + fun onConnectionChanged(deviceIdArg: String, connectedArg: Boolean, callback: (Result) -> Unit) { val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" val channelName = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(deviceIdArg, stateArg)) { + channel.send(listOf(deviceIdArg, connectedArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) diff --git a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt index eda5175..5842300 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt @@ -29,7 +29,9 @@ const val ccdCharacteristic = "00002902-0000-1000-8000-00805f9b34fb" enum class BleConnectionState(val value: Long) { Connected(0), - Disconnected(1) + Disconnected(1), + Connecting(2), + Disconnecting(3) } enum class AvailabilityState(val value: Long) { @@ -65,6 +67,16 @@ enum class CharacteristicProperty(val value: Long) { } +fun Int.toBleConnectionState(): BleConnectionState { + return when (this) { + BluetoothGatt.STATE_CONNECTED -> BleConnectionState.Connected + BluetoothGatt.STATE_CONNECTING -> BleConnectionState.Connecting + BluetoothGatt.STATE_DISCONNECTING -> BleConnectionState.Disconnecting + BluetoothGatt.STATE_DISCONNECTED -> BleConnectionState.Disconnected + else -> BleConnectionState.Disconnected + } +} + fun String.validFullUUID(): String { return when (this.count()) { 4 -> "0000$this-0000-1000-8000-00805F9B34FB" diff --git a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt index 9ec4d30..5b778bf 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt @@ -137,10 +137,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), if (currentState == BluetoothGatt.STATE_CONNECTED) { Log.e(TAG, "$deviceId Already connected") mainThreadHandler?.post { - callbackChannel?.onConnectionChanged( - deviceId, - BleConnectionState.Connected.value - ) {} + callbackChannel?.onConnectionChanged(deviceId, true) {} } return } else if (currentState == BluetoothGatt.STATE_CONNECTING) { @@ -180,8 +177,11 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), cleanConnection(deviceId.toBluetoothGatt()) } - override fun isConnected(deviceId: String): Boolean { - return devicesStateMap[deviceId] == BluetoothGatt.STATE_CONNECTED + override fun getConnectionState(deviceId: String): Long { + return bluetoothManager.getConnectionState( + bluetoothManager.adapter.getRemoteDevice(deviceId), + BluetoothProfile.GATT + ).toBleConnectionState().value } override fun discoverServices( @@ -749,22 +749,27 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { devicesStateMap[gatt.device.address] = newState - if (newState == BluetoothGatt.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) { + if (status != BluetoothGatt.GATT_SUCCESS) { + Log.e(TAG, "Failed to update connected state: $status") + return + } + + if (newState == BluetoothGatt.STATE_CONNECTED) { mainThreadHandler?.post { callbackChannel?.onConnectionChanged( - gatt.device.address, - BleConnectionState.Connected.value + gatt.device.address, true ) {} } - } else { + } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { cleanConnection(gatt) mainThreadHandler?.post { callbackChannel?.onConnectionChanged( - gatt.device.address, - BleConnectionState.Disconnected.value + gatt.device.address, false ) {} } } + + } override fun onCharacteristicChanged( diff --git a/darwin/Classes/UniversalBle.g.swift b/darwin/Classes/UniversalBle.g.swift index 22a565e..d6c1e45 100644 --- a/darwin/Classes/UniversalBle.g.swift +++ b/darwin/Classes/UniversalBle.g.swift @@ -259,7 +259,7 @@ protocol UniversalBlePlatformChannel { func pair(deviceId: String) throws func unPair(deviceId: String) throws func getSystemDevices(withServices: [String], completion: @escaping (Result<[UniversalBleScanResult], Error>) -> Void) - func isConnected(deviceId: String) throws -> Bool + func getConnectionState(deviceId: String) throws -> Int64 } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -516,20 +516,20 @@ class UniversalBlePlatformChannelSetup { } else { getSystemDevicesChannel.setMessageHandler(nil) } - let isConnectedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isConnected\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + let getConnectionStateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectionState\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { - isConnectedChannel.setMessageHandler { message, reply in + getConnectionStateChannel.setMessageHandler { message, reply in let args = message as! [Any?] let deviceIdArg = args[0] as! String do { - let result = try api.isConnected(deviceId: deviceIdArg) + let result = try api.getConnectionState(deviceId: deviceIdArg) reply(wrapResult(result)) } catch { reply(wrapError(error)) } } } else { - isConnectedChannel.setMessageHandler(nil) + getConnectionStateChannel.setMessageHandler(nil) } } } @@ -577,7 +577,7 @@ protocol UniversalBleCallbackChannelProtocol { func onPairStateChange(deviceId deviceIdArg: String, isPaired isPairedArg: Bool, error errorArg: String?, completion: @escaping (Result) -> Void) func onScanResult(result resultArg: UniversalBleScanResult, completion: @escaping (Result) -> Void) func onValueChanged(deviceId deviceIdArg: String, characteristicId characteristicIdArg: String, value valueArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) - func onConnectionChanged(deviceId deviceIdArg: String, state stateArg: Int64, completion: @escaping (Result) -> Void) + func onConnectionChanged(deviceId deviceIdArg: String, connected connectedArg: Bool, completion: @escaping (Result) -> Void) } class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol { private let binaryMessenger: FlutterBinaryMessenger @@ -661,10 +661,10 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol { } } } - func onConnectionChanged(deviceId deviceIdArg: String, state stateArg: Int64, completion: @escaping (Result) -> Void) { + func onConnectionChanged(deviceId deviceIdArg: String, connected connectedArg: Bool, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([deviceIdArg, stateArg] as [Any?]) { response in + channel.sendMessage([deviceIdArg, connectedArg] as [Any?]) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) return diff --git a/darwin/Classes/UniversalBleHelper.swift b/darwin/Classes/UniversalBleHelper.swift index aa9d48a..d10d282 100644 --- a/darwin/Classes/UniversalBleHelper.swift +++ b/darwin/Classes/UniversalBleHelper.swift @@ -27,6 +27,8 @@ enum BleOutputProperty: Int { enum BlueConnectionState: Int64 { case connected = 0 case disconnected = 1 + case connecting = 2 + case disconnecting = 3 } enum AvailabilityState: Int64 { diff --git a/darwin/Classes/UniversalBlePlugin.swift b/darwin/Classes/UniversalBlePlugin.swift index 60bde90..bd9f501 100644 --- a/darwin/Classes/UniversalBlePlugin.swift +++ b/darwin/Classes/UniversalBlePlugin.swift @@ -95,11 +95,20 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral cleanUpConnection(deviceId: deviceId) } - func isConnected(deviceId: String) -> Bool { - guard let peripheral = discoveredPeripherals[deviceId] else { - return false + func getConnectionState(deviceId: String) throws -> Int64 { + let peripheral = try deviceId.getPeripheral() + switch peripheral.state { + case .connecting: + return BlueConnectionState.connecting.rawValue + case .connected: + return BlueConnectionState.connected.rawValue + case .disconnecting: + return BlueConnectionState.disconnecting.rawValue + case .disconnected: + return BlueConnectionState.disconnected.rawValue + @unknown default: + fatalError() } - return peripheral.state == CBPeripheralState.connected } func cleanUpConnection(deviceId: String) { @@ -307,15 +316,19 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral } public func centralManager(_: CBCentralManager, didConnect peripheral: CBPeripheral) { - callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, state: BlueConnectionState.connected.rawValue) { _ in } + callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, connected: true) { _ in } } public func centralManager(_: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error _: Error?) { - callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, state: BlueConnectionState.disconnected.rawValue) { _ in } + callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, connected: false) { _ in } // Cleanup on disconnect cleanUpConnection(deviceId: peripheral.uuid.uuidString) } + public func centralManager(_: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { + print("Failed to connect: \(peripheral.uuid.uuidString): \(String(describing: error))") + } + public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices _: Error?) { let deviceId = peripheral.identifier.uuidString guard let services = peripheral.services else { diff --git a/example/lib/data/mock_universal_ble.dart b/example/lib/data/mock_universal_ble.dart index 5e67d48..15e547b 100644 --- a/example/lib/data/mock_universal_ble.dart +++ b/example/lib/data/mock_universal_ble.dart @@ -31,12 +31,12 @@ class MockUniversalBle extends UniversalBlePlatform { @override Future connect(String deviceId, {Duration? connectionTimeout}) async { - onConnectionChange?.call(deviceId, BleConnectionState.connected); + onConnectionChange?.call(deviceId, true); } @override Future disconnect(String deviceId) async { - onConnectionChange?.call(deviceId, BleConnectionState.disconnected); + onConnectionChange?.call(deviceId, false); } @override @@ -105,7 +105,7 @@ class MockUniversalBle extends UniversalBlePlatform { } @override - Future isConnected(String deviceId) { + Future getConnectionState(String deviceId) { throw UnimplementedError(); } } diff --git a/example/lib/peripheral_details/peripheral_detail_page.dart b/example/lib/peripheral_details/peripheral_detail_page.dart index dc5b1a1..28b9644 100644 --- a/example/lib/peripheral_details/peripheral_detail_page.dart +++ b/example/lib/peripheral_details/peripheral_detail_page.dart @@ -62,16 +62,16 @@ class _PeripheralDetailPageState extends State { }); } - void _handleConnectionChange(String deviceId, BleConnectionState state) { - print('_handleConnectionChange $deviceId, ${state.name}'); + void _handleConnectionChange(String deviceId, bool isConnected) { + print('_handleConnectionChange $deviceId, $isConnected'); setState(() { if (deviceId == widget.deviceId) { - isConnected = (state == BleConnectionState.connected); + this.isConnected = isConnected; } }); - _addLog('Connection', state.name.toUpperCase()); + _addLog('Connection', isConnected ? "Connected" : "Disconnected"); // Auto Discover Services - if (isConnected) { + if (this.isConnected) { _discoverServices(); } } @@ -347,13 +347,13 @@ class _PeripheralDetailPageState extends State { PlatformButton( onPressed: () async { _addLog( - 'IsConnected', - await UniversalBle.isConnected( + 'ConnectionState', + await UniversalBle.getConnectionState( widget.deviceId, ), ); }, - text: 'IsConnected', + text: 'Connection State', ), if (Capabilities.supportsRequestMtuApi) PlatformButton( diff --git a/example/pubspec.lock b/example/pubspec.lock index c29902b..8efa046 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -410,7 +410,7 @@ packages: path: ".." relative: true source: path - version: "0.9.12" + version: "0.10.0" vector_math: dependency: transitive description: diff --git a/lib/src/models/ble_connection_state.dart b/lib/src/models/ble_connection_state.dart index 80d504e..2230299 100644 --- a/lib/src/models/ble_connection_state.dart +++ b/lib/src/models/ble_connection_state.dart @@ -1,6 +1,8 @@ enum BleConnectionState { connected, - disconnected; + disconnected, + connecting, + disconnecting; const BleConnectionState(); diff --git a/lib/src/models/ble_device.dart b/lib/src/models/ble_device.dart index fc2ad1f..ef83b48 100644 --- a/lib/src/models/ble_device.dart +++ b/lib/src/models/ble_device.dart @@ -12,10 +12,11 @@ class BleDevice { Uint8List? manufacturerDataHead; Uint8List? manufacturerData; - Future get connectionState async => - await UniversalBle.isConnected(deviceId) - ? BleConnectionState.connected - : BleConnectionState.disconnected; + /// Returns connection state of device, + /// All platforms will return `Connected/Disconnected` states + /// `Android` and `Apple` can also return `Connecting/Disconnecting` states + Future get connectionState => + UniversalBle.getConnectionState(deviceId); BleDevice({ required this.deviceId, diff --git a/lib/src/universal_ble.dart b/lib/src/universal_ble.dart index b56e6ae..b74c313 100644 --- a/lib/src/universal_ble.dart +++ b/lib/src/universal_ble.dart @@ -203,10 +203,12 @@ class UniversalBle { ); } - /// Returns true if device is connected to the app - static Future isConnected(String deviceId) async { + /// Returns connection state of device, + /// All platforms will return `Connected/Disconnected` states + /// `Android` and `Apple` can also return `Connecting/Disconnecting` states + static Future getConnectionState(String deviceId) async { return await _bleCommandQueue.queueCommand( - () => _platform.isConnected(deviceId), + () => _platform.getConnectionState(deviceId), ); } diff --git a/lib/src/universal_ble_linux/universal_ble_linux.dart b/lib/src/universal_ble_linux/universal_ble_linux.dart index 7f14b09..97bc4d3 100644 --- a/lib/src/universal_ble_linux/universal_ble_linux.dart +++ b/lib/src/universal_ble_linux/universal_ble_linux.dart @@ -85,19 +85,22 @@ class UniversalBleLinux extends UniversalBlePlatform { } @override - Future isConnected(String deviceId) async { + Future getConnectionState(String deviceId) async { BlueZDevice? device = _devices[deviceId] ?? _client.devices.cast().firstWhere( (device) => device?.address == deviceId, orElse: () => null); - return device?.connected ?? false; + bool connected = device?.connected ?? false; + return connected + ? BleConnectionState.connected + : BleConnectionState.disconnected; } @override Future connect(String deviceId, {Duration? connectionTimeout}) async { final device = _findDeviceById(deviceId); if (device.connected) { - onConnectionChange?.call(deviceId, BleConnectionState.connected); + onConnectionChange?.call(deviceId, true); return; } await device.connect(); @@ -107,7 +110,7 @@ class UniversalBleLinux extends UniversalBlePlatform { Future disconnect(String deviceId) async { final device = _findDeviceById(deviceId); if (!device.connected) { - onConnectionChange?.call(deviceId, BleConnectionState.disconnected); + onConnectionChange?.call(deviceId, false); return; } await device.disconnect(); @@ -429,12 +432,7 @@ class UniversalBleLinux extends UniversalBlePlatform { updateScanResult(device.toBleDevice()); break; case BluezProperty.connected: - onConnectionChange?.call( - device.address, - device.connected - ? BleConnectionState.connected - : BleConnectionState.disconnected, - ); + onConnectionChange?.call(device.address, device.connected); break; case BluezProperty.manufacturerData: updateScanResult(device.toBleDevice()); diff --git a/lib/src/universal_ble_pigeon/universal_ble.g.dart b/lib/src/universal_ble_pigeon/universal_ble.g.dart index 3e6cd55..6065d33 100644 --- a/lib/src/universal_ble_pigeon/universal_ble.g.dart +++ b/lib/src/universal_ble_pigeon/universal_ble.g.dart @@ -657,9 +657,9 @@ class UniversalBlePlatformChannel { } } - Future isConnected(String deviceId) async { + Future getConnectionState(String deviceId) async { final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isConnected$__pigeon_messageChannelSuffix'; + 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectionState$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -682,7 +682,7 @@ class UniversalBlePlatformChannel { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as bool?)!; + return (__pigeon_replyList[0] as int?)!; } } } @@ -724,7 +724,7 @@ abstract class UniversalBleCallbackChannel { void onValueChanged( String deviceId, String characteristicId, Uint8List value); - void onConnectionChanged(String deviceId, int state); + void onConnectionChanged(String deviceId, bool connected); static void setUp( UniversalBleCallbackChannel? api, { @@ -873,11 +873,11 @@ abstract class UniversalBleCallbackChannel { final String? arg_deviceId = (args[0] as String?); assert(arg_deviceId != null, 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged was null, expected non-null String.'); - final int? arg_state = (args[1] as int?); - assert(arg_state != null, - 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged was null, expected non-null int.'); + final bool? arg_connected = (args[1] as bool?); + assert(arg_connected != null, + 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged was null, expected non-null bool.'); try { - api.onConnectionChanged(arg_deviceId!, arg_state!); + api.onConnectionChanged(arg_deviceId!, arg_connected!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/lib/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart b/lib/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart index bcff0b1..a9eab8c 100644 --- a/lib/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart +++ b/lib/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart @@ -41,7 +41,10 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform { Future stopScan() => _channel.stopScan(); @override - Future isConnected(String deviceId) => _channel.isConnected(deviceId); + Future getConnectionState(String deviceId) async { + int state = await _channel.getConnectionState(deviceId); + return BleConnectionState.parse(state); + } @override Future connect(String deviceId, {Duration? connectionTimeout}) => @@ -125,8 +128,8 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform { scanResult: (BleDevice bleDevice) => updateScanResult(bleDevice), availabilityChange: (AvailabilityState state) => onAvailabilityChange?.call(state), - connectionChanged: (String deviceId, BleConnectionState state) => - onConnectionChange?.call(deviceId, state), + connectionChanged: (String deviceId, bool connected) => + onConnectionChange?.call(deviceId, connected), valueChanged: (String deviceId, String characteristicId, Uint8List value) => onValueChange?.call(deviceId, characteristicId, value), @@ -173,8 +176,8 @@ class _UniversalBleCallbackHandler extends UniversalBleCallbackChannel { availabilityChange(AvailabilityState.parse(state)); @override - void onConnectionChanged(String deviceId, int state) => - connectionChanged(deviceId, BleConnectionState.parse(state)); + void onConnectionChanged(String deviceId, bool connected) => + connectionChanged(deviceId, connected); @override void onScanResult(UniversalBleScanResult result) => diff --git a/lib/src/universal_ble_platform_interface.dart b/lib/src/universal_ble_platform_interface.dart index 0238194..dbffb68 100644 --- a/lib/src/universal_ble_platform_interface.dart +++ b/lib/src/universal_ble_platform_interface.dart @@ -44,7 +44,7 @@ abstract class UniversalBlePlatform { Future unPair(String deviceId); - Future isConnected(String deviceId); + Future getConnectionState(String deviceId); Future> getSystemDevices( List? withServices, @@ -74,8 +74,7 @@ abstract class UniversalBlePlatform { } // Callback types -typedef OnConnectionChange = void Function( - String deviceId, BleConnectionState state); +typedef OnConnectionChange = void Function(String deviceId, bool isConnected); typedef OnValueChange = void Function( String deviceId, String characteristicId, Uint8List value); diff --git a/lib/src/universal_ble_web/universal_ble_web.dart b/lib/src/universal_ble_web/universal_ble_web.dart index 556fade..e22c0b3 100644 --- a/lib/src/universal_ble_web/universal_ble_web.dart +++ b/lib/src/universal_ble_web/universal_ble_web.dart @@ -20,10 +20,13 @@ class UniversalBleWeb extends UniversalBlePlatform { final Map _characteristicStreamList = {}; @override - Future isConnected(String deviceId) async { + Future getConnectionState(String deviceId) async { // TODO: Test this on Web (All platforms) BluetoothDevice? device = _getDeviceById(deviceId); - return await device?.connected.first ?? false; + bool connected = await device?.connected.first ?? false; + return connected + ? BleConnectionState.connected + : BleConnectionState.disconnected; } @override @@ -42,17 +45,14 @@ class UniversalBleWeb extends UniversalBlePlatform { _connectedDeviceStreamList[deviceId] = device.connected.listen((event) { if (!event) _cleanConnection(deviceId); - onConnectionChange?.call( - deviceId, - event ? BleConnectionState.connected : BleConnectionState.disconnected, - ); + onConnectionChange?.call(deviceId, event); }); } @override Future disconnect(String deviceId) async { _cleanConnection(deviceId); - onConnectionChange?.call(deviceId, BleConnectionState.disconnected); + onConnectionChange?.call(deviceId, false); _getDeviceById(deviceId)?.disconnect(); } diff --git a/pigeon/universal_ble.dart b/pigeon/universal_ble.dart index 336dbe3..e6b68f3 100644 --- a/pigeon/universal_ble.dart +++ b/pigeon/universal_ble.dart @@ -77,7 +77,7 @@ abstract class UniversalBlePlatformChannel { List withServices, ); - bool isConnected(String deviceId); + int getConnectionState(String deviceId); } /// Native -> Flutter @@ -97,7 +97,7 @@ abstract class UniversalBleCallbackChannel { void onConnectionChanged( String deviceId, - int state, + bool connected, ); } diff --git a/windows/src/generated/universal_ble.g.cpp b/windows/src/generated/universal_ble.g.cpp index 68ec08c..768fa4b 100644 --- a/windows/src/generated/universal_ble.g.cpp +++ b/windows/src/generated/universal_ble.g.cpp @@ -926,7 +926,7 @@ void UniversalBlePlatformChannel::SetUp( } } { - BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isConnected" + prepended_suffix, &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectionState" + prepended_suffix, &GetCodec()); if (api != nullptr) { channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { try { @@ -937,7 +937,7 @@ void UniversalBlePlatformChannel::SetUp( return; } const auto& device_id_arg = std::get(encodable_device_id_arg); - ErrorOr output = api->IsConnected(device_id_arg); + ErrorOr output = api->GetConnectionState(device_id_arg); if (output.has_error()) { reply(WrapError(output.error())); return; @@ -1123,14 +1123,14 @@ void UniversalBleCallbackChannel::OnValueChanged( void UniversalBleCallbackChannel::OnConnectionChanged( const std::string& device_id_arg, - int64_t state_arg, + bool connected_arg, std::function&& on_success, std::function&& on_error) { const std::string channel_name = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged" + message_channel_suffix_; BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ EncodableValue(device_id_arg), - EncodableValue(state_arg), + EncodableValue(connected_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) { std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); diff --git a/windows/src/generated/universal_ble.g.h b/windows/src/generated/universal_ble.g.h index cf59743..30b8173 100644 --- a/windows/src/generated/universal_ble.g.h +++ b/windows/src/generated/universal_ble.g.h @@ -313,7 +313,7 @@ class UniversalBlePlatformChannel { virtual void GetSystemDevices( const flutter::EncodableList& with_services, std::function reply)> result) = 0; - virtual ErrorOr IsConnected(const std::string& device_id) = 0; + virtual ErrorOr GetConnectionState(const std::string& device_id) = 0; // The codec used by UniversalBlePlatformChannel. static const flutter::StandardMessageCodec& GetCodec(); @@ -383,7 +383,7 @@ class UniversalBleCallbackChannel { std::function&& on_error); void OnConnectionChanged( const std::string& device_id, - int64_t state, + bool connected, std::function&& on_success, std::function&& on_error); diff --git a/windows/src/universal_ble_plugin.cpp b/windows/src/universal_ble_plugin.cpp index 5af46ae..c65afcc 100644 --- a/windows/src/universal_ble_plugin.cpp +++ b/windows/src/universal_ble_plugin.cpp @@ -164,13 +164,18 @@ namespace universal_ble } }; - ErrorOr UniversalBlePlugin::IsConnected(const std::string &device_id) + ErrorOr UniversalBlePlugin::GetConnectionState(const std::string &device_id) { auto it = connectedDevices.find(_str_to_mac_address(device_id)); if (it == connectedDevices.end()) - return false; + return static_cast(ConnectionState::disconnected); + auto deviceAgent = *it->second; - return deviceAgent.device.ConnectionStatus() == BluetoothConnectionStatus::Connected; + + if (deviceAgent.device.ConnectionStatus() == BluetoothConnectionStatus::Connected) + return static_cast(ConnectionState::connected); + else + return static_cast(ConnectionState::disconnected); } std::optional UniversalBlePlugin::Connect(const std::string &device_id) @@ -185,7 +190,7 @@ namespace universal_ble CleanConnection(deviceAddress); // TODO: send disconnect event only after disconnect is complete uiThreadHandler_.Post([deviceAddress] - { callbackChannel->OnConnectionChanged(_mac_address_to_str(deviceAddress), static_cast(ConnectionState::disconnected), SuccessCallback, ErrorCallback); }); + { callbackChannel->OnConnectionChanged(_mac_address_to_str(deviceAddress), false, SuccessCallback, ErrorCallback); }); return std::nullopt; }; @@ -1007,7 +1012,7 @@ namespace universal_ble { std::cout << "ConnectionLog: ConnectionFailed: Failed to get device" << std::endl; uiThreadHandler_.Post([bluetoothAddress] - { callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), static_cast(ConnectionState::disconnected), SuccessCallback, ErrorCallback); }); + { callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), false, SuccessCallback, ErrorCallback); }); co_return; } @@ -1017,7 +1022,7 @@ namespace universal_ble { std::cout << "ConnectionFailed: Failed to get services: " << GattCommunicationStatusToString(status) << std::endl; uiThreadHandler_.Post([bluetoothAddress] - { callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), static_cast(ConnectionState::disconnected), SuccessCallback, ErrorCallback); }); + { callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), false, SuccessCallback, ErrorCallback); }); co_return; } @@ -1054,7 +1059,7 @@ namespace universal_ble connectedDevices.insert(std::move(pair)); std::cout << "ConnectionLog: Connected" << std::endl; uiThreadHandler_.Post([bluetoothAddress] - { callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), static_cast(ConnectionState::connected), SuccessCallback, ErrorCallback); }); + { callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), true, SuccessCallback, ErrorCallback); }); } void UniversalBlePlugin::BluetoothLEDevice_ConnectionStatusChanged(BluetoothLEDevice sender, IInspectable args) @@ -1064,7 +1069,7 @@ namespace universal_ble CleanConnection(sender.BluetoothAddress()); auto bluetoothAddress = sender.BluetoothAddress(); uiThreadHandler_.Post([bluetoothAddress] - { callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), static_cast(ConnectionState::disconnected), SuccessCallback, ErrorCallback); }); + { callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), false, SuccessCallback, ErrorCallback); }); } } diff --git a/windows/src/universal_ble_plugin.h b/windows/src/universal_ble_plugin.h index d7a4a9b..d1f781c 100644 --- a/windows/src/universal_ble_plugin.h +++ b/windows/src/universal_ble_plugin.h @@ -119,7 +119,7 @@ namespace universal_ble AvailabilityState getAvailabilityStateFromRadio(RadioState radioState); std::string parsePairingFailError(Enumeration::DevicePairingResult result); winrt::fire_and_forget GetSystemDevicesAsync(std::vector with_services, - std::function reply)> result); + std::function reply)> result); winrt::fire_and_forget IsPairedAsync(std::string device_id, std::function reply)> result); winrt::fire_and_forget WriteAsync(GattCharacteristic characteristic, GattWriteOption writeOption, const std::vector &value, @@ -132,7 +132,7 @@ namespace universal_ble // UniversalBlePlatformChannel implementation. void GetBluetoothAvailabilityState(std::function reply)> result) override; void EnableBluetooth(std::function reply)> result) override; - ErrorOr IsConnected(const std::string& device_id) override; + ErrorOr GetConnectionState(const std::string &device_id) override; std::optional StartScan(const UniversalScanFilter *filter) override; std::optional StopScan() override; std::optional Connect(const std::string &device_id) override;