From b74191bb973dcc430b57139192e53792d8d7df5f Mon Sep 17 00:00:00 2001 From: Rohit Sangwan Date: Tue, 11 Jun 2024 15:58:25 +0530 Subject: [PATCH] Implement pairing with dialog on windows (#46) * Implement pairing with dialog on windows * Minor fix * Add pin pairing request for windows 10 * Implement native windows dialog for asking pin pairing * Cleanups * Improve pairing dialog ui * Use PairAsync for Windows 11 * Fix OK button text * Improve pin entry layout Co-authored-by: Rohit Sangwan * Dialog adjustments * Make dialog non resizable * Make window topmost * Final adjustments * Update changelog * Release dialog resources properly * Release dialog resources properly * Remove duplicate import * Minor Fix --------- Co-authored-by: fotidim --- .vscode/settings.json | 26 +- CHANGELOG.md | 1 + .../peripheral_detail_page.dart | 2 + example/pubspec.lock | 82 +++--- lib/src/universal_ble.dart | 4 +- .../universal_ble_pigeon/universal_ble.g.dart | 247 ++++++------------ .../universal_ble_pigeon_channel.dart | 26 +- windows/CMakeLists.txt | 1 + windows/src/helper/utils.cpp | 34 ++- windows/src/helper/utils.h | 1 + windows/src/pin_entry.h | 134 ++++++++++ windows/src/universal_ble_plugin.cpp | 101 +++++-- windows/src/universal_ble_plugin.h | 5 +- 13 files changed, 418 insertions(+), 246 deletions(-) create mode 100644 windows/src/pin_entry.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 43e6ba0..3e47d4f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,27 +2,50 @@ "cSpell.words": [ "bluez", "BTLE", + "BTNHIGHLIGHT", "bytevc", "connnection", "Cupertino", + "DFACE", "DSIMPLEBLE", + "HFONT", + "HINSTANCE", + "HMENU", "hresult", "hstring", "HWND", "Inspectable", + "LOWORD", "LPARAM", + "lpfn", + "LPOSVERSIONINFO", + "lpsz", "LRESULT", + "MAXIMIZEBOX", "microtask", + "MINIMIZEBOX", "modalias", "msix", "navideck", + "NOMOVE", + "NOSIZE", "NSUUID", "NTDDI", + "ntdll", + "NTSTATUS", + "OSVERSIONINFOW", + "osvi", + "OVERLAPPEDWINDOW", "pairable", + "PNTSTATUS", + "PRTL", "rssi", + "SETFONT", + "SETTEXT", "simpleble", "subdata", "sublist", + "THICKFRAME", "unawaited", "UNIVERSALBLE", "Unpair", @@ -30,6 +53,7 @@ "unwatch", "uuidstr", "winrt", + "WNDCLASS", "WPARAM", "ZUUID" ], @@ -122,4 +146,4 @@ "future": "cpp", "regex": "cpp" } -} +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b3c1df..fc84d9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 0.9.12 * Add .perDevice queue * Improve code level documentation +* Support "ProvidePin" pairing on Windows 10/11 ## 0.9.11 * Add device name prefix filtering diff --git a/example/lib/peripheral_details/peripheral_detail_page.dart b/example/lib/peripheral_details/peripheral_detail_page.dart index 1f2326e..7708ef7 100644 --- a/example/lib/peripheral_details/peripheral_detail_page.dart +++ b/example/lib/peripheral_details/peripheral_detail_page.dart @@ -1,5 +1,7 @@ // ignore_for_file: avoid_print, depend_on_referenced_packages +import 'dart:async'; + import 'package:convert/convert.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/foundation.dart'; diff --git a/example/pubspec.lock b/example/pubspec.lock index b1361f9..c29902b 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" async: dependency: transitive description: @@ -69,26 +69,26 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" dbus: dependency: transitive description: name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.10" device_info_plus: dependency: "direct main" description: name: device_info_plus - sha256: "7035152271ff67b072a211152846e9f1259cf1be41e34cd3e0b5463d2d6b8419" + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.1.2" device_info_plus_platform_interface: dependency: transitive description: @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.2" file: dependency: transitive description: @@ -138,10 +138,10 @@ packages: dependency: "direct main" description: name: flutter_adaptive_scaffold - sha256: b3b7f8537b0af3e9982a0d1199d4d0e73b225672c867cecfb731c7f2bfc2f0f8 + sha256: "794791e6fc0cc23e375d07ea987e76cf7f0eb7cd753532e45dc35b7e90575e2d" url: "https://pub.dev" source: hosted - version: "0.1.7+2" + version: "0.1.11" flutter_driver: dependency: transitive description: flutter @@ -164,10 +164,10 @@ packages: dependency: transitive description: name: flutter_web_bluetooth - sha256: "0823eabf8643fb9adbf0d074b117a6b1357b312f6c0a34bd76cef0cd34b7df54" + sha256: "52ce64f65d7321c4bf6abfe9dac02fb888731339a5e0ad6de59fb916c20c9f02" url: "https://pub.dev" source: hosted - version: "0.2.2" + version: "0.2.3" flutter_web_plugins: dependency: transitive description: flutter @@ -187,34 +187,34 @@ packages: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "0.7.1" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: @@ -251,10 +251,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" path: dependency: transitive description: @@ -307,10 +307,10 @@ packages: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.2" platform: dependency: transitive description: @@ -323,10 +323,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.8" process: dependency: transitive description: @@ -392,10 +392,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -410,7 +410,7 @@ packages: path: ".." relative: true source: path - version: "0.9.10" + version: "0.9.12" vector_math: dependency: transitive description: @@ -423,10 +423,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" webdriver: dependency: transitive description: @@ -439,26 +439,26 @@ packages: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.5.1" win32_registry: dependency: transitive description: name: win32_registry - sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" + sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" xml: dependency: transitive description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.5.0" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.10.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.19.0" diff --git a/lib/src/universal_ble.dart b/lib/src/universal_ble.dart index 9cdda54..c3d63d0 100644 --- a/lib/src/universal_ble.dart +++ b/lib/src/universal_ble.dart @@ -24,7 +24,7 @@ class UniversalBle { /// Set how commands will be executed. By default, all commands are executed in a global queue (`QueueType.global`), /// with each command waiting for the previous one to finish. - /// + /// /// [QueueType.global] will execute commands of all devices in a single queue /// [QueueType.perDevice] will execute command of each device in separate queues /// [QueueType.none] will execute all commands in parallel @@ -33,7 +33,7 @@ class UniversalBle { UniversalBlePlatform.logInfo('Queue ${queueType.name}'); } - /// Get Bluetooth availability state + /// Get Bluetooth availability state /// To be notified of updates, set [onAvailabilityChange] listener static Future getBluetoothAvailabilityState() async { return await _bleCommandQueue.executeCommand( diff --git a/lib/src/universal_ble_pigeon/universal_ble.g.dart b/lib/src/universal_ble_pigeon/universal_ble.g.dart index f51b3aa..942bb97 100644 --- a/lib/src/universal_ble_pigeon/universal_ble.g.dart +++ b/lib/src/universal_ble_pigeon/universal_ble.g.dart @@ -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(), ); } } @@ -216,15 +213,15 @@ class _UniversalBlePlatformChannelCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + 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: + case 131: return UniversalManufacturerDataFilter.decode(readValue(buffer)!); - case 132: + case 132: return UniversalScanFilter.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -237,23 +234,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 = _UniversalBlePlatformChannelCodec(); 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 +271,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 +298,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 +320,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 +342,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 +364,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 +385,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 +408,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 +430,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 +462,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 +488,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 +511,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, @@ -579,10 +538,8 @@ 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( + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.pair$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -603,10 +560,8 @@ class UniversalBlePlatformChannel { } 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 +581,9 @@ class UniversalBlePlatformChannel { } } - Future> getConnectedDevices( - List withServices) async { - final String __pigeon_channelName = - 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectedDevices$__pigeon_messageChannelSuffix'; - final BasicMessageChannel __pigeon_channel = - BasicMessageChannel( + Future> getConnectedDevices(List withServices) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectedDevices$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, @@ -652,8 +604,7 @@ 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(); } } } @@ -673,7 +624,7 @@ class _UniversalBleCallbackChannelCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 128: + case 128: return UniversalBleScanResult.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -683,8 +634,7 @@ class _UniversalBleCallbackChannelCodec extends StandardMessageCodec { /// Native -> Flutter abstract class UniversalBleCallbackChannel { - static const MessageCodec pigeonChannelCodec = - _UniversalBleCallbackChannelCodec(); + static const MessageCodec pigeonChannelCodec = _UniversalBleCallbackChannelCodec(); void onAvailabilityChanged(int state); @@ -692,30 +642,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, int state); - 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, @@ -725,25 +667,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, @@ -757,28 +696,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 { @@ -786,25 +721,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, @@ -816,30 +748,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, @@ -852,9 +780,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 e6fb3f8..d8b8f75 100644 --- a/lib/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart +++ b/lib/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart @@ -116,18 +116,20 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform { /// To set listeners void _setupListeners() { - UniversalBleCallbackChannel.setUp(_UniversalBleCallbackHandler( - scanResult: (BleScanResult scanResult) => updateScanResult(scanResult), - availabilityChange: (AvailabilityState state) => - onAvailabilityChange?.call(state), - connectionChanged: (String deviceId, BleConnectionState state) => - onConnectionChanged?.call(deviceId, state), - valueChanged: - (String deviceId, String characteristicId, Uint8List value) => - onValueChanged?.call(deviceId, characteristicId, value), - pairStateChange: (String deviceId, bool isPaired, String? error) => - onPairingStateChange?.call(deviceId, isPaired, error), - )); + UniversalBleCallbackChannel.setUp( + _UniversalBleCallbackHandler( + scanResult: (BleScanResult scanResult) => updateScanResult(scanResult), + availabilityChange: (AvailabilityState state) => + onAvailabilityChange?.call(state), + connectionChanged: (String deviceId, BleConnectionState state) => + onConnectionChanged?.call(deviceId, state), + valueChanged: + (String deviceId, String characteristicId, Uint8List value) => + onValueChanged?.call(deviceId, characteristicId, value), + pairStateChange: (String deviceId, bool isPaired, String? error) => + onPairingStateChange?.call(deviceId, isPaired, error), + ), + ); } } diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index f01c79e..73267dd 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -41,6 +41,7 @@ list(APPEND PLUGIN_SOURCES "src/ui_thread_handler.hpp" "src/generated/universal_ble.g.cpp" "src/generated/universal_ble.g.h" + "src/pin_entry.h" ) add_library(${PLUGIN_NAME} SHARED diff --git a/windows/src/helper/utils.cpp b/windows/src/helper/utils.cpp index 4d8ebb2..4f9d4b2 100644 --- a/windows/src/helper/utils.cpp +++ b/windows/src/helper/utils.cpp @@ -3,7 +3,8 @@ #include #include #include - +#include +#include #include #if WDK_NTDDI_VERSION < NTDDI_WIN10_VB @@ -13,6 +14,9 @@ #endif #define MAC_ADDRESS_STR_LENGTH (size_t)17 +typedef LONG NTSTATUS, *PNTSTATUS; +#define STATUS_SUCCESS (0x00000000) +typedef NTSTATUS(WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); namespace universal_ble { @@ -126,4 +130,32 @@ namespace universal_ble return (numPtr[0] == 1); } + bool isWindows11OrGreater() + { + HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); + if (!hMod) + { + std::cout << "Failed to get ntdll" << std::endl; + return false; + } + + RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); + if (fxPtr == nullptr) + { + std::cout << "Failed to get RtlGetVersionPtr" << std::endl; + return false; + } + + RTL_OSVERSIONINFOW rove = {0}; + rove.dwOSVersionInfoSize = sizeof(rove); + if (STATUS_SUCCESS != fxPtr(&rove)) + { + std::cout << "Failed to get RTL_OSVERSIONINFOW" << std::endl; + return false; + } + + // Windows 11 => MajorVersion = 10 and BuildNumber >= 22000 + return rove.dwMajorVersion == 10 && rove.dwBuildNumber >= 22000; + } + } // namespace SimpleBLE diff --git a/windows/src/helper/utils.h b/windows/src/helper/utils.h index 4d14bf2..1dd31e8 100644 --- a/windows/src/helper/utils.h +++ b/windows/src/helper/utils.h @@ -26,6 +26,7 @@ namespace universal_ble std::string to_uuidstr(winrt::guid guid); bool isLittleEndian(); + bool isWindows11OrGreater(); /// To call async functions synchronously template diff --git a/windows/src/pin_entry.h b/windows/src/pin_entry.h new file mode 100644 index 0000000..e0c0e41 --- /dev/null +++ b/windows/src/pin_entry.h @@ -0,0 +1,134 @@ +#pragma once + +#include +#include + +namespace universal_ble +{ + typedef struct SizeAndPos_s + { + int x, y, width, height; + } SizeAndPos_t; + + const WORD ID_btnOK = 1; + const WORD ID_txtEdit = 4; + HWND txtEditHandle = NULL; + TCHAR textBoxText[16]; + + // Positions and dimensions of UI elements: X, Y, Width, Height + const SizeAndPos_t mainWindow = {150, 150, 450, 240}; + const SizeAndPos_t txtEdit = {50, 40, 320, 40}; + const SizeAndPos_t btnOK = {50, 100, 320, 40}; + + LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) + { + switch (msg) + { + case WM_CREATE: + { + txtEditHandle = CreateWindow( + TEXT("Edit"), TEXT(""), + WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NUMBER, + txtEdit.x, txtEdit.y, txtEdit.width, txtEdit.height, + hwnd, (HMENU)ID_txtEdit, NULL, NULL); + CreateWindow( + TEXT("Button"), TEXT("OK"), + WS_CHILD | WS_VISIBLE | BS_FLAT, + btnOK.x, btnOK.y, btnOK.width, btnOK.height, + hwnd, (HMENU)ID_btnOK, NULL, NULL); + break; + } + case WM_COMMAND: + if (LOWORD(wParam) == ID_btnOK) + { + GetWindowText(txtEditHandle, textBoxText, sizeof(textBoxText) / sizeof(TCHAR)); + DestroyWindow(hwnd); + } + break; + case WM_CLOSE: + DestroyWindow(hwnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + return 0; + } + + hstring askForPairingPin() + { + textBoxText[0] = '\0'; + HINSTANCE hInstance = GetModuleHandle(NULL); + MSG msg; + WNDCLASS mainWindowClass = {0}; + mainWindowClass.lpszClassName = TEXT("JRH.MainWindow"); + mainWindowClass.hInstance = hInstance; + mainWindowClass.hbrBackground = GetSysColorBrush(COLOR_BTNHIGHLIGHT); + mainWindowClass.lpfnWndProc = WndProc; + mainWindowClass.hCursor = LoadCursor(0, IDC_ARROW); + + // Register the window class + if (!RegisterClass(&mainWindowClass)) + { + std::cout << "PinPairDialog: Failed to register window class" << std::endl; + return L""; + } + + HWND hwnd = CreateWindow( + mainWindowClass.lpszClassName, + TEXT("PIN"), (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MINIMIZEBOX & ~WS_MAXIMIZEBOX) | WS_VISIBLE, + mainWindow.x, mainWindow.y, mainWindow.width, mainWindow.height, + NULL, 0, hInstance, NULL); + + if (hwnd == NULL) + { + std::cout << "PinPairDialog: Failed to create window" << std::endl; + return L""; + } + + // After creating the window, make it topmost + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + + ShowWindow(hwnd, SW_SHOW); + + // Create a font with the desired size + HFONT hFont = CreateFont( + 36, // Height of the font + 0, // Width of the font + 0, // Angle of escapement + 0, // Orientation angle + FW_NORMAL, // Font weight + FALSE, // Italic attribute option + FALSE, // Underline attribute option + FALSE, // Strikeout attribute option + DEFAULT_CHARSET, // Character set identifier + OUT_DEFAULT_PRECIS, // Output precision + CLIP_DEFAULT_PRECIS, // Clipping precision + DEFAULT_QUALITY, // Output quality + DEFAULT_PITCH | FF_SWISS, // Pitch and family + TEXT("Arial") // Font name + ); + + // Set the font to the edit control + SendMessage(txtEditHandle, WM_SETFONT, (WPARAM)hFont, TRUE); + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + if (msg.wParam != 0) + { + std::wcout << "PinPairDialog: Got invalid result" << std::endl; + } + + DeleteObject(hFont); + DestroyWindow(hwnd); + UnregisterClass(mainWindowClass.lpszClassName, hInstance); + return winrt::to_hstring(textBoxText); + } +} diff --git a/windows/src/universal_ble_plugin.cpp b/windows/src/universal_ble_plugin.cpp index 9add74c..97c431a 100644 --- a/windows/src/universal_ble_plugin.cpp +++ b/windows/src/universal_ble_plugin.cpp @@ -14,6 +14,7 @@ #include "helper/utils.h" #include "helper/universal_enum.h" #include "generated/universal_ble.g.h" +#include "pin_entry.h" namespace universal_ble { @@ -408,37 +409,24 @@ namespace universal_ble 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(); - bool isPaired = deviceInformation.Pairing().IsPaired(); - if (isPaired) + + if (deviceInformation.Pairing().IsPaired()) + { return FlutterError("PairLog: Device is already paired"); - bool canPair = deviceInformation.Pairing().CanPair(); - if (!canPair) + } + if (!deviceInformation.Pairing().CanPair()) + { return FlutterError("PairLog: Device is not pairable"); + } - auto customPairing = deviceInformation.Pairing().Custom(); - winrt::event_token token = customPairing.PairingRequested([this](const Enumeration::DeviceInformationCustomPairing &sender, const Enumeration::DevicePairingRequestedEventArgs &eventArgs) - { - std::cout << "PairLog: Pairing requested" << std::endl; - // eventArgs.AcceptWithPasswordCredential(nullptr, nullptr); - // eventArgs.Pin(); - // Accept all pairing request - eventArgs.Accept(); }); - // DevicePairingKinds => None, ConfirmOnly, DisplayPin, ProvidePin, ConfirmPinMatch, ProvidePasswordCredential - // DevicePairingProtectionLevel => Default, None, Encryption, EncryptionAndAuthentication - std::cout << "PairLog: Trying to pair" << std::endl; - auto async_c = customPairing.PairAsync( - Enumeration::DevicePairingKinds::ConfirmOnly); - async_c.Completed([this, customPairing, token, device_id](IAsyncOperation const &sender, AsyncStatus const args) - { - auto result = sender.GetResults(); - std::cout << "PairLog: Received pairing status" << std::endl; - customPairing.PairingRequested(token); - auto isPaired = result.Status() == Enumeration::DevicePairingResultStatus::Paired; - - // Post to main thread - std::string errorStr = parsePairingFailError(result); - uiThreadHandler_.Post([device_id, isPaired, errorStr] - { callbackChannel->OnPairStateChange(device_id, isPaired, &errorStr, SuccessCallback, ErrorCallback); }); }); + if (isWindows11OrGreater()) + { + PairAsync(device_id, deviceInformation); + } + else + { + CustomPairAsync(device_id, deviceInformation); + } return std::nullopt; } catch (const FlutterError &err) @@ -554,6 +542,63 @@ namespace universal_ble } } + winrt::fire_and_forget UniversalBlePlugin::PairAsync(std::string device_id, const DeviceInformation deviceInformation) + { + 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); }); + } + catch (...) + { + std::cout << "PairLog: Unknown error" << std::endl; + } + } + + winrt::fire_and_forget UniversalBlePlugin::CustomPairAsync(std::string device_id, const DeviceInformation deviceInformation) + { + 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); }); + } + catch (...) + { + std::cout << "PairLog Error: Pairing Failed" << std::endl; + } + } + + void UniversalBlePlugin::PairingRequestedHandler(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs eventArgs) + { + std::cout << "PairLog: Got PairingRequest" << std::endl; + DevicePairingKinds kind = eventArgs.PairingKind(); + if (kind != DevicePairingKinds::ProvidePin) + { + eventArgs.Accept(); + return; + } + + std::cout << "PairLog: Trying to get pin from user" << std::endl; + hstring pin = askForPairingPin(); + std::wcout << "PairLog: Got Pin: " << pin.c_str() << std::endl; + eventArgs.Accept(pin); + } + // Send device to callback channel // if device is already discovered in deviceWatcher then merge the scan result void UniversalBlePlugin::pushUniversalScanResult(UniversalBleScanResult scanResult, bool isConnectable) diff --git a/windows/src/universal_ble_plugin.h b/windows/src/universal_ble_plugin.h index e1267df..5bdd2e9 100644 --- a/windows/src/universal_ble_plugin.h +++ b/windows/src/universal_ble_plugin.h @@ -114,7 +114,7 @@ namespace universal_ble void DiscoverServicesAsync(BluetoothDeviceAgent &bluetoothDeviceAgent, std::function reply)>); winrt::fire_and_forget SetNotifiableAsync(BluetoothDeviceAgent &bluetoothDeviceAgent, const std::string &service, const std::string &characteristic, GattClientCharacteristicConfigurationDescriptorValue descriptorValue, - std::function reply)> result ); + std::function reply)> result); void GattCharacteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args); AvailabilityState getAvailabilityStateFromRadio(RadioState radioState); std::string parsePairingFailError(Enumeration::DevicePairingResult result); @@ -125,6 +125,9 @@ 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); + void PairingRequestedHandler(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs eventArgs); // UniversalBlePlatformChannel implementation. void GetBluetoothAvailabilityState(std::function reply)> result) override;