diff --git a/CHANGELOG.md b/CHANGELOG.md index 0919cf6..873b8fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,12 @@ ## 0.12.0 * BREAKING CHANGE: `unPair` is now `unpair` -* Add isPaired support on Apple -* `connect` will now return the connection result +* Add `pair()`, `isPaired` and `onPairingStateChange` support for Apple and web +* `connect()` and `pair()` now return a bool result * Add `PlatformConfig` property in `StartScan` * Add `WebConfig` property in `PlatformConfig` * Fix notifications for characteristics without cccd on Android * Add `connectionStream` API to get connection updates as stream +* Promote Linux to stable ## 0.11.1 * Trim spaces in UUIDs diff --git a/README.md b/README.md index 7f9375b..e468b7a 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE ### API Support Matrix -| API | Android | iOS | macOS | Windows | Linux (beta) | Web | +| API | Android | iOS | macOS | Windows | Linux | Web | | :------------------- | :-----: | :-: | :---: | :-----: | :----------: | :-: | | startScan/stopScan | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | connect/disconnect | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | @@ -31,11 +31,11 @@ A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE | readValue | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | writeValue | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | setNotifiable | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| pair | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | +| pair | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ⏺ | | unpair | ✔️ | ❌ | ❌ | ✔️ | ✔️ | ❌ | -| isPaired | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ? | -| getBluetoothAvailabilityState | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | -| onPairingStateChange | ✔️ | ❌ | ❌ | ✔️ | ✔️ | ❌ | +| isPaired | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| onPairingStateChange | ✔️ | ⏺ | ⏺ | ✔️ | ✔️ | ⏺ | +| getBluetoothAvailabilityState | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | | enableBluetooth | ✔️ | ❌ | ❌ | ✔️ | ✔️ | ❌ | | onAvailabilityChange | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | requestMtu | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | @@ -196,18 +196,22 @@ UniversalBle.setNotifiable(deviceId, serviceId, characteristicId, BleInputProper ```dart // Pair -UniversalBle.pair(deviceId); +bool? isPaired = await UniversalBle.pair(deviceId); // Returns true if successful -// Get the pairing result +// For Apple and Web, you can optionally pass a pairingCommand if you know an encrypted read or write characteristic. +// Not supported on Web/Windows +UniversalBle.pair(deviceId, pairingCommand: BleCommand(service:"SERVICE", characteristic:"ENCRYPTED_CHARACTERISTIC",)); + +// Receive pairing state changes UniversalBle.onPairingStateChange = (String deviceId, bool isPaired, String? error) { - // Handle Pairing state change + // Handle pairing state change } // Unpair UniversalBle.unpair(deviceId); // Check current pairing state -bool isPaired = UniversalBle.isPaired(deviceId); +bool? isPaired = UniversalBle.isPaired(deviceId); ``` ### Bluetooth Availability 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 de99323..70cb15a 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 @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v18.0.1), do not edit directly. +// Autogenerated from Pigeon (v21.1.0), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @@ -72,7 +72,7 @@ data class UniversalBleScanResult ( } } fun toList(): List { - return listOf( + return listOf( deviceId, name, isPaired, @@ -99,7 +99,7 @@ data class UniversalBleService ( } } fun toList(): List { - return listOf( + return listOf( uuid, characteristics, ) @@ -121,7 +121,7 @@ data class UniversalBleCharacteristic ( } } fun toList(): List { - return listOf( + return listOf( uuid, properties, ) @@ -147,7 +147,7 @@ data class UniversalScanFilter ( } } fun toList(): List { - return listOf( + return listOf( withServices, withManufacturerData, ) @@ -171,22 +171,16 @@ data class UniversalManufacturerDataFilter ( } } fun toList(): List { - return listOf( + return listOf( companyIdentifier, data, mask, ) } } - -private object UniversalBlePlatformChannelCodec : StandardMessageCodec() { +private object UniversalBlePigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { - 128.toByte() -> { - return (readValue(buffer) as? List)?.let { - UniversalBleCharacteristic.fromList(it) - } - } 129.toByte() -> { return (readValue(buffer) as? List)?.let { UniversalBleScanResult.fromList(it) @@ -199,7 +193,7 @@ private object UniversalBlePlatformChannelCodec : StandardMessageCodec() { } 131.toByte() -> { return (readValue(buffer) as? List)?.let { - UniversalManufacturerDataFilter.fromList(it) + UniversalBleCharacteristic.fromList(it) } } 132.toByte() -> { @@ -207,15 +201,16 @@ private object UniversalBlePlatformChannelCodec : StandardMessageCodec() { UniversalScanFilter.fromList(it) } } + 133.toByte() -> { + return (readValue(buffer) as? List)?.let { + UniversalManufacturerDataFilter.fromList(it) + } + } else -> super.readValueOfType(type, buffer) } } override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { - is UniversalBleCharacteristic -> { - stream.write(128) - writeValue(stream, value.toList()) - } is UniversalBleScanResult -> { stream.write(129) writeValue(stream, value.toList()) @@ -224,7 +219,7 @@ private object UniversalBlePlatformChannelCodec : StandardMessageCodec() { stream.write(130) writeValue(stream, value.toList()) } - is UniversalManufacturerDataFilter -> { + is UniversalBleCharacteristic -> { stream.write(131) writeValue(stream, value.toList()) } @@ -232,11 +227,16 @@ private object UniversalBlePlatformChannelCodec : StandardMessageCodec() { stream.write(132) writeValue(stream, value.toList()) } + is UniversalManufacturerDataFilter -> { + stream.write(133) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } } + /** * Flutter -> Native * @@ -255,7 +255,7 @@ interface UniversalBlePlatformChannel { fun requestMtu(deviceId: String, expectedMtu: Long, callback: (Result) -> Unit) fun writeValue(deviceId: String, service: String, characteristic: String, value: ByteArray, bleOutputProperty: Long, callback: (Result) -> Unit) fun isPaired(deviceId: String, callback: (Result) -> Unit) - fun pair(deviceId: String) + fun pair(deviceId: String, callback: (Result) -> Unit) fun unPair(deviceId: String) fun getSystemDevices(withServices: List, callback: (Result>) -> Unit) fun getConnectionState(deviceId: String): Long @@ -263,16 +263,17 @@ interface UniversalBlePlatformChannel { companion object { /** The codec used by UniversalBlePlatformChannel. */ val codec: MessageCodec by lazy { - UniversalBlePlatformChannelCodec + UniversalBlePigeonCodec } /** Sets up an instance of `UniversalBlePlatformChannel` to handle messages through the `binaryMessenger`. */ + @JvmOverloads fun setUp(binaryMessenger: BinaryMessenger, api: UniversalBlePlatformChannel?, messageChannelSuffix: String = "") { val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getBluetoothAvailabilityState$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { _, reply -> - api.getBluetoothAvailabilityState() { result: Result -> + api.getBluetoothAvailabilityState{ result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -290,7 +291,7 @@ interface UniversalBlePlatformChannel { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.enableBluetooth$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { _, reply -> - api.enableBluetooth() { result: Result -> + api.enableBluetooth{ result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -312,7 +313,7 @@ interface UniversalBlePlatformChannel { val filterArg = args[0] as UniversalScanFilter? val wrapped: List = try { api.startScan(filterArg) - listOf(null) + listOf(null) } catch (exception: Throwable) { wrapError(exception) } @@ -328,7 +329,7 @@ interface UniversalBlePlatformChannel { channel.setMessageHandler { _, reply -> val wrapped: List = try { api.stopScan() - listOf(null) + listOf(null) } catch (exception: Throwable) { wrapError(exception) } @@ -346,7 +347,7 @@ interface UniversalBlePlatformChannel { val deviceIdArg = args[0] as String val wrapped: List = try { api.connect(deviceIdArg) - listOf(null) + listOf(null) } catch (exception: Throwable) { wrapError(exception) } @@ -364,7 +365,7 @@ interface UniversalBlePlatformChannel { val deviceIdArg = args[0] as String val wrapped: List = try { api.disconnect(deviceIdArg) - listOf(null) + listOf(null) } catch (exception: Throwable) { wrapError(exception) } @@ -508,13 +509,15 @@ interface UniversalBlePlatformChannel { channel.setMessageHandler { message, reply -> val args = message as List val deviceIdArg = args[0] as String - val wrapped: List = try { - api.pair(deviceIdArg) - listOf(null) - } catch (exception: Throwable) { - wrapError(exception) + api.pair(deviceIdArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(wrapResult(data)) + } } - reply.reply(wrapped) } } else { channel.setMessageHandler(null) @@ -528,7 +531,7 @@ interface UniversalBlePlatformChannel { val deviceIdArg = args[0] as String val wrapped: List = try { api.unPair(deviceIdArg) - listOf(null) + listOf(null) } catch (exception: Throwable) { wrapError(exception) } @@ -565,7 +568,7 @@ interface UniversalBlePlatformChannel { val args = message as List val deviceIdArg = args[0] as String val wrapped: List = try { - listOf(api.getConnectionState(deviceIdArg)) + listOf(api.getConnectionState(deviceIdArg)) } catch (exception: Throwable) { wrapError(exception) } @@ -578,28 +581,6 @@ interface UniversalBlePlatformChannel { } } } -private object UniversalBleCallbackChannelCodec : StandardMessageCodec() { - override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { - return when (type) { - 128.toByte() -> { - return (readValue(buffer) as? List)?.let { - UniversalBleScanResult.fromList(it) - } - } - else -> super.readValueOfType(type, buffer) - } - } - override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { - when (value) { - is UniversalBleScanResult -> { - stream.write(128) - writeValue(stream, value.toList()) - } - else -> super.writeValue(stream, value) - } - } -} - /** * Native -> Flutter * @@ -609,7 +590,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger, companion object { /** The codec used by UniversalBleCallbackChannel. */ val codec: MessageCodec by lazy { - UniversalBleCallbackChannelCodec + UniversalBlePigeonCodec } } fun onAvailabilityChanged(stateArg: Long, callback: (Result) -> Unit) 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 608ace5..58f3f3b 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt @@ -25,7 +25,7 @@ import java.util.UUID private const val TAG = "UniversalBlePlugin" val knownGatts = mutableListOf() -val ccdCharacteristic = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb") +val ccdCharacteristic: UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb") enum class BleConnectionState(val value: Long) { Connected(0), @@ -157,8 +157,7 @@ fun Int.parseGattErrorCode(): String? { } fun UniversalScanFilter.toScanFilters(): List { - var scanFilters: ArrayList = arrayListOf() - + val scanFilters: ArrayList = arrayListOf() // Add withServices Filter for (service in this.withServices) { try { 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 bc77e50..d4a8be7 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.app.Activity 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 @@ -37,14 +36,18 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), private lateinit var context: Context private var activity: Activity? = null private lateinit var bluetoothManager: BluetoothManager + private val cachedServicesMap = mutableMapOf>() + private val devicesStateMap = mutableMapOf() + + // Flutter Futures + private var bluetoothEnableRequestFuture: ((Result) -> Unit)? = null private val discoverServicesFutureList = mutableListOf() private val mtuResultFutureList = mutableListOf() private val readResultFutureList = mutableListOf() private val writeResultFutureList = mutableListOf() private val subscriptionResultFutureList = mutableListOf() - private val cachedServicesMap = mutableMapOf>() - private val devicesStateMap = mutableMapOf() - private var bluetoothEnableRequestFuture: ((Result) -> Unit)? = null + private val pairResultFutures = mutableMapOf) -> Unit>() + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { UniversalBlePlatformChannel.setUp(flutterPluginBinding.binaryMessenger, this) @@ -504,16 +507,46 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), callback(Result.success(remoteDevice.bondState == BOND_BONDED)) } - override fun pair(deviceId: String) { - val remoteDevice = - bluetoothManager.adapter.getRemoteDevice(deviceId) - if (remoteDevice.bondState == BOND_NONE) { - if (!remoteDevice.createBond()) { - throw FlutterError("Failed", "Failed to pair", null) + override fun pair(deviceId: String, callback: (Result) -> Unit) { + try { + val remoteDevice = bluetoothManager.adapter.getRemoteDevice(deviceId) + val pendingFuture = pairResultFutures.remove(deviceId) + + // If already paired, return and complete pending futures + if (remoteDevice.bondState == BOND_BONDED) { + pendingFuture?.let { it(Result.success(true)) } + callback(Result.success(true)) + return } - } else { - throw FlutterError("AlreadyPair", "Already paired", null) + + // throw error if we already have a pending future + if (pendingFuture != null) { + callback( + Result.failure( + FlutterError( + "InProgress", + "Pairing already in progress", + null + ) + ) + ) + return + } + + // Make a Pair request and complete future from Pair Update intent + if (remoteDevice.createBond()) { + pairResultFutures[deviceId] = callback + } else { + callback(Result.failure(FlutterError("Failed", "Failed to pair", null))) + } + } catch (e: Exception) { + callback( + Result.failure( + FlutterError("Failed", e.toString(), null) + ) + ) } + } override fun unPair(deviceId: String) { @@ -702,6 +735,14 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), } } + private fun onBondStateUpdate(deviceId: String, bonded: Boolean, error: String? = null) { + val future = pairResultFutures.remove(deviceId) + future?.let { it(Result.success(bonded)) } + mainThreadHandler?.post { + callbackChannel?.onPairStateChange(deviceId, bonded, error) {} + } + } + private val broadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action == BluetoothAdapter.ACTION_STATE_CHANGED) { @@ -712,52 +753,37 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), ) {} } } else if (intent.action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) { - val device: BluetoothDevice = + val device: BluetoothDevice? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { intent.getParcelableExtra( BluetoothDevice.EXTRA_DEVICE, BluetoothDevice::class.java ) - ?: return } else { @Suppress("DEPRECATION") - intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) ?: return + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) } + if (device == null) { + Log.e(TAG, "No device found in ACTION_BOND_STATE_CHANGED intent") + return + } // get pairing failed error when (intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR)) { BluetoothDevice.BOND_BONDING -> { Log.v(TAG, "${device.address} BOND_BONDING") } - BluetoothDevice.ERROR -> { - mainThreadHandler?.post { - callbackChannel?.onPairStateChange( - device.address, - false, - "No pairing state received" - ) {} - } + BluetoothDevice.BOND_BONDED -> { + onBondStateUpdate(device.address, true) } - BOND_BONDED -> { - mainThreadHandler?.post { - callbackChannel?.onPairStateChange( - device.address, - true, - null - ) {} - } + BluetoothDevice.ERROR -> { + onBondStateUpdate(device.address, false, "Failed to Pair") } - - BOND_NONE -> { - mainThreadHandler?.post { - callbackChannel?.onPairStateChange( - device.address, - false, - null - ) {} - } + BluetoothDevice.BOND_NONE -> { + Log.e(TAG, "${device.address} BOND_NONE") + onBondStateUpdate(device.address, false) } } } diff --git a/darwin/Classes/UniversalBle.g.swift b/darwin/Classes/UniversalBle.g.swift index d6c1e45..c2db1f0 100644 --- a/darwin/Classes/UniversalBle.g.swift +++ b/darwin/Classes/UniversalBle.g.swift @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v18.0.1), do not edit directly. +// Autogenerated from Pigeon (v21.1.0), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -11,11 +11,36 @@ import Foundation #error("Unsupported platform.") #endif +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + private func wrapResult(_ result: Any?) -> [Any?] { return [result] } private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } if let flutterError = error as? FlutterError { return [ flutterError.code, @@ -30,8 +55,8 @@ private func wrapError(_ error: Any) -> [Any?] { ] } -private func createConnectionError(withChannelName channelName: String) -> FlutterError { - return FlutterError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") } private func isNullish(_ value: Any?) -> Bool { @@ -183,63 +208,63 @@ struct UniversalManufacturerDataFilter { ] } } - -private class UniversalBlePlatformChannelCodecReader: FlutterStandardReader { +private class UniversalBlePigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { - case 128: - return UniversalBleCharacteristic.fromList(self.readValue() as! [Any?]) case 129: return UniversalBleScanResult.fromList(self.readValue() as! [Any?]) case 130: return UniversalBleService.fromList(self.readValue() as! [Any?]) case 131: - return UniversalManufacturerDataFilter.fromList(self.readValue() as! [Any?]) + return UniversalBleCharacteristic.fromList(self.readValue() as! [Any?]) case 132: return UniversalScanFilter.fromList(self.readValue() as! [Any?]) + case 133: + return UniversalManufacturerDataFilter.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } } } -private class UniversalBlePlatformChannelCodecWriter: FlutterStandardWriter { +private class UniversalBlePigeonCodecWriter: FlutterStandardWriter { override func writeValue(_ value: Any) { - if let value = value as? UniversalBleCharacteristic { - super.writeByte(128) - super.writeValue(value.toList()) - } else if let value = value as? UniversalBleScanResult { + if let value = value as? UniversalBleScanResult { super.writeByte(129) super.writeValue(value.toList()) } else if let value = value as? UniversalBleService { super.writeByte(130) super.writeValue(value.toList()) - } else if let value = value as? UniversalManufacturerDataFilter { + } else if let value = value as? UniversalBleCharacteristic { super.writeByte(131) super.writeValue(value.toList()) } else if let value = value as? UniversalScanFilter { super.writeByte(132) super.writeValue(value.toList()) + } else if let value = value as? UniversalManufacturerDataFilter { + super.writeByte(133) + super.writeValue(value.toList()) } else { super.writeValue(value) } } } -private class UniversalBlePlatformChannelCodecReaderWriter: FlutterStandardReaderWriter { +private class UniversalBlePigeonCodecReaderWriter: FlutterStandardReaderWriter { override func reader(with data: Data) -> FlutterStandardReader { - return UniversalBlePlatformChannelCodecReader(data: data) + return UniversalBlePigeonCodecReader(data: data) } override func writer(with data: NSMutableData) -> FlutterStandardWriter { - return UniversalBlePlatformChannelCodecWriter(data: data) + return UniversalBlePigeonCodecWriter(data: data) } } -class UniversalBlePlatformChannelCodec: FlutterStandardMessageCodec { - static let shared = UniversalBlePlatformChannelCodec(readerWriter: UniversalBlePlatformChannelCodecReaderWriter()) +class UniversalBlePigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = UniversalBlePigeonCodec(readerWriter: UniversalBlePigeonCodecReaderWriter()) } + /// Flutter -> Native /// /// Generated protocol from Pigeon that represents a handler of messages from Flutter. @@ -256,7 +281,7 @@ protocol UniversalBlePlatformChannel { func requestMtu(deviceId: String, expectedMtu: Int64, completion: @escaping (Result) -> Void) func writeValue(deviceId: String, service: String, characteristic: String, value: FlutterStandardTypedData, bleOutputProperty: Int64, completion: @escaping (Result) -> Void) func isPaired(deviceId: String, completion: @escaping (Result) -> Void) - func pair(deviceId: String) throws + func pair(deviceId: String, completion: @escaping (Result) -> Void) func unPair(deviceId: String) throws func getSystemDevices(withServices: [String], completion: @escaping (Result<[UniversalBleScanResult], Error>) -> Void) func getConnectionState(deviceId: String) throws -> Int64 @@ -264,8 +289,7 @@ protocol UniversalBlePlatformChannel { /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. class UniversalBlePlatformChannelSetup { - /// The codec used by UniversalBlePlatformChannel. - static var codec: FlutterStandardMessageCodec { UniversalBlePlatformChannelCodec.shared } + static var codec: FlutterStandardMessageCodec { UniversalBlePigeonCodec.shared } /// Sets up an instance of `UniversalBlePlatformChannel` to handle messages through the `binaryMessenger`. static func setUp(binaryMessenger: FlutterBinaryMessenger, api: UniversalBlePlatformChannel?, messageChannelSuffix: String = "") { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" @@ -474,11 +498,13 @@ class UniversalBlePlatformChannelSetup { pairChannel.setMessageHandler { message, reply in let args = message as! [Any?] let deviceIdArg = args[0] as! String - do { - try api.pair(deviceId: deviceIdArg) - reply(wrapResult(nil)) - } catch { - reply(wrapError(error)) + api.pair(deviceId: deviceIdArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } } } } else { @@ -533,51 +559,15 @@ class UniversalBlePlatformChannelSetup { } } } -private class UniversalBleCallbackChannelCodecReader: FlutterStandardReader { - override func readValue(ofType type: UInt8) -> Any? { - switch type { - case 128: - return UniversalBleScanResult.fromList(self.readValue() as! [Any?]) - default: - return super.readValue(ofType: type) - } - } -} - -private class UniversalBleCallbackChannelCodecWriter: FlutterStandardWriter { - override func writeValue(_ value: Any) { - if let value = value as? UniversalBleScanResult { - super.writeByte(128) - super.writeValue(value.toList()) - } else { - super.writeValue(value) - } - } -} - -private class UniversalBleCallbackChannelCodecReaderWriter: FlutterStandardReaderWriter { - override func reader(with data: Data) -> FlutterStandardReader { - return UniversalBleCallbackChannelCodecReader(data: data) - } - - override func writer(with data: NSMutableData) -> FlutterStandardWriter { - return UniversalBleCallbackChannelCodecWriter(data: data) - } -} - -class UniversalBleCallbackChannelCodec: FlutterStandardMessageCodec { - static let shared = UniversalBleCallbackChannelCodec(readerWriter: UniversalBleCallbackChannelCodecReaderWriter()) -} - /// Native -> Flutter /// /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. protocol UniversalBleCallbackChannelProtocol { - func onAvailabilityChanged(state stateArg: Int64, completion: @escaping (Result) -> Void) - 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, connected connectedArg: Bool, completion: @escaping (Result) -> Void) + func onAvailabilityChanged(state stateArg: Int64, completion: @escaping (Result) -> Void) + 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, connected connectedArg: Bool, completion: @escaping (Result) -> Void) } class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol { private let binaryMessenger: FlutterBinaryMessenger @@ -586,10 +576,10 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol { self.binaryMessenger = binaryMessenger self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" } - var codec: FlutterStandardMessageCodec { - return UniversalBleCallbackChannelCodec.shared + var codec: UniversalBlePigeonCodec { + return UniversalBlePigeonCodec.shared } - func onAvailabilityChanged(state stateArg: Int64, completion: @escaping (Result) -> Void) { + func onAvailabilityChanged(state stateArg: Int64, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) channel.sendMessage([stateArg] as [Any?]) { response in @@ -601,13 +591,13 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { completion(.success(Void())) } } } - func onPairStateChange(deviceId deviceIdArg: String, isPaired isPairedArg: Bool, error errorArg: String?, completion: @escaping (Result) -> Void) { + func onPairStateChange(deviceId deviceIdArg: String, isPaired isPairedArg: Bool, error errorArg: String?, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) channel.sendMessage([deviceIdArg, isPairedArg, errorArg] as [Any?]) { response in @@ -619,13 +609,13 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { completion(.success(Void())) } } } - func onScanResult(result resultArg: UniversalBleScanResult, completion: @escaping (Result) -> Void) { + func onScanResult(result resultArg: UniversalBleScanResult, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) channel.sendMessage([resultArg] as [Any?]) { response in @@ -637,13 +627,13 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { completion(.success(Void())) } } } - func onValueChanged(deviceId deviceIdArg: String, characteristicId characteristicIdArg: String, value valueArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) { + func onValueChanged(deviceId deviceIdArg: String, characteristicId characteristicIdArg: String, value valueArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) channel.sendMessage([deviceIdArg, characteristicIdArg, valueArg] as [Any?]) { response in @@ -655,13 +645,13 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { completion(.success(Void())) } } } - func onConnectionChanged(deviceId deviceIdArg: String, connected connectedArg: Bool, 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, connectedArg] as [Any?]) { response in @@ -673,7 +663,7 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { completion(.success(Void())) } diff --git a/darwin/Classes/UniversalBlePlugin.swift b/darwin/Classes/UniversalBlePlugin.swift index ceda265..a827af2 100644 --- a/darwin/Classes/UniversalBlePlugin.swift +++ b/darwin/Classes/UniversalBlePlugin.swift @@ -280,8 +280,8 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral completion(Result.failure(FlutterError(code: "NotSupported", message: nil, details: nil))) } - func pair(deviceId _: String) throws { - throw FlutterError(code: "Implemented in Dart", message: nil, details: nil) + func pair(deviceId _: String, completion: @escaping (Result) -> Void){ + completion(Result.failure(FlutterError(code: "Implemented in Dart", message: nil, details: nil))) } func unPair(deviceId _: String) throws { diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 88dd507..c3cbcfb 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -41,7 +41,6 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.navideck.universal_ble_example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. @@ -53,7 +52,6 @@ android { buildTypes { release { - // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } diff --git a/example/lib/data/capabilities.dart b/example/lib/data/capabilities.dart deleted file mode 100644 index a2606bc..0000000 --- a/example/lib/data/capabilities.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:flutter/foundation.dart'; - -class Capabilities { - static bool requiresRuntimePermission = - !Platform.isWeb && !Platform.isWindows && !Platform.isLinux; - - static bool supportsBluetoothEnableApi = - !Platform.isWeb && !Platform.isCupertino; - - static bool supportsConnectedDevicesApi = !Platform.isWeb; - - static bool supportsPairingApi = !Platform.isWeb; - - static bool supportsRequestMtuApi = !Platform.isWeb; -} - -class Platform { - static bool isWeb = kIsWeb; - static bool isIOS = !isWeb && defaultTargetPlatform == TargetPlatform.iOS; - static bool isAndroid = - !isWeb && defaultTargetPlatform == TargetPlatform.android; - static bool isMacos = !isWeb && defaultTargetPlatform == TargetPlatform.macOS; - static bool isWindows = - !isWeb && defaultTargetPlatform == TargetPlatform.windows; - static bool isLinux = !isWeb && defaultTargetPlatform == TargetPlatform.linux; - static bool get isMobile => isIOS || isAndroid; - static bool get isCupertino => isIOS || isMacos; - static bool get isDesktop => isWindows || isLinux || isMacos; -} diff --git a/example/lib/data/mock_universal_ble.dart b/example/lib/data/mock_universal_ble.dart index f5945b5..d1c9ec3 100644 --- a/example/lib/data/mock_universal_ble.dart +++ b/example/lib/data/mock_universal_ble.dart @@ -65,7 +65,11 @@ class MockUniversalBle extends UniversalBlePlatform { @override Future readValue( - String deviceId, String service, String characteristic) async { + String deviceId, + String service, + String characteristic, { + final Duration? timeout, + }) async { await Future.delayed(const Duration(milliseconds: 500)); return _serviceValue; } @@ -98,8 +102,9 @@ class MockUniversalBle extends UniversalBlePlatform { } @override - Future pair(String deviceId) async { + Future pair(String deviceId) async { updatePairingState(deviceId, true, null); + return true; } @override diff --git a/example/lib/home/home.dart b/example/lib/home/home.dart index e0769ad..a423eca 100644 --- a/example/lib/home/home.dart +++ b/example/lib/home/home.dart @@ -1,10 +1,7 @@ // ignore_for_file: use_build_context_synchronously -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:universal_ble/universal_ble.dart'; -import 'package:universal_ble_example/data/capabilities.dart'; import 'package:universal_ble_example/data/mock_universal_ble.dart'; import 'package:universal_ble_example/home/widgets/scan_filter_widget.dart'; import 'package:universal_ble_example/home/widgets/scanned_devices_placeholder_widget.dart'; @@ -52,7 +49,7 @@ class _MyAppState extends State { }; UniversalBle.onScanResult = (result) { - log(result.toString()); + // log(result.toString()); int index = _bleDevices.indexWhere((e) => e.deviceId == result.deviceId); if (index == -1) { _bleDevices.add(result); @@ -148,7 +145,7 @@ class _MyAppState extends State { }); }, ), - if (Capabilities.supportsBluetoothEnableApi && + if (BleCapabilities.supportsBluetoothEnableApi && bleAvailabilityState == AvailabilityState.poweredOff) PlatformButton( text: 'Enable Bluetooth', @@ -159,7 +156,7 @@ class _MyAppState extends State { ); }, ), - if (Capabilities.requiresRuntimePermission) + if (BleCapabilities.requiresRuntimePermission) PlatformButton( text: 'Check Permissions', onPressed: () async { @@ -172,7 +169,7 @@ class _MyAppState extends State { } }, ), - if (Capabilities.supportsConnectedDevicesApi) + if (BleCapabilities.supportsConnectedDevicesApi) PlatformButton( text: 'Connected Devices', onPressed: () async { diff --git a/example/lib/home/widgets/scanned_item_widget.dart b/example/lib/home/widgets/scanned_item_widget.dart index 31c0302..b7b7848 100644 --- a/example/lib/home/widgets/scanned_item_widget.dart +++ b/example/lib/home/widgets/scanned_item_widget.dart @@ -1,7 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:universal_ble/universal_ble.dart'; -import 'package:universal_ble_example/data/capabilities.dart'; class ScannedItemWidget extends StatelessWidget { final BleDevice bleDevice; @@ -31,9 +30,7 @@ class ScannedItemWidget extends StatelessWidget { Visibility( visible: manufacturerData != null, child: Text( - Platform.isWeb || Platform.isDesktop - ? manufacturerData.toString() - : 'ManufacturerCompanyId: ${manufacturerData?.companyIdRadix16}', + 'CompanyIdentifier: ${manufacturerData.toString()} (${manufacturerData?.companyIdRadix16})', ), ), bleDevice.isPaired == true diff --git a/example/lib/peripheral_details/peripheral_detail_page.dart b/example/lib/peripheral_details/peripheral_detail_page.dart index 6c97417..480b3f7 100644 --- a/example/lib/peripheral_details/peripheral_detail_page.dart +++ b/example/lib/peripheral_details/peripheral_detail_page.dart @@ -6,7 +6,6 @@ import 'package:convert/convert.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:universal_ble/universal_ble.dart'; -import 'package:universal_ble_example/data/capabilities.dart'; import 'package:universal_ble_example/peripheral_details/widgets/result_widget.dart'; import 'package:universal_ble_example/peripheral_details/widgets/services_list_widget.dart'; import 'package:universal_ble_example/widgets/platform_button.dart'; @@ -86,9 +85,9 @@ class _PeripheralDetailPageState extends State { String deviceId, bool isPaired, String? error) { print('isPaired $deviceId, $isPaired'); if (error != null && error.isNotEmpty) { - _addLog("PairStateChangeError", "(Paired: $isPaired): $error "); + _addLog("PairingStateChangeError", "(Paired: $isPaired): $error "); } else { - _addLog("PairStateChange - isPaired", isPaired); + _addLog("PairingStateChange - isPaired", isPaired); } } @@ -357,7 +356,7 @@ class _PeripheralDetailPageState extends State { }, text: 'Connection State', ), - if (Capabilities.supportsRequestMtuApi) + if (BleCapabilities.supportsRequestMtuApi) PlatformButton( enabled: isConnected, onPressed: () async { @@ -408,29 +407,29 @@ class _PeripheralDetailPageState extends State { BleInputProperty.disabled), text: 'Unsubscribe', ), - PlatformButton( - onPressed: () async { - await UniversalBle.pair( - widget.deviceId, - // pairingCommand: BleCommand( - // service: - // "", - // characteristic: - // "", - // ), - ); - }, - text: 'Pair', + Visibility( + visible: BleCapabilities.supportsInAppPairing, + child: PlatformButton( + onPressed: () async { + bool? pairingResult = await UniversalBle.pair( + widget.deviceId, + // pairingCommand: BleCommand( + // service: "", + // characteristic: "", + // ), + ); + _addLog("Pairing Result", pairingResult); + }, + text: 'Pair', + ), ), PlatformButton( onPressed: () async { bool? isPaired = await UniversalBle.isPaired( widget.deviceId, - // pairingCheckCommand: BleCommand( - // service: - // "", - // characteristic: - // "", + // pairingCommand: BleCommand( + // service: "", + // characteristic: "", // ), ); _addLog('IsPaired', isPaired); diff --git a/lib/src/ble_command_queue.dart b/lib/src/ble_command_queue.dart index 14f9d24..245d4d4 100644 --- a/lib/src/ble_command_queue.dart +++ b/lib/src/ble_command_queue.dart @@ -15,12 +15,12 @@ class BleCommandQueue { Future Function() command, { bool withTimeout = true, String? deviceId, + Duration? timeout, }) { - Duration? duration = withTimeout ? timeout : null; - + Duration? duration = timeout ?? (withTimeout ? this.timeout : null); return switch (queueType) { - QueueType.global => _queue().add(command, timeout: duration), - QueueType.perDevice => _queue(deviceId).add(command, timeout: duration), + QueueType.global => _queue().add(command, duration), + QueueType.perDevice => _queue(deviceId).add(command, duration), QueueType.none => duration != null ? command().timeout(duration) : command(), }; diff --git a/lib/src/models/ble_capabilities.dart b/lib/src/models/ble_capabilities.dart new file mode 100644 index 0000000..c6be310 --- /dev/null +++ b/lib/src/models/ble_capabilities.dart @@ -0,0 +1,36 @@ +import 'package:flutter/foundation.dart'; + +class BleCapabilities { + /// Returns true if pairing is possible either by API or by encrypted characteristic. + /// All platforms support this except Windows/Web. + static final bool supportsInAppPairing = + _triggersPairingWithEncryptedChar || hasSystemPairingApi; + + static final _triggersPairingWithEncryptedChar = + defaultTargetPlatform != TargetPlatform.windows; + + static bool hasSystemPairingApi = !kIsWeb && + (defaultTargetPlatform == TargetPlatform.android || + defaultTargetPlatform == TargetPlatform.windows || + defaultTargetPlatform == TargetPlatform.linux); + + static bool requiresRuntimePermission = + !_Platform.isWeb && !_Platform.isWindows && !_Platform.isLinux; + + static bool supportsBluetoothEnableApi = + !_Platform.isWeb && !_Platform.isCupertino; + + static bool supportsConnectedDevicesApi = !_Platform.isWeb; + + static bool supportsRequestMtuApi = !_Platform.isWeb; +} + +class _Platform { + static bool isWeb = kIsWeb; + static bool isIOS = !isWeb && defaultTargetPlatform == TargetPlatform.iOS; + static bool isMacos = !isWeb && defaultTargetPlatform == TargetPlatform.macOS; + static bool isWindows = + !isWeb && defaultTargetPlatform == TargetPlatform.windows; + static bool isLinux = !isWeb && defaultTargetPlatform == TargetPlatform.linux; + static bool get isCupertino => isIOS || isMacos; +} diff --git a/lib/src/models/model_exports.dart b/lib/src/models/model_exports.dart index 207b7c4..2a10350 100644 --- a/lib/src/models/model_exports.dart +++ b/lib/src/models/model_exports.dart @@ -8,4 +8,5 @@ export 'package:universal_ble/src/models/availability_state.dart'; export 'package:universal_ble/src/models/ble_connection_state.dart'; export 'package:universal_ble/src/models/ble_device.dart'; export 'package:universal_ble/src/models/ble_command.dart'; +export 'package:universal_ble/src/models/ble_capabilities.dart'; diff --git a/lib/src/queue.dart b/lib/src/queue.dart index 313d9e4..1203bce 100644 --- a/lib/src/queue.dart +++ b/lib/src/queue.dart @@ -11,7 +11,7 @@ class Queue { final List<_QueuedFuture> _nextCycle = []; Function(int)? onRemainingItemsUpdate; - Future add(Future Function() closure, {Duration? timeout}) { + Future add(Future Function() closure, [Duration? timeout]) { if (_isCancelled) throw Exception('Queue Cancelled'); final completer = Completer(); _nextCycle.add(_QueuedFuture(closure, completer, timeout)); diff --git a/lib/src/universal_ble.dart b/lib/src/universal_ble.dart index f4e160c..508c6db 100644 --- a/lib/src/universal_ble.dart +++ b/lib/src/universal_ble.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:universal_ble/src/ble_command_queue.dart'; @@ -157,14 +156,17 @@ class UniversalBle { static Future readValue( String deviceId, String service, - String characteristic, - ) async { + String characteristic, { + final Duration? timeout, + }) async { return await _bleCommandQueue.queueCommand( () => _platform.readValue( deviceId, BleUuidParser.string(service), BleUuidParser.string(characteristic), + timeout: timeout ?? _bleCommandQueue.timeout, ), + timeout: timeout, deviceId: deviceId, ); } @@ -200,45 +202,49 @@ class UniversalBle { } /// Check if a device is paired. - /// Returns null on `Apple` and `Web` when no `bleCommand` is passed. /// - /// On Apple and Web, you can optionally pass a pairingCheckCommand if you know an encrypted read or write characteristic. - /// It will trigger pairing if the device is not already paired. + /// For Apple and Web, you can optionally pass a pairingCommand if you know an encrypted read or write characteristic. /// It will return true/false if it manages to execute the command. + /// Note that it will trigger pairing if the device is not already paired. + /// + /// Returns null on `Apple` and `Web` when no `bleCommand` is passed. static Future isPaired( String deviceId, { - BleCommand? pairingCheckCommand, + BleCommand? pairingCommand, }) async { - bool lacksPairingApi = kIsWeb || Platform.isMacOS || Platform.isIOS; - if (lacksPairingApi && pairingCheckCommand == null) { - return null; + if (BleCapabilities.hasSystemPairingApi) { + return _bleCommandQueue.queueCommand( + () => _platform.isPaired(deviceId), + deviceId: deviceId, + ); + } else if (pairingCommand != null) { + return _connectAndExecuteBleCommand(deviceId, pairingCommand, + updateCallbackValue: false); } - - return lacksPairingApi - ? await _connectAndExecuteBleCommand(deviceId, pairingCheckCommand) - : await _bleCommandQueue.queueCommand( - () => _platform.isPaired(deviceId), - deviceId: deviceId, - ); + return null; } /// Pair a device. - /// It might throw an error if device is already paired. /// - /// On Apple, it only works on devices with encrypted characteristics. - /// It throws an error if there is no readable characteristic. + /// On `Apple` and `Web`, it only works on devices with encrypted characteristics. + /// It returns null if there is no readable characteristic. /// /// You can optionally pass a pairingCommand if you know an encrypted read or write characteristic. - static Future pair( + /// If you do, it returns true if it can successfully execute the command after pairing. + /// + /// Throws UnsupportedError on `Web/Windows` + static Future pair( String deviceId, { BleCommand? pairingCommand, - }) => - kIsWeb || Platform.isMacOS || Platform.isIOS - ? _connectAndExecuteBleCommand(deviceId, pairingCommand) - : _bleCommandQueue.queueCommand( - () => _platform.pair(deviceId), - deviceId: deviceId, - ); + }) async { + if (!BleCapabilities.supportsInAppPairing) { + throw UnsupportedError("Not supported"); + } + if (BleCapabilities.hasSystemPairingApi) { + return _platform.pair(deviceId); + } + return _connectAndExecuteBleCommand(deviceId, pairingCommand); + } /// Unpair a device. /// It might throw an error if device is not paired. @@ -302,8 +308,9 @@ class UniversalBle { static Future _connectAndExecuteBleCommand( String deviceId, - BleCommand? bleCommand, - ) async { + BleCommand? bleCommand, { + bool updateCallbackValue = false, + }) async { try { if (await getConnectionState(deviceId) != BleConnectionState.connected) { await connect(deviceId); @@ -315,16 +322,20 @@ class UniversalBle { await _attemptPairingReadingAll(deviceId, services); return null; } else { - await _executeBleCommand(deviceId, services, bleCommand); - _platform.updatePairingState(deviceId, true, null); - return true; + bool commandResult = + await _executeBleCommand(deviceId, services, bleCommand); + if (updateCallbackValue) { + _platform.updatePairingState(deviceId, commandResult, null); + } + return commandResult; } } catch (e) { UniversalBlePlatform.logInfo( "FailedToPerform EncryptedCharOperation: $e", ); - // Probably failed to pair, Notify callback - _platform.updatePairingState(deviceId, false, e.toString()); + if (updateCallbackValue) { + _platform.updatePairingState(deviceId, false, e.toString()); + } return false; } } @@ -341,7 +352,12 @@ class UniversalBle { for (BleCharacteristic characteristic in service.characteristics) { if (characteristic.properties.contains(CharacteristicProperty.read)) { containsReadCharacteristics = true; - await readValue(deviceId, service.uuid, characteristic.uuid); + await readValue( + deviceId, + service.uuid, + characteristic.uuid, + timeout: const Duration(seconds: 30), + ); } } } @@ -351,7 +367,7 @@ class UniversalBle { } } - static Future _executeBleCommand( + static Future _executeBleCommand( String deviceId, List services, BleCommand bleCommand, @@ -371,7 +387,7 @@ class UniversalBle { } if (characteristic == null) { - throw "BleCommand char not found"; + return false; } // Check if BleCommand Supports Read or Write @@ -383,7 +399,7 @@ class UniversalBle { bleOutputProperty = BleOutputProperty.withoutResponse; } else if (!characteristic.properties .contains(CharacteristicProperty.read)) { - throw "BleCommand does not support read or write operations"; + return false; } Uint8List? value = bleCommand.writeValue; @@ -401,8 +417,10 @@ class UniversalBle { deviceId, bleCommand.service, bleCommand.characteristic, + timeout: const Duration(seconds: 30), ); } + return true; } /// Get updates of remaining items of a queue. diff --git a/lib/src/universal_ble_linux/universal_ble_linux.dart b/lib/src/universal_ble_linux/universal_ble_linux.dart index 5cb453d..1cfa442 100644 --- a/lib/src/universal_ble_linux/universal_ble_linux.dart +++ b/lib/src/universal_ble_linux/universal_ble_linux.dart @@ -214,7 +214,11 @@ class UniversalBleLinux extends UniversalBlePlatform { @override Future readValue( - String deviceId, String service, String characteristic) async { + String deviceId, + String service, + String characteristic, { + final Duration? timeout, + }) async { try { final c = _getCharacteristic(deviceId, service, characteristic); final data = await c.readValue(); @@ -270,11 +274,16 @@ class UniversalBleLinux extends UniversalBlePlatform { } @override - Future pair(String deviceId) async { + Future pair(String deviceId) async { BlueZDevice device = _findDeviceById(deviceId); - device.pair().onError((error, _) { + try { + if (device.paired) return true; + await device.pair(); + return true; + } catch (error) { updatePairingState(deviceId, false, error.toString()); - }); + return false; + } } @override diff --git a/lib/src/universal_ble_pigeon/universal_ble.g.dart b/lib/src/universal_ble_pigeon/universal_ble.g.dart index 6065d33..e80dfc4 100644 --- a/lib/src/universal_ble_pigeon/universal_ble.g.dart +++ b/lib/src/universal_ble_pigeon/universal_ble.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v18.0.1), do not edit directly. +// Autogenerated from Pigeon (v21.1.0), 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 @@ -15,8 +15,7 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse( - {Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -98,8 +97,7 @@ class UniversalBleService { result as List; return UniversalBleService( uuid: result[0]! as String, - characteristics: - (result[1] as List?)?.cast(), + characteristics: (result[1] as List?)?.cast(), ); } } @@ -152,8 +150,7 @@ class UniversalScanFilter { result as List; return UniversalScanFilter( withServices: (result[0] as List?)!.cast(), - withManufacturerData: (result[1] as List?)! - .cast(), + withManufacturerData: (result[1] as List?)!.cast(), ); } } @@ -189,25 +186,26 @@ class UniversalManufacturerDataFilter { } } -class _UniversalBlePlatformChannelCodec extends StandardMessageCodec { - const _UniversalBlePlatformChannelCodec(); + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is UniversalBleCharacteristic) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is UniversalBleScanResult) { + if (value is UniversalBleScanResult) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is UniversalBleService) { + } else if (value is UniversalBleService) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is UniversalManufacturerDataFilter) { + } else if (value is UniversalBleCharacteristic) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is UniversalScanFilter) { + } else if (value is UniversalScanFilter) { buffer.putUint8(132); writeValue(buffer, value.encode()); + } else if (value is UniversalManufacturerDataFilter) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -216,16 +214,16 @@ class _UniversalBlePlatformChannelCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: - return UniversalBleCharacteristic.decode(readValue(buffer)!); - case 129: + case 129: return UniversalBleScanResult.decode(readValue(buffer)!); - case 130: + case 130: return UniversalBleService.decode(readValue(buffer)!); - case 131: - return UniversalManufacturerDataFilter.decode(readValue(buffer)!); - case 132: + case 131: + return UniversalBleCharacteristic.decode(readValue(buffer)!); + case 132: return UniversalScanFilter.decode(readValue(buffer)!); + case 133: + return UniversalManufacturerDataFilter.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -237,23 +235,18 @@ class UniversalBlePlatformChannel { /// Constructor for [UniversalBlePlatformChannel]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - UniversalBlePlatformChannel( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + UniversalBlePlatformChannel({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : __pigeon_binaryMessenger = binaryMessenger, - __pigeon_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + __pigeon_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec pigeonChannelCodec = - _UniversalBlePlatformChannelCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); final String __pigeon_messageChannelSuffix; Future getBluetoothAvailabilityState() async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getBluetoothAvailabilityState$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getBluetoothAvailabilityState$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -279,10 +272,8 @@ class UniversalBlePlatformChannel { } Future enableBluetooth() async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.enableBluetooth$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.enableBluetooth$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -308,10 +299,8 @@ class UniversalBlePlatformChannel { } Future startScan(UniversalScanFilter? filter) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -332,10 +321,8 @@ class UniversalBlePlatformChannel { } Future stopScan() async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopScan$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopScan$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -356,10 +343,8 @@ class UniversalBlePlatformChannel { } Future connect(String deviceId) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.connect$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.connect$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -380,10 +365,8 @@ class UniversalBlePlatformChannel { } Future disconnect(String deviceId) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disconnect$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disconnect$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -403,19 +386,15 @@ class UniversalBlePlatformChannel { } } - Future setNotifiable(String deviceId, String service, - String characteristic, int bleInputProperty) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setNotifiable$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + Future setNotifiable(String deviceId, String service, String characteristic, int bleInputProperty) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setNotifiable$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = await __pigeon_channel.send( - [deviceId, service, characteristic, bleInputProperty]) - as List?; + final List? __pigeon_replyList = + await __pigeon_channel.send([deviceId, service, characteristic, bleInputProperty]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -430,10 +409,8 @@ class UniversalBlePlatformChannel { } Future> discoverServices(String deviceId) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.discoverServices$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.discoverServices$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -454,23 +431,19 @@ class UniversalBlePlatformChannel { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)! - .cast(); + return (__pigeon_replyList[0] as List?)!.cast(); } } - Future readValue( - String deviceId, String service, String characteristic) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readValue$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + Future readValue(String deviceId, String service, String characteristic) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readValue$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = await __pigeon_channel - .send([deviceId, service, characteristic]) as List?; + final List? __pigeon_replyList = + await __pigeon_channel.send([deviceId, service, characteristic]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -490,16 +463,14 @@ class UniversalBlePlatformChannel { } Future requestMtu(String deviceId, int expectedMtu) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestMtu$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestMtu$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = await __pigeon_channel - .send([deviceId, expectedMtu]) as List?; + final List? __pigeon_replyList = + await __pigeon_channel.send([deviceId, expectedMtu]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -518,24 +489,15 @@ class UniversalBlePlatformChannel { } } - Future writeValue(String deviceId, String service, - String characteristic, Uint8List value, int bleOutputProperty) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.writeValue$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + Future writeValue(String deviceId, String service, String characteristic, Uint8List value, int bleOutputProperty) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.writeValue$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); - final List? __pigeon_replyList = await __pigeon_channel - .send([ - deviceId, - service, - characteristic, - value, - bleOutputProperty - ]) as List?; + final List? __pigeon_replyList = + await __pigeon_channel.send([deviceId, service, characteristic, value, bleOutputProperty]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -550,10 +512,8 @@ class UniversalBlePlatformChannel { } Future isPaired(String deviceId) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPaired$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPaired$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -578,11 +538,9 @@ class UniversalBlePlatformChannel { } } - Future pair(String deviceId) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.pair$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + Future pair(String deviceId) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.pair$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -597,16 +555,19 @@ class UniversalBlePlatformChannel { message: __pigeon_replyList[1] as String?, details: __pigeon_replyList[2], ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { - return; + return (__pigeon_replyList[0] as bool?)!; } } Future unPair(String deviceId) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.unPair$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.unPair$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -626,12 +587,9 @@ class UniversalBlePlatformChannel { } } - Future> getSystemDevices( - List withServices) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getSystemDevices$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + Future> getSystemDevices(List withServices) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getSystemDevices$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -652,16 +610,13 @@ class UniversalBlePlatformChannel { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)! - .cast(); + return (__pigeon_replyList[0] as List?)!.cast(); } } Future getConnectionState(String deviceId) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectionState$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectionState$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -687,33 +642,9 @@ class UniversalBlePlatformChannel { } } -class _UniversalBleCallbackChannelCodec extends StandardMessageCodec { - const _UniversalBleCallbackChannelCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is UniversalBleScanResult) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return UniversalBleScanResult.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } - } -} - /// Native -> Flutter abstract class UniversalBleCallbackChannel { - static const MessageCodec pigeonChannelCodec = - _UniversalBleCallbackChannelCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); void onAvailabilityChanged(int state); @@ -721,30 +652,22 @@ abstract class UniversalBleCallbackChannel { void onScanResult(UniversalBleScanResult result); - void onValueChanged( - String deviceId, String characteristicId, Uint8List value); + void onValueChanged(String deviceId, String characteristicId, Uint8List value); void onConnectionChanged(String deviceId, bool connected); - static void setUp( - UniversalBleCallbackChannel? api, { - BinaryMessenger? binaryMessenger, - String messageChannelSuffix = '', - }) { - messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + static void setUp(UniversalBleCallbackChannel? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged was null.'); + 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged was null.'); final List args = (message as List?)!; final int? arg_state = (args[0] as int?); assert(arg_state != null, @@ -754,25 +677,22 @@ abstract class UniversalBleCallbackChannel { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange was null.'); + 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange was null.'); final List args = (message as List?)!; final String? arg_deviceId = (args[0] as String?); assert(arg_deviceId != null, @@ -786,28 +706,24 @@ abstract class UniversalBleCallbackChannel { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult was null.'); + 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult was null.'); final List args = (message as List?)!; - final UniversalBleScanResult? arg_result = - (args[0] as UniversalBleScanResult?); + final UniversalBleScanResult? arg_result = (args[0] as UniversalBleScanResult?); assert(arg_result != null, 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult was null, expected non-null UniversalBleScanResult.'); try { @@ -815,25 +731,22 @@ abstract class UniversalBleCallbackChannel { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged was null.'); + 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged was null.'); final List args = (message as List?)!; final String? arg_deviceId = (args[0] as String?); assert(arg_deviceId != null, @@ -845,30 +758,26 @@ abstract class UniversalBleCallbackChannel { assert(arg_value != null, 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged was null, expected non-null Uint8List.'); try { - api.onValueChanged( - arg_deviceId!, arg_characteristicId!, arg_value!); + api.onValueChanged(arg_deviceId!, arg_characteristicId!, arg_value!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel< - Object?>( - 'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged$messageChannelSuffix', - pigeonChannelCodec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged was null.'); + 'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged was null.'); final List args = (message as List?)!; final String? arg_deviceId = (args[0] as String?); assert(arg_deviceId != null, @@ -881,9 +790,8 @@ abstract class UniversalBleCallbackChannel { return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse( - error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); } }); } 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 e6213fd..b8dcd35 100644 --- a/lib/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart +++ b/lib/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:flutter/foundation.dart'; import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart'; import 'package:universal_ble/universal_ble.dart'; @@ -23,7 +21,9 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform { @override Future enableBluetooth() { - if (Platform.isIOS || Platform.isMacOS) throw UnimplementedError(); + if (!BleCapabilities.supportsBluetoothEnableApi) { + throw UnsupportedError("Not supported"); + } return _channel.enableBluetooth(); } @@ -77,7 +77,11 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform { @override Future readValue( - String deviceId, String service, String characteristic) { + String deviceId, + String service, + String characteristic, { + final Duration? timeout, + }) { return _channel.readValue(deviceId, service, characteristic); } @@ -105,7 +109,7 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform { Future isPaired(String deviceId) => _channel.isPaired(deviceId); @override - Future pair(String deviceId) => _channel.pair(deviceId); + Future pair(String deviceId) => _channel.pair(deviceId); @override Future unpair(String deviceId) => _channel.unPair(deviceId); diff --git a/lib/src/universal_ble_platform_interface.dart b/lib/src/universal_ble_platform_interface.dart index 2ef89af..1adce7c 100644 --- a/lib/src/universal_ble_platform_interface.dart +++ b/lib/src/universal_ble_platform_interface.dart @@ -30,7 +30,11 @@ abstract class UniversalBlePlatform { String characteristic, BleInputProperty bleInputProperty); Future readValue( - String deviceId, String service, String characteristic); + String deviceId, + String service, + String characteristic, { + final Duration? timeout, + }); Future writeValue( String deviceId, @@ -43,7 +47,7 @@ abstract class UniversalBlePlatform { Future isPaired(String deviceId); - Future pair(String deviceId); + Future pair(String deviceId); Future unpair(String deviceId); diff --git a/lib/src/universal_ble_web/universal_ble_web.dart b/lib/src/universal_ble_web/universal_ble_web.dart index 02bc233..5e5e942 100644 --- a/lib/src/universal_ble_web/universal_ble_web.dart +++ b/lib/src/universal_ble_web/universal_ble_web.dart @@ -214,8 +214,9 @@ class UniversalBleWeb extends UniversalBlePlatform { Future readValue( String deviceId, String service, - String characteristic, - ) async { + String characteristic, { + final Duration? timeout, + }) async { var bleCharacteristic = await _getBleCharacteristic( deviceId: deviceId, serviceId: service, @@ -225,8 +226,10 @@ class UniversalBleWeb extends UniversalBlePlatform { throw Exception( 'Characteristic $characteristic for service $service not found'); } - var data = await bleCharacteristic.readValue(); - return data.buffer.asUint8List(); + var data = timeout != null + ? bleCharacteristic.readValue(timeout: timeout) + : bleCharacteristic.readValue(); + return (await data).buffer.asUint8List(); } /// `Unimplemented` @@ -241,7 +244,7 @@ class UniversalBleWeb extends UniversalBlePlatform { } @override - Future pair(String deviceId) { + Future pair(String deviceId) { throw UnimplementedError(); } diff --git a/pigeon/universal_ble.dart b/pigeon/universal_ble.dart index e6b68f3..9f1d2af 100644 --- a/pigeon/universal_ble.dart +++ b/pigeon/universal_ble.dart @@ -68,7 +68,8 @@ abstract class UniversalBlePlatformChannel { @async bool isPaired(String deviceId); - void pair(String deviceId); + @async + bool pair(String deviceId); void unPair(String deviceId); diff --git a/pubspec.yaml b/pubspec.yaml index f2703cb..7a20523 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,7 +30,7 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 - pigeon: ^18.0.1 + pigeon: ^21.1.0 flutter: plugin: diff --git a/windows/src/generated/universal_ble.g.cpp b/windows/src/generated/universal_ble.g.cpp index 768fa4b..d14b422 100644 --- a/windows/src/generated/universal_ble.g.cpp +++ b/windows/src/generated/universal_ble.g.cpp @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v18.0.1), do not edit directly. +// Autogenerated from Pigeon (v21.1.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -390,36 +390,31 @@ UniversalManufacturerDataFilter UniversalManufacturerDataFilter::FromEncodableLi } -UniversalBlePlatformChannelCodecSerializer::UniversalBlePlatformChannelCodecSerializer() {} +PigeonCodecSerializer::PigeonCodecSerializer() {} -EncodableValue UniversalBlePlatformChannelCodecSerializer::ReadValueOfType( +EncodableValue PigeonCodecSerializer::ReadValueOfType( uint8_t type, flutter::ByteStreamReader* stream) const { switch (type) { - case 128: - return CustomEncodableValue(UniversalBleCharacteristic::FromEncodableList(std::get(ReadValue(stream)))); case 129: return CustomEncodableValue(UniversalBleScanResult::FromEncodableList(std::get(ReadValue(stream)))); case 130: return CustomEncodableValue(UniversalBleService::FromEncodableList(std::get(ReadValue(stream)))); case 131: - return CustomEncodableValue(UniversalManufacturerDataFilter::FromEncodableList(std::get(ReadValue(stream)))); + return CustomEncodableValue(UniversalBleCharacteristic::FromEncodableList(std::get(ReadValue(stream)))); case 132: return CustomEncodableValue(UniversalScanFilter::FromEncodableList(std::get(ReadValue(stream)))); + case 133: + return CustomEncodableValue(UniversalManufacturerDataFilter::FromEncodableList(std::get(ReadValue(stream)))); default: return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); - } + } } -void UniversalBlePlatformChannelCodecSerializer::WriteValue( +void PigeonCodecSerializer::WriteValue( const EncodableValue& value, flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { - if (custom_value->type() == typeid(UniversalBleCharacteristic)) { - stream->WriteByte(128); - WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); - return; - } if (custom_value->type() == typeid(UniversalBleScanResult)) { stream->WriteByte(129); WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); @@ -430,9 +425,9 @@ void UniversalBlePlatformChannelCodecSerializer::WriteValue( WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(UniversalManufacturerDataFilter)) { + if (custom_value->type() == typeid(UniversalBleCharacteristic)) { stream->WriteByte(131); - WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } if (custom_value->type() == typeid(UniversalScanFilter)) { @@ -440,13 +435,18 @@ void UniversalBlePlatformChannelCodecSerializer::WriteValue( WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } + if (custom_value->type() == typeid(UniversalManufacturerDataFilter)) { + stream->WriteByte(133); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } } flutter::StandardCodecSerializer::WriteValue(value, stream); } /// The codec used by UniversalBlePlatformChannel. const flutter::StandardMessageCodec& UniversalBlePlatformChannel::GetCodec() { - return flutter::StandardMessageCodec::GetInstance(&UniversalBlePlatformChannelCodecSerializer::GetInstance()); + return flutter::StandardMessageCodec::GetInstance(&PigeonCodecSerializer::GetInstance()); } // Sets up an instance of `UniversalBlePlatformChannel` to handle messages through the `binary_messenger`. @@ -512,7 +512,7 @@ void UniversalBlePlatformChannel::SetUp( try { const auto& args = std::get(message); const auto& encodable_filter_arg = args.at(0); - const auto* filter_arg = &(std::any_cast(std::get(encodable_filter_arg))); + const auto* filter_arg = encodable_filter_arg.IsNull() ? nullptr : &(std::any_cast(std::get(encodable_filter_arg))); std::optional output = api->StartScan(filter_arg); if (output.has_value()) { reply(WrapError(output.value())); @@ -852,14 +852,15 @@ void UniversalBlePlatformChannel::SetUp( return; } const auto& device_id_arg = std::get(encodable_device_id_arg); - std::optional output = api->Pair(device_id_arg); - if (output.has_value()) { - reply(WrapError(output.value())); - return; - } - EncodableList wrapped; - wrapped.push_back(EncodableValue()); - reply(EncodableValue(std::move(wrapped))); + api->Pair(device_id_arg, [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + }); } catch (const std::exception& exception) { reply(WrapError(exception.what())); } @@ -971,33 +972,6 @@ EncodableValue UniversalBlePlatformChannel::WrapError(const FlutterError& error) }); } - -UniversalBleCallbackChannelCodecSerializer::UniversalBleCallbackChannelCodecSerializer() {} - -EncodableValue UniversalBleCallbackChannelCodecSerializer::ReadValueOfType( - uint8_t type, - flutter::ByteStreamReader* stream) const { - switch (type) { - case 128: - return CustomEncodableValue(UniversalBleScanResult::FromEncodableList(std::get(ReadValue(stream)))); - default: - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); - } -} - -void UniversalBleCallbackChannelCodecSerializer::WriteValue( - const EncodableValue& value, - flutter::ByteStreamWriter* stream) const { - if (const CustomEncodableValue* custom_value = std::get_if(&value)) { - if (custom_value->type() == typeid(UniversalBleScanResult)) { - stream->WriteByte(128); - WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); - return; - } - } - flutter::StandardCodecSerializer::WriteValue(value, stream); -} - // Generated class from Pigeon that represents Flutter messages that can be called from C++. UniversalBleCallbackChannel::UniversalBleCallbackChannel(flutter::BinaryMessenger* binary_messenger) : binary_messenger_(binary_messenger), @@ -1010,7 +984,7 @@ UniversalBleCallbackChannel::UniversalBleCallbackChannel( message_channel_suffix_(message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : "") {} const flutter::StandardMessageCodec& UniversalBleCallbackChannel::GetCodec() { - return flutter::StandardMessageCodec::GetInstance(&UniversalBleCallbackChannelCodecSerializer::GetInstance()); + return flutter::StandardMessageCodec::GetInstance(&PigeonCodecSerializer::GetInstance()); } void UniversalBleCallbackChannel::OnAvailabilityChanged( diff --git a/windows/src/generated/universal_ble.g.h b/windows/src/generated/universal_ble.g.h index 30b8173..ec7d190 100644 --- a/windows/src/generated/universal_ble.g.h +++ b/windows/src/generated/universal_ble.g.h @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v18.0.1), do not edit directly. +// Autogenerated from Pigeon (v21.1.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_UNIVERSAL_BLE_G_H_ @@ -105,9 +105,8 @@ class UniversalBleScanResult { static UniversalBleScanResult FromEncodableList(const flutter::EncodableList& list); flutter::EncodableList ToEncodableList() const; friend class UniversalBlePlatformChannel; - friend class UniversalBlePlatformChannelCodecSerializer; friend class UniversalBleCallbackChannel; - friend class UniversalBleCallbackChannelCodecSerializer; + friend class PigeonCodecSerializer; std::string device_id_; std::optional name_; std::optional is_paired_; @@ -142,9 +141,8 @@ class UniversalBleService { static UniversalBleService FromEncodableList(const flutter::EncodableList& list); flutter::EncodableList ToEncodableList() const; friend class UniversalBlePlatformChannel; - friend class UniversalBlePlatformChannelCodecSerializer; friend class UniversalBleCallbackChannel; - friend class UniversalBleCallbackChannelCodecSerializer; + friend class PigeonCodecSerializer; std::string uuid_; std::optional characteristics_; @@ -170,9 +168,8 @@ class UniversalBleCharacteristic { static UniversalBleCharacteristic FromEncodableList(const flutter::EncodableList& list); flutter::EncodableList ToEncodableList() const; friend class UniversalBlePlatformChannel; - friend class UniversalBlePlatformChannelCodecSerializer; friend class UniversalBleCallbackChannel; - friend class UniversalBleCallbackChannelCodecSerializer; + friend class PigeonCodecSerializer; std::string uuid_; flutter::EncodableList properties_; @@ -200,9 +197,8 @@ class UniversalScanFilter { static UniversalScanFilter FromEncodableList(const flutter::EncodableList& list); flutter::EncodableList ToEncodableList() const; friend class UniversalBlePlatformChannel; - friend class UniversalBlePlatformChannelCodecSerializer; friend class UniversalBleCallbackChannel; - friend class UniversalBleCallbackChannelCodecSerializer; + friend class PigeonCodecSerializer; flutter::EncodableList with_services_; flutter::EncodableList with_manufacturer_data_; @@ -238,20 +234,19 @@ class UniversalManufacturerDataFilter { static UniversalManufacturerDataFilter FromEncodableList(const flutter::EncodableList& list); flutter::EncodableList ToEncodableList() const; friend class UniversalBlePlatformChannel; - friend class UniversalBlePlatformChannelCodecSerializer; friend class UniversalBleCallbackChannel; - friend class UniversalBleCallbackChannelCodecSerializer; + friend class PigeonCodecSerializer; std::optional company_identifier_; std::optional> data_; std::optional> mask_; }; -class UniversalBlePlatformChannelCodecSerializer : public flutter::StandardCodecSerializer { +class PigeonCodecSerializer : public flutter::StandardCodecSerializer { public: - UniversalBlePlatformChannelCodecSerializer(); - inline static UniversalBlePlatformChannelCodecSerializer& GetInstance() { - static UniversalBlePlatformChannelCodecSerializer sInstance; + PigeonCodecSerializer(); + inline static PigeonCodecSerializer& GetInstance() { + static PigeonCodecSerializer sInstance; return sInstance; } @@ -308,7 +303,9 @@ class UniversalBlePlatformChannel { virtual void IsPaired( const std::string& device_id, std::function reply)> result) = 0; - virtual std::optional Pair(const std::string& device_id) = 0; + virtual void Pair( + const std::string& device_id, + std::function reply)> result) = 0; virtual std::optional UnPair(const std::string& device_id) = 0; virtual void GetSystemDevices( const flutter::EncodableList& with_services, @@ -332,25 +329,6 @@ class UniversalBlePlatformChannel { UniversalBlePlatformChannel() = default; }; -class UniversalBleCallbackChannelCodecSerializer : public flutter::StandardCodecSerializer { - public: - UniversalBleCallbackChannelCodecSerializer(); - inline static UniversalBleCallbackChannelCodecSerializer& GetInstance() { - static UniversalBleCallbackChannelCodecSerializer sInstance; - return sInstance; - } - - void WriteValue( - const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; - - protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, - flutter::ByteStreamReader* stream) const override; - -}; - // Native -> Flutter // // Generated class from Pigeon that represents Flutter messages that can be called from C++. diff --git a/windows/src/universal_ble_plugin.cpp b/windows/src/universal_ble_plugin.cpp index c65afcc..b5b8eff 100644 --- a/windows/src/universal_ble_plugin.cpp +++ b/windows/src/universal_ble_plugin.cpp @@ -416,36 +416,20 @@ namespace universal_ble IsPairedAsync(device_id, result); }; - std::optional UniversalBlePlugin::Pair(const std::string &device_id) + void UniversalBlePlugin::Pair( + const std::string &device_id, + std::function reply)> result) { try { - auto device = async_get(BluetoothLEDevice::FromBluetoothAddressAsync(_str_to_mac_address(device_id))); - std::cout << "PairLog: Device to be paired was found" << std::endl; - auto deviceInformation = device.DeviceInformation(); - - if (deviceInformation.Pairing().IsPaired()) - { - return FlutterError("PairLog: Device is already paired"); - } - if (!deviceInformation.Pairing().CanPair()) - { - return FlutterError("PairLog: Device is not pairable"); - } - if (isWindows11OrGreater()) - { - PairAsync(device_id, deviceInformation); - } + PairAsync(device_id, result); else - { - CustomPairAsync(device_id, deviceInformation); - } - return std::nullopt; + CustomPairAsync(device_id, result); } catch (const FlutterError &err) { - return err; + result(err); } }; @@ -556,43 +540,69 @@ namespace universal_ble } } - winrt::fire_and_forget UniversalBlePlugin::PairAsync(std::string device_id, const DeviceInformation deviceInformation) + winrt::fire_and_forget UniversalBlePlugin::PairAsync( + std::string device_id, + std::function reply)> result) { try { - auto result = co_await deviceInformation.Pairing().PairAsync(); - std::cout << "PairLog: Received pairing status" << std::endl; - auto isPaired = result.Status() == Enumeration::DevicePairingResultStatus::Paired; - std::string errorStr = parsePairingFailError(result); - uiThreadHandler_.Post([device_id, isPaired, errorStr] - { callbackChannel->OnPairStateChange(device_id, isPaired, &errorStr, SuccessCallback, ErrorCallback); }); + auto device = co_await BluetoothLEDevice::FromBluetoothAddressAsync(_str_to_mac_address(device_id)); + auto deviceInformation = device.DeviceInformation(); + if (deviceInformation.Pairing().IsPaired()) + result(true); + else if (!deviceInformation.Pairing().CanPair()) + result(FlutterError("Device is not pairable")); + else + { + auto pairResult = co_await deviceInformation.Pairing().PairAsync(); + std::cout << "PairLog: Received pairing status" << std::endl; + bool isPaired = pairResult.Status() == Enumeration::DevicePairingResultStatus::Paired; + result(isPaired); + std::string errorStr = parsePairingFailError(pairResult); + uiThreadHandler_.Post([device_id, isPaired, errorStr] + { callbackChannel->OnPairStateChange(device_id, isPaired, &errorStr, SuccessCallback, ErrorCallback); }); + } } catch (...) { + result(false); std::cout << "PairLog: Unknown error" << std::endl; } } - winrt::fire_and_forget UniversalBlePlugin::CustomPairAsync(std::string device_id, const DeviceInformation deviceInformation) + winrt::fire_and_forget UniversalBlePlugin::CustomPairAsync( + std::string device_id, + std::function reply)> result) { try { - auto customPairing = deviceInformation.Pairing().Custom(); - winrt::event_token token = customPairing.PairingRequested({this, &UniversalBlePlugin::PairingRequestedHandler}); - std::cout << "PairLog: Trying to pair" << std::endl; - DevicePairingProtectionLevel protectionLevel = deviceInformation.Pairing().ProtectionLevel(); - // DevicePairingKinds => None, ConfirmOnly, DisplayPin, ProvidePin, ConfirmPinMatch, ProvidePasswordCredential - auto result = co_await customPairing.PairAsync(DevicePairingKinds::ConfirmOnly | DevicePairingKinds::ProvidePin, protectionLevel); - std::cout << "PairLog: Got Pair Result" << std::endl; - DevicePairingResultStatus status = result.Status(); - customPairing.PairingRequested(token); - auto isPaired = status == Enumeration::DevicePairingResultStatus::Paired; - std::string errorStr = parsePairingFailError(result); - uiThreadHandler_.Post([device_id, isPaired, errorStr] - { callbackChannel->OnPairStateChange(device_id, isPaired, &errorStr, SuccessCallback, ErrorCallback); }); + auto device = co_await BluetoothLEDevice::FromBluetoothAddressAsync(_str_to_mac_address(device_id)); + auto deviceInformation = device.DeviceInformation(); + if (deviceInformation.Pairing().IsPaired()) + result(true); + else if (!deviceInformation.Pairing().CanPair()) + result(FlutterError("Device is not pairable")); + else + { + auto customPairing = deviceInformation.Pairing().Custom(); + winrt::event_token token = customPairing.PairingRequested({this, &UniversalBlePlugin::PairingRequestedHandler}); + std::cout << "PairLog: Trying to pair" << std::endl; + DevicePairingProtectionLevel protectionLevel = deviceInformation.Pairing().ProtectionLevel(); + // DevicePairingKinds => None, ConfirmOnly, DisplayPin, ProvidePin, ConfirmPinMatch, ProvidePasswordCredential + auto pairResult = co_await customPairing.PairAsync(DevicePairingKinds::ConfirmOnly | DevicePairingKinds::ProvidePin, protectionLevel); + std::cout << "PairLog: Got Pair Result" << std::endl; + DevicePairingResultStatus status = pairResult.Status(); + customPairing.PairingRequested(token); + bool isPaired = status == Enumeration::DevicePairingResultStatus::Paired; + result(isPaired); + std::string errorStr = parsePairingFailError(pairResult); + uiThreadHandler_.Post([device_id, isPaired, errorStr] + { callbackChannel->OnPairStateChange(device_id, isPaired, &errorStr, SuccessCallback, ErrorCallback); }); + } } catch (...) { + result(false); std::cout << "PairLog Error: Pairing Failed" << std::endl; } } diff --git a/windows/src/universal_ble_plugin.h b/windows/src/universal_ble_plugin.h index d1f781c..a7548d0 100644 --- a/windows/src/universal_ble_plugin.h +++ b/windows/src/universal_ble_plugin.h @@ -125,8 +125,8 @@ namespace universal_ble const std::vector &value, std::function reply)> result); bool filterByManufacturerData(IVector deviceManufactureData); - winrt::fire_and_forget PairAsync(std::string device_id, const DeviceInformation deviceInformation); - winrt::fire_and_forget CustomPairAsync(std::string device_id, const DeviceInformation deviceInformation); + winrt::fire_and_forget PairAsync(std::string device_id, std::function reply)> result); + winrt::fire_and_forget CustomPairAsync(std::string device_id, std::function reply)> result); void PairingRequestedHandler(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs eventArgs); // UniversalBlePlatformChannel implementation. @@ -165,7 +165,9 @@ namespace universal_ble void IsPaired( const std::string &device_id, std::function reply)> result) override; - std::optional Pair(const std::string &device_id) override; + void Pair( + const std::string &device_id, + std::function reply)> result) override; std::optional UnPair(const std::string &device_id) override; void GetSystemDevices( const flutter::EncodableList &with_services,