From 87fe3e24478a22247d7e482c50b04ddede1bd428 Mon Sep 17 00:00:00 2001 From: iAMD Date: Sun, 31 Dec 2023 00:53:48 +0800 Subject: [PATCH] feat: 5.0.0 (#35) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * draft: 临时提交 * feat: 实现扫描功能 * fix: 优化广播逻辑 * feat: 添加协程方法 * fix: 修改宏定义 * draft: 临时提交 * feat: 调整接口 * fix: 修改版本号 * feat: 4.1.1 * draft: 临时提交 * feat: 5.0.0-dev.2 * fix: 修复版本号错误 * draft: 临时提交 * fix: 修复连接断开异常 * fix: 修复问题 * fix: 优化代码 * fix: 优化 short UUID 格式化逻辑 * fix: 尝试实现 read_rssi 接口,当前此接口不可用,会报异常 * feat: 删除 getMaximumWriteLength 方法 * fix: 更新 CHANGELOG.md * feat: 5.0.0-dev.1 * fix: 更新依赖项 * feat: linux-5.0.0-dev.1 * fix: 更新 CHANGELOG.md * fix: 开始搜索设备时清空设备列表 * fix: 开始扫描时清空设备列表 * feat: 5.0.0-dev.2 * fix: 优化 MyGattService 和 MyGattCharacteristic * feat: 更新 interface 版本 -> 5.0.0-dev.4 * feat: 更新 interface 版本 -> 5.0.0-dev.4 * feat: 实现 flutter 部分 5.0.0 * fix: 移除 maximumWriteLength * fix: 移除 rssi * feat: 5.0.0-dev.1 * feat: 5.0.0-dev.2 * fix: 更新依赖项 * fix: 5.0.0-dev.4 * fix: 更新依赖项 * draft: 临时提交 * feat: 5.0.0-dev.5 * draft: 删除 MyCentralManager 和 MyPeripheralManager * fix: 更新依赖项 * fix: 更新依赖项 * feat: 适配新接口 * feat: 5.0.0-dev.6 * draft: 临时提交 * feat: 5.0.0-dev.7 * fix: 修改版本号 * feat: 5.0.0-dev.8 * feat: 5.0.0-dev.9 * fix: 修复 trimGATT 错误 * feat: 5.0.0-dev.6 * feat: 5.0.0-dev.3 * feat: 5.0.0-dev.4 * fix: 更新 pubspec.lock * feat: 5.0.0-dev.7 * feat: 5.0.0-dev.3 * fix: balabala * fix: balabala * draft: 5.0.0-dev.1 * fix: trim GATT when call the `writeCharacteristic` method. * fix: make difference of `trim` and `fragment`. * feat: 5.0.0-dev.1 * feat: 5.0.0-dev.1 * feat: 优化示例程序 * fix: 更新 README.md * fix: 修复插件引用 * draft: XXXX * feat: 增加调试信息 * fix: 更新 pubspec.lock * feat: 5.0.0-dev.4 * feat: 5.0.0-dev.3 * feat: 5.0.0 * feat: 5.0.0 * feat: 5.0.0 * feat: 5.0.0 * feat: 5.0.0 * feat: 5.0.0 --- bluetooth_low_energy/CHANGELOG.md | 52 + bluetooth_low_energy/README.md | 25 +- bluetooth_low_energy/example/lib/main.dart | 552 +++--- .../example/macos/Podfile.lock | 2 +- bluetooth_low_energy/example/pubspec.lock | 84 +- bluetooth_low_energy/example/pubspec.yaml | 2 +- .../example/windows/flutter/CMakeLists.txt | 7 +- .../flutter/generated_plugin_registrant.cc | 3 + .../windows/flutter/generated_plugins.cmake | 1 + .../lib/bluetooth_low_energy.dart | 12 +- bluetooth_low_energy/pubspec.yaml | 13 +- bluetooth_low_energy_android/CHANGELOG.md | 47 + .../BluetoothLowEnergyAndroid.kt | 20 +- .../MyAdvertiseCallback.kt | 12 +- .../bluetooth_low_energy_android/MyApi.g.kt | 609 +++---- .../bluetooth_low_energy_android/MyApi.kt | 376 ++-- .../MyBluetoothGattCallback.kt | 94 +- .../MyBluetoothGattServerCallback.kt | 90 +- .../MyBluetoothLowEnergyManager.kt | 119 +- .../MyBroadcastReceiver.kt | 10 +- .../MyCentralManager.kt | 694 ++++---- .../MyPeripheralManager.kt | 608 +++---- .../MyRequestPermissionResultListener.kt | 15 +- .../MyScanCallback.kt | 12 +- .../example/lib/main.dart | 547 +++--- .../example/pubspec.lock | 52 +- .../example/pubspec.yaml | 4 +- .../lib/bluetooth_low_energy_android.dart | 8 +- .../lib/src/my_api.dart | 192 +- .../lib/src/my_api.g.dart | 1293 +++++++------- .../lib/src/my_central2.dart | 11 + .../lib/src/my_central_manager.dart | 341 ++++ .../lib/src/my_central_manager2.dart | 308 ---- .../lib/src/my_gatt_characteristic2.dart | 16 +- .../lib/src/my_gatt_descriptor2.dart | 16 +- .../lib/src/my_gatt_service2.dart | 15 +- .../lib/src/my_peripheral2.dart | 11 + .../lib/src/my_peripheral_manager.dart | 413 +++++ .../lib/src/my_peripheral_manager2.dart | 252 --- bluetooth_low_energy_android/my_api.dart | 420 +++-- bluetooth_low_energy_android/pubspec.yaml | 6 +- bluetooth_low_energy_darwin/CHANGELOG.md | 26 + .../Classes/BluetoothLowEnergyDarwin.swift | 12 +- .../darwin/Classes/MyApi.g.swift | 633 ++++--- .../darwin/Classes/MyApi.swift | 476 ++--- .../darwin/Classes/MyCentralManager.swift | 741 ++++---- .../Classes/MyCentralManagerDelegate.swift | 18 +- .../darwin/Classes/MyError.swift | 1 - .../darwin/Classes/MyPeripheralDelegate.swift | 24 +- .../darwin/Classes/MyPeripheralManager.swift | 606 ++++--- .../Classes/MyPeripheralManagerDelegate.swift | 24 +- .../example/ios/Podfile.lock | 2 +- .../example/lib/main.dart | 547 +++--- .../example/macos/Podfile.lock | 2 +- .../example/pubspec.lock | 52 +- .../example/pubspec.yaml | 4 +- .../lib/bluetooth_low_energy_darwin.dart | 8 +- .../lib/src/my_api.dart | 202 ++- .../lib/src/my_api.g.dart | 1181 ++++++------- .../lib/src/my_central_manager.dart | 331 ++++ .../lib/src/my_central_manager_2.dart | 297 ---- .../lib/src/my_gatt_characteristic2.dart | 15 +- .../lib/src/my_gatt_descriptor2.dart | 16 +- .../lib/src/my_gatt_service2.dart | 14 +- .../lib/src/my_peripheral_manager.dart | 315 ++++ .../lib/src/my_peripheral_manager_2.dart | 252 --- bluetooth_low_energy_darwin/my_api.dart | 322 ++-- bluetooth_low_energy_darwin/pubspec.yaml | 6 +- bluetooth_low_energy_linux/CHANGELOG.md | 30 + .../example/lib/main.dart | 776 +++------ .../example/pubspec.lock | 64 +- .../example/pubspec.yaml | 4 +- .../lib/bluetooth_low_energy_linux.dart | 4 +- .../lib/src/my_bluez.dart | 147 +- .../lib/src/my_central_manager.dart | 364 ++++ .../lib/src/my_central_manager2.dart | 341 ---- .../lib/src/my_gatt_characteristic2.dart | 25 +- .../lib/src/my_gatt_descriptor2.dart | 16 +- .../lib/src/my_gatt_service2.dart | 22 +- .../lib/src/my_peripheral2.dart | 7 +- bluetooth_low_energy_linux/pubspec.yaml | 4 +- .../CHANGELOG.md | 83 + .../README.md | 8 +- ...uetooth_low_energy_platform_interface.dart | 9 +- .../lib/src/bluetooth_low_energy_manager.dart | 8 +- .../lib/src/central_event_args.dart | 51 + .../lib/src/central_manager.dart | 55 +- .../lib/src/central_manager_event_args.dart | 45 - .../lib/src/gatt_characteristic.dart | 4 + .../lib/src/my_bluetooth_low_energy_peer.dart | 11 + .../lib/src/my_central.dart | 19 +- .../lib/src/my_central_manager.dart | 41 - .../lib/src/my_gatt_attribute.dart | 19 + .../lib/src/my_gatt_characteristic.dart | 22 +- .../lib/src/my_gatt_descriptor.dart | 21 +- .../lib/src/my_gatt_service.dart | 10 +- .../lib/src/my_object.dart | 13 - .../lib/src/my_peripheral.dart | 19 +- .../lib/src/my_peripheral_manager.dart | 41 - .../lib/src/peripheral_event_args.dart | 61 + .../lib/src/peripheral_manager.dart | 84 +- .../src/peripheral_manager_event_args.dart | 73 - .../lib/src/uuid.dart | 10 + .../pubspec.yaml | 4 +- .../test/my_gatt_attribute_test.dart | 46 + .../test/uuid_test.dart | 23 + bluetooth_low_energy_windows/.metadata | 10 +- bluetooth_low_energy_windows/CHANGELOG.md | 37 + .../example/lib/main.dart | 796 +++------ .../example/pubspec.lock | 88 +- .../example/pubspec.yaml | 6 +- .../example/windows/flutter/CMakeLists.txt | 7 +- .../flutter/generated_plugin_registrant.cc | 3 + .../windows/flutter/generated_plugins.cmake | 1 + .../lib/bluetooth_low_energy_windows.dart | 4 +- .../lib/src/my_api.dart | 254 +++ .../lib/src/my_api.g.dart | 1179 +++++++++++++ .../lib/src/my_bluetooth_low_energy.dart | 11 - .../lib/src/my_central_manager.dart | 317 ++++ .../lib/src/my_gatt_characteristic2.dart | 30 + .../lib/src/my_gatt_descriptor2.dart | 22 + .../lib/src/my_gatt_service2.dart | 29 + bluetooth_low_energy_windows/my_api.dart | 220 +++ bluetooth_low_energy_windows/pubspec.yaml | 9 +- .../windows/.gitignore | 17 + .../windows/CMakeLists.txt | 135 ++ .../windows/bluetooth_low_energy_windows.cpp | 24 + .../windows/bluetooth_low_energy_windows.h | 26 + .../bluetooth_low_energy_windows_c_api.cpp | 12 + .../bluetooth_low_energy_windows_c_api.h | 23 + .../windows/my_api.g.cpp | 1551 +++++++++++++++++ .../windows/my_api.g.h | 588 +++++++ .../windows/my_central_manager.cpp | 871 +++++++++ .../windows/my_central_manager.h | 82 + .../windows/my_exception.cpp | 9 + .../windows/my_exception.h | 22 + .../bluetooth_low_energy_windows_test.cpp | 43 + 137 files changed, 14108 insertions(+), 8393 deletions(-) create mode 100644 bluetooth_low_energy_android/lib/src/my_central2.dart create mode 100644 bluetooth_low_energy_android/lib/src/my_central_manager.dart delete mode 100644 bluetooth_low_energy_android/lib/src/my_central_manager2.dart create mode 100644 bluetooth_low_energy_android/lib/src/my_peripheral2.dart create mode 100644 bluetooth_low_energy_android/lib/src/my_peripheral_manager.dart delete mode 100644 bluetooth_low_energy_android/lib/src/my_peripheral_manager2.dart create mode 100644 bluetooth_low_energy_darwin/lib/src/my_central_manager.dart delete mode 100644 bluetooth_low_energy_darwin/lib/src/my_central_manager_2.dart create mode 100644 bluetooth_low_energy_darwin/lib/src/my_peripheral_manager.dart delete mode 100644 bluetooth_low_energy_darwin/lib/src/my_peripheral_manager_2.dart create mode 100644 bluetooth_low_energy_linux/lib/src/my_central_manager.dart delete mode 100644 bluetooth_low_energy_linux/lib/src/my_central_manager2.dart create mode 100644 bluetooth_low_energy_platform_interface/lib/src/central_event_args.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/central_manager_event_args.dart create mode 100644 bluetooth_low_energy_platform_interface/lib/src/my_bluetooth_low_energy_peer.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/my_central_manager.dart create mode 100644 bluetooth_low_energy_platform_interface/lib/src/my_gatt_attribute.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/my_object.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/my_peripheral_manager.dart create mode 100644 bluetooth_low_energy_platform_interface/lib/src/peripheral_event_args.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/peripheral_manager_event_args.dart create mode 100644 bluetooth_low_energy_platform_interface/test/my_gatt_attribute_test.dart create mode 100644 bluetooth_low_energy_platform_interface/test/uuid_test.dart create mode 100644 bluetooth_low_energy_windows/lib/src/my_api.dart create mode 100644 bluetooth_low_energy_windows/lib/src/my_api.g.dart delete mode 100644 bluetooth_low_energy_windows/lib/src/my_bluetooth_low_energy.dart create mode 100644 bluetooth_low_energy_windows/lib/src/my_central_manager.dart create mode 100644 bluetooth_low_energy_windows/lib/src/my_gatt_characteristic2.dart create mode 100644 bluetooth_low_energy_windows/lib/src/my_gatt_descriptor2.dart create mode 100644 bluetooth_low_energy_windows/lib/src/my_gatt_service2.dart create mode 100644 bluetooth_low_energy_windows/my_api.dart create mode 100644 bluetooth_low_energy_windows/windows/.gitignore create mode 100644 bluetooth_low_energy_windows/windows/CMakeLists.txt create mode 100644 bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.cpp create mode 100644 bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.h create mode 100644 bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_c_api.cpp create mode 100644 bluetooth_low_energy_windows/windows/include/bluetooth_low_energy_windows/bluetooth_low_energy_windows_c_api.h create mode 100644 bluetooth_low_energy_windows/windows/my_api.g.cpp create mode 100644 bluetooth_low_energy_windows/windows/my_api.g.h create mode 100644 bluetooth_low_energy_windows/windows/my_central_manager.cpp create mode 100644 bluetooth_low_energy_windows/windows/my_central_manager.h create mode 100644 bluetooth_low_energy_windows/windows/my_exception.cpp create mode 100644 bluetooth_low_energy_windows/windows/my_exception.h create mode 100644 bluetooth_low_energy_windows/windows/test/bluetooth_low_energy_windows_test.cpp diff --git a/bluetooth_low_energy/CHANGELOG.md b/bluetooth_low_energy/CHANGELOG.md index f074aef..7092ddf 100644 --- a/bluetooth_low_energy/CHANGELOG.md +++ b/bluetooth_low_energy/CHANGELOG.md @@ -1,3 +1,55 @@ +## 5.0.0 + +* Now `CentralManager#writeCharacteristic` and `PeripheralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes. +* Add implementation of `CentralManager` on windows platform. +* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`. +* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`. +* Add `PeripheralManager#readCharacteristic`. +* Remove `CentralManager#getMaximumWriteLength` method. +* Remove `PeripheralManager#getMaximumWriteLength` method. +* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`. +* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`. +* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`. +* Move `CentralManager#state` to `CentralManager#getState()`. +* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`. +* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`. +* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`. +* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`. +* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`. +* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#writeCharacteristic`. +* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`. +* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`. + +## 5.0.0-dev.3 + +* Add logs on Linux platform. + +## 5.0.0-dev.2 + +* Add default_package of windows in pubspec.yaml. + +## 5.0.0-dev.1 + +* Now `CentralManager#writeCharacteristic` and `PeripheralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes. +* Add `UUID#fromAddress` constructor. +* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`. +* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`. +* Add `PeripheralManager#readCharacteristic`. +* Remove `CentralManager#getMaximumWriteLength` method. +* Remove `PeripheralManager#getMaximumWriteLength` method. +* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`. +* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`. +* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`. +* Move `CentralManager#state` to `CentralManager#getState()`. +* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`. +* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`. +* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`. +* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`. +* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`. +* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#writeCharacteristic`. +* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`. +* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`. + ## 4.0.0 * Remove `BluetoothLowEnergy` class. diff --git a/bluetooth_low_energy/README.md b/bluetooth_low_energy/README.md index b12cf01..854bb2a 100644 --- a/bluetooth_low_energy/README.md +++ b/bluetooth_low_energy/README.md @@ -6,29 +6,24 @@ A Flutter plugin for controlling the bluetooth low energy. ### CentralManager -- [x] Set up the central manager. - [x] Get/Listen the state of the central manager. +- [x] Listen connection state cahgned. +- [x] Listen GATT characteristic notified. - [x] Start/Stop discovery. - [x] Connect/Disconnect peripherals. -- [x] Get maximum write length of peripherals. - [x] Read RSSI of peripherals. - [x] Discover GATT. -- [x] Get GATT services. -- [x] Get GATT characteristics. -- [x] Get GATT descriptors. -- [x] Read/Write/Notify GATT characteristics. +- [x] Read/Write GATT characteristics. +- [x] Set GATT characteristics notify state. - [x] Read/Write GATT descriptors. ### PeripheralManager -- [x] Set up the peripheral manager. - [x] Get/Listen the state of the peripheral manager. +- [x] Listen GATT characteristic read/written/notifyStateChanged. - [x] Add/Remove/Clear service(s). - [x] Start/Stop advertising. -- [x] Get maximum write length of centrals. -- [x] Listen read/write/notify characteristic requests from centrals. -- [x] Send read/write characteristic replies to centrals. -- [x] Notify characteristic value changed to centrals. +- [x] Read/Write(Notify) GATT characteristics. ## Getting Started @@ -55,10 +50,10 @@ According to Apple's [documents](https://developer.apple.com/documentation/coreb ### Linux -Not tested enough, if you occured any problems, file an issue to let me know about it, i will fix it as soon as possible. - -PeripheralManager api is not supported because the `bluez` plugin doesn't support this yet, see [How to use bluez to act as bluetooth peripheral](https://github.com/canonical/bluez.dart/issues/85) +PeripheralManager is not implemented because the `bluez` plugin doesn't support this yet, see [How to use bluez to act as bluetooth peripheral](https://github.com/canonical/bluez.dart/issues/85) ### Windows -Not implemented yet but maybe someday or someone can use the `win32` api to implement this plugin_interface or someday the flutter team support C# on windows platform or someday I am familiar with C++ language... +PeripheralManager is not implemented, it will be implemented in the future. + +*Note:* The `CentralManager#readRSSI` method is not implemented on windows(windows doesn't support read RSSI after connected), avoid call this when running on windows devices. diff --git a/bluetooth_low_energy/example/lib/main.dart b/bluetooth_low_energy/example/lib/main.dart index 7dbf468..47ced48 100644 --- a/bluetooth_low_energy/example/lib/main.dart +++ b/bluetooth_low_energy/example/lib/main.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:developer'; +import 'dart:io'; import 'dart:typed_data'; import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; @@ -8,8 +9,7 @@ import 'package:convert/convert.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; -CentralManager get centralManager => CentralManager.instance; -PeripheralManager get peripheralManager => PeripheralManager.instance; +bool get enablePeripheral => !Platform.isLinux && !Platform.isWindows; void main() { runZonedGuarded(onStartUp, onCrashed); @@ -17,9 +17,13 @@ void main() { void onStartUp() async { Logger.root.onRecord.listen(onLogRecord); + // hierarchicalLoggingEnabled = true; + // CentralManager.instance.logLevel = Level.WARNING; WidgetsFlutterBinding.ensureInitialized(); - await centralManager.setUp(); - await peripheralManager.setUp(); + await CentralManager.instance.setUp(); + if (enablePeripheral) { + await PeripheralManager.instance.setUp(); + } runApp(const MyApp()); } @@ -58,8 +62,10 @@ class _MyAppState extends State { return MaterialApp( theme: ThemeData.light( useMaterial3: true, + ).copyWith( + materialTapTargetSize: MaterialTapTargetSize.padded, ), - home: const HomeView(), + home: enablePeripheral ? const HomeView() : const ScannerView(), routes: { 'peripheral': (context) { final route = ModalRoute.of(context); @@ -168,15 +174,15 @@ class _ScannerViewState extends State { @override void initState() { super.initState(); - state = ValueNotifier(centralManager.state); + state = ValueNotifier(BluetoothLowEnergyState.unknown); discovering = ValueNotifier(false); discoveredEventArgs = ValueNotifier([]); - stateChangedSubscription = centralManager.stateChanged.listen( + stateChangedSubscription = CentralManager.instance.stateChanged.listen( (eventArgs) { state.value = eventArgs.state; }, ); - discoveredSubscription = centralManager.discovered.listen( + discoveredSubscription = CentralManager.instance.discovered.listen( (eventArgs) { final items = discoveredEventArgs.value; final i = items.indexWhere( @@ -190,6 +196,11 @@ class _ScannerViewState extends State { } }, ); + _initialize(); + } + + void _initialize() async { + state.value = await CentralManager.instance.getState(); } @override @@ -233,12 +244,13 @@ class _ScannerViewState extends State { } Future startDiscovery() async { - await centralManager.startDiscovery(); + discoveredEventArgs.value = []; + await CentralManager.instance.startDiscovery(); discovering.value = true; } Future stopDiscovery() async { - await centralManager.stopDiscovery(); + await CentralManager.instance.stopDiscovery(); discovering.value = false; } @@ -340,7 +352,13 @@ class _ScannerViewState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, ), - trailing: RssiWidget(rssi), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RssiWidget(rssi), + Text('$rssi'), + ], + ), ); }, separatorBuilder: (context, i) { @@ -378,44 +396,39 @@ class PeripheralView extends StatefulWidget { } class _PeripheralViewState extends State { - late final ValueNotifier state; + late final ValueNotifier connectionState; late final DiscoveredEventArgs eventArgs; late final ValueNotifier> services; late final ValueNotifier> characteristics; late final ValueNotifier service; late final ValueNotifier characteristic; late final ValueNotifier writeType; - late final ValueNotifier maximumWriteLength; - late final ValueNotifier rssi; late final ValueNotifier> logs; late final TextEditingController writeController; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription valueChangedSubscription; - late final StreamSubscription rssiChangedSubscription; - late final Timer rssiTimer; + late final StreamSubscription connectionStateChangedSubscription; + late final StreamSubscription characteristicNotifiedSubscription; @override void initState() { super.initState(); eventArgs = widget.eventArgs; - state = ValueNotifier(false); + connectionState = ValueNotifier(false); services = ValueNotifier([]); characteristics = ValueNotifier([]); service = ValueNotifier(null); characteristic = ValueNotifier(null); writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); - maximumWriteLength = ValueNotifier(0); - rssi = ValueNotifier(-100); logs = ValueNotifier([]); writeController = TextEditingController(); - stateChangedSubscription = centralManager.peripheralStateChanged.listen( + connectionStateChangedSubscription = + CentralManager.instance.connectionStateChanged.listen( (eventArgs) { if (eventArgs.peripheral != this.eventArgs.peripheral) { return; } - final state = eventArgs.state; - this.state.value = state; - if (!state) { + final connectionState = eventArgs.connectionState; + this.connectionState.value = connectionState; + if (!connectionState) { services.value = []; characteristics.value = []; service.value = null; @@ -424,12 +437,13 @@ class _PeripheralViewState extends State { } }, ); - valueChangedSubscription = centralManager.characteristicValueChanged.listen( + characteristicNotifiedSubscription = + CentralManager.instance.characteristicNotified.listen( (eventArgs) { - final characteristic = this.characteristic.value; - if (eventArgs.characteristic != characteristic) { - return; - } + // final characteristic = this.characteristic.value; + // if (eventArgs.characteristic != characteristic) { + // return; + // } const type = LogType.notify; final log = Log(type, eventArgs.value); logs.value = [ @@ -438,28 +452,16 @@ class _PeripheralViewState extends State { ]; }, ); - rssiTimer = Timer.periodic( - const Duration(seconds: 5), - (timer) async { - final state = this.state.value; - if (state) { - rssi.value = await centralManager.readRSSI(eventArgs.peripheral); - } else { - rssi.value = -100; - } - }, - ); } @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () async { - if (state.value) { + return PopScope( + onPopInvoked: (didPop) async { + if (connectionState.value) { final peripheral = eventArgs.peripheral; - await centralManager.disconnect(peripheral); + await CentralManager.instance.disconnect(peripheral); } - return true; }, child: Scaffold( appBar: buildAppBar(context), @@ -474,25 +476,17 @@ class _PeripheralViewState extends State { title: Text(title), actions: [ ValueListenableBuilder( - valueListenable: state, + valueListenable: connectionState, builder: (context, state, child) { return TextButton( onPressed: () async { final peripheral = eventArgs.peripheral; if (state) { - await centralManager.disconnect(peripheral); - maximumWriteLength.value = 0; - rssi.value = 0; + await CentralManager.instance.disconnect(peripheral); } else { - await centralManager.connect(peripheral); + await CentralManager.instance.connect(peripheral); services.value = - await centralManager.discoverGATT(peripheral); - maximumWriteLength.value = - await centralManager.getMaximumWriteLength( - peripheral, - type: writeType.value, - ); - rssi.value = await centralManager.readRSSI(peripheral); + await CentralManager.instance.discoverGATT(peripheral); } }, child: Text(state ? 'DISCONNECT' : 'CONNECT'), @@ -566,7 +560,32 @@ class _PeripheralViewState extends State { hint: const Text('CHOOSE A CHARACTERISTIC'), value: characteristic, onChanged: (characteristic) { + if (characteristic == null) { + return; + } this.characteristic.value = characteristic; + final writeType = this.writeType.value; + final canWrite = characteristic.properties.contains( + GattCharacteristicProperty.write, + ); + final canWriteWithoutResponse = + characteristic.properties.contains( + GattCharacteristicProperty.writeWithoutResponse, + ); + if (writeType == + GattCharacteristicWriteType.withResponse && + !canWrite && + canWriteWithoutResponse) { + this.writeType.value = + GattCharacteristicWriteType.withoutResponse; + } + if (writeType == + GattCharacteristicWriteType.withoutResponse && + !canWriteWithoutResponse && + canWrite) { + this.writeType.value = + GattCharacteristicWriteType.withResponse; + } }, ); }, @@ -624,129 +643,45 @@ class _PeripheralViewState extends State { }, ), ), - Row( - children: [ - ValueListenableBuilder( - valueListenable: writeType, - builder: (context, writeType, child) { - return ToggleButtons( - onPressed: (i) async { - final type = GattCharacteristicWriteType.values[i]; - this.writeType.value = type; - maximumWriteLength.value = - await centralManager.getMaximumWriteLength( - eventArgs.peripheral, - type: type, - ); - }, - constraints: const BoxConstraints( - minWidth: 0.0, - minHeight: 0.0, - ), - borderRadius: BorderRadius.circular(4.0), - isSelected: GattCharacteristicWriteType.values - .map((type) => type == writeType) - .toList(), - children: GattCharacteristicWriteType.values.map((type) { - return Container( - margin: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 4.0, - ), - child: Text(type.name), - ); - }).toList(), - ); - // final segments = - // GattCharacteristicWriteType.values.map((type) { - // return ButtonSegment( - // value: type, - // label: Text(type.name), - // ); - // }).toList(); - // return SegmentedButton( - // segments: segments, - // selected: {writeType}, - // showSelectedIcon: false, - // style: OutlinedButton.styleFrom( - // tapTargetSize: MaterialTapTargetSize.shrinkWrap, - // padding: EdgeInsets.zero, - // visualDensity: VisualDensity.compact, - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(8.0), - // ), - // ), - // ); - }, - ), - const SizedBox(width: 8.0), - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: maximumWriteLength, - builder: (context, maximumWriteLength, child) { - return Text('$maximumWriteLength'); - }, - ); - }, - ), - const Spacer(), - ValueListenableBuilder( - valueListenable: rssi, - builder: (context, rssi, child) { - return RssiWidget(rssi); - }, - ), - ], - ), - Container( - margin: const EdgeInsets.only(bottom: 16.0), - height: 160.0, - child: ValueListenableBuilder( - valueListenable: characteristic, - builder: (context, characteristic, child) { - final bool canNotify, canRead, canWrite; - if (characteristic == null) { - canNotify = canRead = canWrite = false; - } else { - final properties = characteristic.properties; - canNotify = properties.contains( - GattCharacteristicProperty.notify, - ); - canRead = properties.contains( - GattCharacteristicProperty.read, - ); - canWrite = properties.contains( - GattCharacteristicProperty.write, - ); - } - return Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded( - child: TextField( - controller: writeController, - enabled: canWrite, - expands: true, - maxLines: null, - textAlignVertical: TextAlignVertical.top, - decoration: const InputDecoration( - border: OutlineInputBorder(), - contentPadding: EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 8.0, - ), - ), - ), - ), - Column( - mainAxisSize: MainAxisSize.min, + ValueListenableBuilder( + valueListenable: characteristic, + builder: (context, characteristic, chld) { + final bool canNotify, canRead, canWrite, canWriteWithoutResponse; + if (characteristic == null) { + canNotify = + canRead = canWrite = canWriteWithoutResponse = false; + } else { + final properties = characteristic.properties; + canNotify = properties.contains( + GattCharacteristicProperty.notify, + ) || + properties.contains( + GattCharacteristicProperty.indicate, + ); + canRead = properties.contains( + GattCharacteristicProperty.read, + ); + canWrite = properties.contains( + GattCharacteristicProperty.write, + ); + canWriteWithoutResponse = properties.contains( + GattCharacteristicProperty.writeWithoutResponse, + ); + } + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + margin: const EdgeInsets.symmetric(vertical: 4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ - TextButton( + ElevatedButton( onPressed: characteristic != null && canNotify ? () async { - await centralManager.notifyCharacteristic( + await CentralManager.instance + .setCharacteristicNotifyState( characteristic, state: true, ); @@ -754,10 +689,11 @@ class _PeripheralViewState extends State { : null, child: const Text('NOTIFY'), ), - TextButton( + const SizedBox(width: 8.0), + ElevatedButton( onPressed: characteristic != null && canRead ? () async { - final value = await centralManager + final value = await CentralManager.instance .readCharacteristic(characteristic); const type = LogType.read; final log = Log(type, value); @@ -765,31 +701,124 @@ class _PeripheralViewState extends State { } : null, child: const Text('READ'), - ), - TextButton( - onPressed: characteristic != null && canWrite - ? () async { - final text = writeController.text; - final elements = utf8.encode(text); - final value = Uint8List.fromList(elements); - final type = writeType.value; - await centralManager.writeCharacteristic( - characteristic, - value: value, - type: type, - ); - final log = Log(LogType.write, value); - logs.value = [...logs.value, log]; - } - : null, - child: const Text('WRITE'), - ), + ) ], ), - ], - ); - }, - ), + ), + SizedBox( + height: 160.0, + child: TextField( + controller: writeController, + enabled: canWrite || canWriteWithoutResponse, + expands: true, + maxLines: null, + textAlignVertical: TextAlignVertical.top, + decoration: const InputDecoration( + border: OutlineInputBorder(), + contentPadding: EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 8.0, + ), + ), + ), + ), + Row( + children: [ + ValueListenableBuilder( + valueListenable: writeType, + builder: (context, writeType, child) { + return ToggleButtons( + onPressed: canWrite || canWriteWithoutResponse + ? (i) { + if (!canWrite || !canWriteWithoutResponse) { + return; + } + final type = + GattCharacteristicWriteType.values[i]; + this.writeType.value = type; + } + : null, + constraints: const BoxConstraints( + minWidth: 0.0, + minHeight: 0.0, + ), + borderRadius: BorderRadius.circular(4.0), + isSelected: GattCharacteristicWriteType.values + .map((type) => type == writeType) + .toList(), + children: GattCharacteristicWriteType.values.map( + (type) { + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, + ), + child: Text(type.name), + ); + }, + ).toList(), + ); + // final segments = + // GattCharacteristicWriteType.values.map((type) { + // return ButtonSegment( + // value: type, + // label: Text(type.name), + // ); + // }).toList(); + // return SegmentedButton( + // segments: segments, + // selected: {writeType}, + // showSelectedIcon: false, + // style: OutlinedButton.styleFrom( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, + // padding: EdgeInsets.zero, + // visualDensity: VisualDensity.compact, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8.0), + // ), + // ), + // ); + }, + ), + const Spacer(), + ElevatedButton( + onPressed: characteristic != null && canWrite + ? () async { + final text = writeController.text; + final elements = utf8.encode(text); + final value = Uint8List.fromList(elements); + final type = writeType.value; + // Fragments the value by 512 bytes. + const fragmentSize = 512; + var start = 0; + while (start < value.length) { + final end = start + fragmentSize; + final fragmentedValue = end < value.length + ? value.sublist(start, end) + : value.sublist(start); + await CentralManager.instance + .writeCharacteristic( + characteristic, + value: fragmentedValue, + type: type, + ); + final log = Log( + LogType.write, + fragmentedValue, + ); + logs.value = [...logs.value, log]; + start = end; + } + } + : null, + child: const Text('WRITE'), + ), + ], + ), + const SizedBox(height: 16.0), + ], + ); + }, ), ], ), @@ -799,17 +828,14 @@ class _PeripheralViewState extends State { @override void dispose() { super.dispose(); - rssiTimer.cancel(); - stateChangedSubscription.cancel(); - valueChangedSubscription.cancel(); - state.dispose(); + connectionStateChangedSubscription.cancel(); + characteristicNotifiedSubscription.cancel(); + connectionState.dispose(); services.dispose(); characteristics.dispose(); service.dispose(); characteristic.dispose(); writeType.dispose(); - maximumWriteLength.dispose(); - rssi.dispose(); logs.dispose(); writeController.dispose(); } @@ -828,87 +854,63 @@ class _AdvertiserViewState extends State late final ValueNotifier advertising; late final ValueNotifier> logs; late final StreamSubscription stateChangedSubscription; - late final StreamSubscription readCharacteristicCommandReceivedSubscription; - late final StreamSubscription writeCharacteristicCommandReceivedSubscription; - late final StreamSubscription notifyCharacteristicCommandReceivedSubscription; + late final StreamSubscription characteristicReadSubscription; + late final StreamSubscription characteristicWrittenSubscription; + late final StreamSubscription characteristicNotifyStateChangedSubscription; @override void initState() { super.initState(); - state = ValueNotifier(peripheralManager.state); + state = ValueNotifier(BluetoothLowEnergyState.unknown); advertising = ValueNotifier(false); logs = ValueNotifier([]); - stateChangedSubscription = peripheralManager.stateChanged.listen( + stateChangedSubscription = PeripheralManager.instance.stateChanged.listen( (eventArgs) { state.value = eventArgs.state; }, ); - readCharacteristicCommandReceivedSubscription = - peripheralManager.readCharacteristicCommandReceived.listen( + characteristicReadSubscription = + PeripheralManager.instance.characteristicRead.listen( (eventArgs) async { final central = eventArgs.central; final characteristic = eventArgs.characteristic; - final id = eventArgs.id; - final offset = eventArgs.offset; + final value = eventArgs.value; final log = Log( LogType.read, - Uint8List.fromList([]), - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', + value, + 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', ); logs.value = [ ...logs.value, log, ]; - // final maximumWriteLength = peripheralManager.getMaximumWriteLength( - // central, - // ); - const status = true; - final value = Uint8List.fromList([0x01, 0x02, 0x03]); - await peripheralManager.sendReadCharacteristicReply( - central, - characteristic: characteristic, - id: id, - offset: offset, - status: status, - value: value, - ); }, ); - writeCharacteristicCommandReceivedSubscription = - peripheralManager.writeCharacteristicCommandReceived.listen( + characteristicWrittenSubscription = + PeripheralManager.instance.characteristicWritten.listen( (eventArgs) async { final central = eventArgs.central; final characteristic = eventArgs.characteristic; - final id = eventArgs.id; - final offset = eventArgs.offset; final value = eventArgs.value; final log = Log( LogType.write, value, - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', + 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', ); logs.value = [ ...logs.value, log, ]; - const status = true; - await peripheralManager.sendWriteCharacteristicReply( - central, - characteristic: characteristic, - id: id, - offset: offset, - status: status, - ); }, ); - notifyCharacteristicCommandReceivedSubscription = - peripheralManager.notifyCharacteristicCommandReceived.listen( + characteristicNotifyStateChangedSubscription = + PeripheralManager.instance.characteristicNotifyStateChanged.listen( (eventArgs) async { final central = eventArgs.central; final characteristic = eventArgs.characteristic; final state = eventArgs.state; final log = Log( - LogType.write, + LogType.notify, Uint8List.fromList([]), 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state', ); @@ -918,15 +920,21 @@ class _AdvertiserViewState extends State ]; // Write someting to the central when notify started. if (state) { - final value = Uint8List.fromList([0x03, 0x02, 0x01]); - await peripheralManager.notifyCharacteristicValueChanged( - central, - characteristic: characteristic, + final elements = List.generate(2000, (i) => i % 256); + final value = Uint8List.fromList(elements); + await PeripheralManager.instance.writeCharacteristic( + characteristic, value: value, + central: central, ); } }, ); + _initialize(); + } + + void _initialize() async { + state.value = await PeripheralManager.instance.getState(); } @override @@ -970,7 +978,9 @@ class _AdvertiserViewState extends State } Future startAdvertising() async { - await peripheralManager.clearServices(); + await PeripheralManager.instance.clearServices(); + final elements = List.generate(1000, (i) => i % 256); + final value = Uint8List.fromList(elements); final service = GattService( uuid: UUID.short(100), characteristics: [ @@ -979,15 +989,16 @@ class _AdvertiserViewState extends State properties: [ GattCharacteristicProperty.read, ], + value: value, descriptors: [], ), GattCharacteristic( uuid: UUID.short(201), properties: [ - GattCharacteristicProperty.read, GattCharacteristicProperty.write, GattCharacteristicProperty.writeWithoutResponse, ], + value: Uint8List.fromList([]), descriptors: [], ), GattCharacteristic( @@ -996,24 +1007,41 @@ class _AdvertiserViewState extends State GattCharacteristicProperty.notify, GattCharacteristicProperty.indicate, ], + value: Uint8List.fromList([]), + descriptors: [], + ), + GattCharacteristic( + uuid: UUID.short(203), + properties: [ + GattCharacteristicProperty.notify, + ], + value: Uint8List.fromList([]), + descriptors: [], + ), + GattCharacteristic( + uuid: UUID.short(204), + properties: [ + GattCharacteristicProperty.indicate, + ], + value: Uint8List.fromList([]), descriptors: [], ), ], ); - await peripheralManager.addService(service); + await PeripheralManager.instance.addService(service); final advertisement = Advertisement( - name: 'flutter', + name: 'le12138', manufacturerSpecificData: ManufacturerSpecificData( id: 0x2e19, data: Uint8List.fromList([0x01, 0x02, 0x03]), ), ); - await peripheralManager.startAdvertising(advertisement); + await PeripheralManager.instance.startAdvertising(advertisement); advertising.value = true; } Future stopAdvertising() async { - await peripheralManager.stopAdvertising(); + await PeripheralManager.instance.stopAdvertising(); advertising.value = false; } @@ -1074,9 +1102,9 @@ class _AdvertiserViewState extends State void dispose() { super.dispose(); stateChangedSubscription.cancel(); - readCharacteristicCommandReceivedSubscription.cancel(); - writeCharacteristicCommandReceivedSubscription.cancel(); - notifyCharacteristicCommandReceivedSubscription.cancel(); + characteristicReadSubscription.cancel(); + characteristicWrittenSubscription.cancel(); + characteristicNotifyStateChangedSubscription.cancel(); state.dispose(); advertising.dispose(); logs.dispose(); diff --git a/bluetooth_low_energy/example/macos/Podfile.lock b/bluetooth_low_energy/example/macos/Podfile.lock index 6e81df1..c3133f1 100644 --- a/bluetooth_low_energy/example/macos/Podfile.lock +++ b/bluetooth_low_energy/example/macos/Podfile.lock @@ -20,4 +20,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.12.1 +COCOAPODS: 1.14.3 diff --git a/bluetooth_low_energy/example/pubspec.lock b/bluetooth_low_energy/example/pubspec.lock index 1cb34b9..3a998ac 100644 --- a/bluetooth_low_energy/example/pubspec.lock +++ b/bluetooth_low_energy/example/pubspec.lock @@ -23,39 +23,47 @@ packages: path: ".." relative: true source: path - version: "4.0.0" + version: "5.0.0" bluetooth_low_energy_android: dependency: transitive description: name: bluetooth_low_energy_android - sha256: "7668f695f195ed67b9985a76a21f10d181a26ebfc2124b4cd272fb6f202c1ee9" + sha256: "502382b2bc6d0bf9e7aa635bafa28057cb8538d6f2dd9c2eceb6d0111bcee94a" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" bluetooth_low_energy_darwin: dependency: transitive description: name: bluetooth_low_energy_darwin - sha256: "027b46d8efea5c726cc32ab6b2ca387f0ef15507afcd1077d75712ad4e7f941d" + sha256: "9f28475bb878b5a48c2746c776010bc77d7040b5fead128295d22f0ce6ef5995" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" bluetooth_low_energy_linux: dependency: transitive description: name: bluetooth_low_energy_linux - sha256: "548a869579684ce7602577ef5e0498d18eef552fb2abb3ee7909814bef57807f" + sha256: "100dc824a9b409442e7018994de74d56d65faa95b8c6fbbf5a8c0ae70cd58286" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" bluetooth_low_energy_platform_interface: dependency: transitive description: name: bluetooth_low_energy_platform_interface - sha256: a01819f4ef89d033edaa979465ec8c3af13b2618dc718d90fe681be91b6c4356 + sha256: "54f92ab2d7746fb6f2b4a40a48cd7eb8e3bf772f3ee89e1979d4d7b741fb2a05" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" + bluetooth_low_energy_windows: + dependency: transitive + description: + name: bluetooth_low_energy_windows + sha256: "1a4382a1c3d480a35b4de5b7065b00e739cf44129108006c2a3ed8f3ca99fd3d" + url: "https://pub.dev" + source: hosted + version: "5.0.0" bluez: dependency: transitive description: @@ -92,10 +100,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: "direct main" description: @@ -116,10 +124,10 @@ packages: dependency: transitive description: name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.10" fake_async: dependency: transitive description: @@ -158,10 +166,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -181,10 +189,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" lints: dependency: transitive description: @@ -229,10 +237,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -245,26 +253,26 @@ 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: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" process: dependency: transitive description: @@ -290,18 +298,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -330,10 +338,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" typed_data: dependency: transitive description: @@ -354,18 +362,18 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 url: "https://pub.dev" source: hosted - version: "11.7.1" + version: "11.10.0" web: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" webdriver: dependency: transitive description: @@ -378,10 +386,10 @@ packages: 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.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.3.0" diff --git a/bluetooth_low_energy/example/pubspec.yaml b/bluetooth_low_energy/example/pubspec.yaml index 73ea057..703e02c 100644 --- a/bluetooth_low_energy/example/pubspec.yaml +++ b/bluetooth_low_energy/example/pubspec.yaml @@ -29,7 +29,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 convert: ^3.1.1 - intl: ^0.18.1 + intl: ^0.19.0 dev_dependencies: integration_test: diff --git a/bluetooth_low_energy/example/windows/flutter/CMakeLists.txt b/bluetooth_low_energy/example/windows/flutter/CMakeLists.txt index 930d207..903f489 100644 --- a/bluetooth_low_energy/example/windows/flutter/CMakeLists.txt +++ b/bluetooth_low_energy/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/bluetooth_low_energy/example/windows/flutter/generated_plugin_registrant.cc b/bluetooth_low_energy/example/windows/flutter/generated_plugin_registrant.cc index 8b6d468..d051da9 100644 --- a/bluetooth_low_energy/example/windows/flutter/generated_plugin_registrant.cc +++ b/bluetooth_low_energy/example/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + BluetoothLowEnergyWindowsCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("BluetoothLowEnergyWindowsCApi")); } diff --git a/bluetooth_low_energy/example/windows/flutter/generated_plugins.cmake b/bluetooth_low_energy/example/windows/flutter/generated_plugins.cmake index b93c4c3..4b19aa9 100644 --- a/bluetooth_low_energy/example/windows/flutter/generated_plugins.cmake +++ b/bluetooth_low_energy/example/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + bluetooth_low_energy_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/bluetooth_low_energy/lib/bluetooth_low_energy.dart b/bluetooth_low_energy/lib/bluetooth_low_energy.dart index b51330b..31d4ddd 100644 --- a/bluetooth_low_energy/lib/bluetooth_low_energy.dart +++ b/bluetooth_low_energy/lib/bluetooth_low_energy.dart @@ -1,14 +1,14 @@ /// A Flutter plugin for controlling the bluetooth low energy, supports central /// and peripheral apis. -library; +library bluetooth_low_energy; export 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart' hide - MyCentralManager, - MyPeripheralManager, - MyObject, + MyBluetoothLowEnergyPeer, MyCentral, MyPeripheral, - MyGattService, + MyGattAttribute, + MyGattAttributeUint8List, + MyGattDescriptor, MyGattCharacteristic, - MyGattDescriptor; + MyGattService; diff --git a/bluetooth_low_energy/pubspec.yaml b/bluetooth_low_energy/pubspec.yaml index 9ce0cb9..907f91b 100644 --- a/bluetooth_low_energy/pubspec.yaml +++ b/bluetooth_low_energy/pubspec.yaml @@ -1,6 +1,6 @@ name: bluetooth_low_energy description: A Flutter plugin for controlling the bluetooth low energy, supports central and peripheral apis. -version: 4.0.0 +version: 5.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy environment: @@ -10,10 +10,11 @@ environment: dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^4.0.0 - bluetooth_low_energy_android: ^4.0.0 - bluetooth_low_energy_darwin: ^4.0.0 - bluetooth_low_energy_linux: ^4.0.0 + bluetooth_low_energy_platform_interface: ^5.0.0 + bluetooth_low_energy_android: ^5.0.0 + bluetooth_low_energy_darwin: ^5.0.0 + bluetooth_low_energy_windows: ^5.0.0 + bluetooth_low_energy_linux: ^5.0.0 dev_dependencies: flutter_test: @@ -29,5 +30,7 @@ flutter: default_package: bluetooth_low_energy_darwin macos: default_package: bluetooth_low_energy_darwin + windows: + default_package: bluetooth_low_energy_windows linux: default_package: bluetooth_low_energy_linux diff --git a/bluetooth_low_energy_android/CHANGELOG.md b/bluetooth_low_energy_android/CHANGELOG.md index 79d5638..ff4ec05 100644 --- a/bluetooth_low_energy_android/CHANGELOG.md +++ b/bluetooth_low_energy_android/CHANGELOG.md @@ -1,3 +1,50 @@ +## 5.0.0 + +* Now `CentralManager#writeCharacteristic` and `PeripheralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes. +* Add `UUID#fromAddress` constructor. +* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`. +* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`. +* Add `PeripheralManager#readCharacteristic`. +* Remove `CentralManager#getMaximumWriteLength` method. +* Remove `PeripheralManager#getMaximumWriteLength` method. +* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`. +* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`. +* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`. +* Move `CentralManager#state` to `CentralManager#getState()`. +* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`. +* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`. +* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`. +* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`. +* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`. +* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#writeCharacteristic`. +* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`. +* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`. + +## 5.0.0-dev.4 + +* Fix the issue that `PeripheralMananger#startAdvertising` throws after powered off.\ +* Optimize project structure. + +## 5.0.0-dev.3 + +* Implements new Api. + +## 5.0.0-dev.2 + +* Optimize example. +* Add event logs. +* Fix the issue that PeripheralManager's service duplicated after hot reload. +* Fix the issue that `PeripheralManager#notifyCharacteristicChanged` lost data when value is larger then the MTU size. +* Optimize instances' retrieve speed. +* Update dependency. + +## 5.0.0-dev.1 + +* Implement the `5.0.0` api. +* Optimize example. +* Remove `CentralManager#getMaximumWriteLength` method. +* Remove `PeripheralManager#getMaximumWriteLength` method. + ## 4.0.0 * Remove `BluetoothLowEnergy` class. diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/BluetoothLowEnergyAndroid.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/BluetoothLowEnergyAndroid.kt index bdd3b6d..3ee4d2b 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/BluetoothLowEnergyAndroid.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/BluetoothLowEnergyAndroid.kt @@ -6,16 +6,16 @@ import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding /** BluetoothLowEnergyAndroid */ class BluetoothLowEnergyAndroid : FlutterPlugin, ActivityAware { - private lateinit var centralManager: MyCentralManager - private lateinit var peripheralManager: MyPeripheralManager + private lateinit var mCentralManager: MyCentralManager + private lateinit var mPeripheralManager: MyPeripheralManager override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { val context = binding.applicationContext val binaryMessenger = binding.binaryMessenger - centralManager = MyCentralManager(context, binaryMessenger) - peripheralManager = MyPeripheralManager(context, binaryMessenger) - MyCentralManagerHostApi.setUp(binaryMessenger, centralManager) - MyPeripheralManagerHostApi.setUp(binaryMessenger, peripheralManager) + mCentralManager = MyCentralManager(context, binaryMessenger) + mPeripheralManager = MyPeripheralManager(context, binaryMessenger) + MyCentralManagerHostApi.setUp(binaryMessenger, mCentralManager) + MyPeripheralManagerHostApi.setUp(binaryMessenger, mPeripheralManager) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { @@ -25,13 +25,13 @@ class BluetoothLowEnergyAndroid : FlutterPlugin, ActivityAware { } override fun onAttachedToActivity(binding: ActivityPluginBinding) { - centralManager.onAttachedToActivity(binding) - peripheralManager.onAttachedToActivity(binding) + mCentralManager.onAttachedToActivity(binding) + mPeripheralManager.onAttachedToActivity(binding) } override fun onDetachedFromActivity() { - centralManager.onDetachedFromActivity() - peripheralManager.onDetachedFromActivity() + mCentralManager.onDetachedFromActivity() + mPeripheralManager.onDetachedFromActivity() } override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyAdvertiseCallback.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyAdvertiseCallback.kt index 59d2aff..40c4671 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyAdvertiseCallback.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyAdvertiseCallback.kt @@ -3,14 +3,20 @@ package dev.yanshouwang.bluetooth_low_energy_android import android.bluetooth.le.AdvertiseCallback import android.bluetooth.le.AdvertiseSettings -class MyAdvertiseCallback(private val peripheralManager: MyPeripheralManager) : AdvertiseCallback() { +class MyAdvertiseCallback(manager: MyPeripheralManager) : AdvertiseCallback() { + private val mManager: MyPeripheralManager + + init { + mManager = manager + } + override fun onStartSuccess(settingsInEffect: AdvertiseSettings) { super.onStartSuccess(settingsInEffect) - peripheralManager.onStartSuccess(settingsInEffect) + mManager.onStartSuccess(settingsInEffect) } override fun onStartFailure(errorCode: Int) { super.onStartFailure(errorCode) - peripheralManager.onStartFailure(errorCode) + mManager.onStartFailure(errorCode) } } \ No newline at end of file diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.g.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.g.kt index 6b0865f..1ed89c2 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.g.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.g.kt @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v12.0.1), do not edit directly. +// Autogenerated from Pigeon (v15.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon package dev.yanshouwang.bluetooth_low_energy_android @@ -31,6 +31,9 @@ private fun wrapError(exception: Throwable): List { } } +private fun createConnectionError(channelName: String): FlutterError { + return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "")} + /** * Error class for passing custom error details to Flutter via a thrown PlatformException. * @property code The error code. @@ -82,84 +85,55 @@ enum class MyGattCharacteristicWriteTypeArgs(val raw: Int) { } } -/** Generated class from Pigeon that represents data sent in messages. */ -data class MyCentralManagerArgs ( - val stateNumberArgs: Long +enum class MyGattCharacteristicNotifyStateArgs(val raw: Int) { + NONE(0), + NOTIFY(1), + INDICATE(2); -) { companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MyCentralManagerArgs { - val stateNumberArgs = list[0].let { if (it is Int) it.toLong() else it as Long } - return MyCentralManagerArgs(stateNumberArgs) + fun ofRaw(raw: Int): MyGattCharacteristicNotifyStateArgs? { + return values().firstOrNull { it.raw == raw } } } - fun toList(): List { - return listOf( - stateNumberArgs, - ) +} + +enum class MyGattStatusArgs(val raw: Int) { + SUCCESS(0), + READNOTPERMITTED(1), + WRITENOTPERMITTED(2), + REQUESTNOTSUPPORTED(3), + INVALIDOFFSET(4), + INSUFFICIENTAUTHENTICATION(5), + INSUFFICIENTENCRYPTION(6), + INVALIDATTRIBUTELENGTH(7), + CONNECTIONCONGESTED(8), + FAILURE(9); + + companion object { + fun ofRaw(raw: Int): MyGattStatusArgs? { + return values().firstOrNull { it.raw == raw } + } } } /** Generated class from Pigeon that represents data sent in messages. */ -data class MyPeripheralManagerArgs ( - val stateNumberArgs: Long +data class MyManufacturerSpecificDataArgs ( + val idArgs: Long, + val dataArgs: ByteArray ) { companion object { @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MyPeripheralManagerArgs { - val stateNumberArgs = list[0].let { if (it is Int) it.toLong() else it as Long } - return MyPeripheralManagerArgs(stateNumberArgs) + fun fromList(list: List): MyManufacturerSpecificDataArgs { + val idArgs = list[0].let { if (it is Int) it.toLong() else it as Long } + val dataArgs = list[1] as ByteArray + return MyManufacturerSpecificDataArgs(idArgs, dataArgs) } } fun toList(): List { return listOf( - stateNumberArgs, - ) - } -} - -/** Generated class from Pigeon that represents data sent in messages. */ -data class MyCentralArgs ( - val hashCodeArgs: Long, - val uuidArgs: String - -) { - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MyCentralArgs { - val hashCodeArgs = list[0].let { if (it is Int) it.toLong() else it as Long } - val uuidArgs = list[1] as String - return MyCentralArgs(hashCodeArgs, uuidArgs) - } - } - fun toList(): List { - return listOf( - hashCodeArgs, - uuidArgs, - ) - } -} - -/** Generated class from Pigeon that represents data sent in messages. */ -data class MyPeripheralArgs ( - val hashCodeArgs: Long, - val uuidArgs: String - -) { - companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MyPeripheralArgs { - val hashCodeArgs = list[0].let { if (it is Int) it.toLong() else it as Long } - val uuidArgs = list[1] as String - return MyPeripheralArgs(hashCodeArgs, uuidArgs) - } - } - fun toList(): List { - return listOf( - hashCodeArgs, - uuidArgs, + idArgs, + dataArgs, ) } } @@ -195,48 +169,64 @@ data class MyAdvertisementArgs ( } /** Generated class from Pigeon that represents data sent in messages. */ -data class MyManufacturerSpecificDataArgs ( - val idArgs: Long, - val dataArgs: ByteArray +data class MyCentralArgs ( + val addressArgs: String ) { companion object { @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MyManufacturerSpecificDataArgs { - val idArgs = list[0].let { if (it is Int) it.toLong() else it as Long } - val dataArgs = list[1] as ByteArray - return MyManufacturerSpecificDataArgs(idArgs, dataArgs) + fun fromList(list: List): MyCentralArgs { + val addressArgs = list[0] as String + return MyCentralArgs(addressArgs) } } fun toList(): List { return listOf( - idArgs, - dataArgs, + addressArgs, ) } } /** Generated class from Pigeon that represents data sent in messages. */ -data class MyGattServiceArgs ( - val hashCodeArgs: Long, - val uuidArgs: String, - val characteristicsArgs: List +data class MyPeripheralArgs ( + val addressArgs: String ) { companion object { @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MyGattServiceArgs { + fun fromList(list: List): MyPeripheralArgs { + val addressArgs = list[0] as String + return MyPeripheralArgs(addressArgs) + } + } + fun toList(): List { + return listOf( + addressArgs, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class MyGattDescriptorArgs ( + val hashCodeArgs: Long, + val uuidArgs: String, + val valueArgs: ByteArray? = null + +) { + companion object { + @Suppress("UNCHECKED_CAST") + fun fromList(list: List): MyGattDescriptorArgs { val hashCodeArgs = list[0].let { if (it is Int) it.toLong() else it as Long } val uuidArgs = list[1] as String - val characteristicsArgs = list[2] as List - return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs) + val valueArgs = list[2] as ByteArray? + return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, valueArgs) } } fun toList(): List { return listOf( hashCodeArgs, uuidArgs, - characteristicsArgs, + valueArgs, ) } } @@ -270,26 +260,26 @@ data class MyGattCharacteristicArgs ( } /** Generated class from Pigeon that represents data sent in messages. */ -data class MyGattDescriptorArgs ( +data class MyGattServiceArgs ( val hashCodeArgs: Long, val uuidArgs: String, - val valueArgs: ByteArray? = null + val characteristicsArgs: List ) { companion object { @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MyGattDescriptorArgs { + fun fromList(list: List): MyGattServiceArgs { val hashCodeArgs = list[0].let { if (it is Int) it.toLong() else it as Long } val uuidArgs = list[1] as String - val valueArgs = list[2] as ByteArray? - return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, valueArgs) + val characteristicsArgs = list[2] as List + return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs) } } fun toList(): List { return listOf( hashCodeArgs, uuidArgs, - valueArgs, + characteristicsArgs, ) } } @@ -300,20 +290,15 @@ private object MyCentralManagerHostApiCodec : StandardMessageCodec() { return when (type) { 128.toByte() -> { return (readValue(buffer) as? List)?.let { - MyCentralManagerArgs.fromList(it) + MyGattCharacteristicArgs.fromList(it) } } 129.toByte() -> { - return (readValue(buffer) as? List)?.let { - MyGattCharacteristicArgs.fromList(it) - } - } - 130.toByte() -> { return (readValue(buffer) as? List)?.let { MyGattDescriptorArgs.fromList(it) } } - 131.toByte() -> { + 130.toByte() -> { return (readValue(buffer) as? List)?.let { MyGattServiceArgs.fromList(it) } @@ -323,20 +308,16 @@ private object MyCentralManagerHostApiCodec : StandardMessageCodec() { } override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { - is MyCentralManagerArgs -> { + is MyGattCharacteristicArgs -> { stream.write(128) writeValue(stream, value.toList()) } - is MyGattCharacteristicArgs -> { + is MyGattDescriptorArgs -> { stream.write(129) writeValue(stream, value.toList()) } - is MyGattDescriptorArgs -> { - stream.write(130) - writeValue(stream, value.toList()) - } is MyGattServiceArgs -> { - stream.write(131) + stream.write(130) writeValue(stream, value.toList()) } else -> super.writeValue(stream, value) @@ -346,20 +327,19 @@ private object MyCentralManagerHostApiCodec : StandardMessageCodec() { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface MyCentralManagerHostApi { - fun setUp(callback: (Result) -> Unit) + fun setUp() fun startDiscovery(callback: (Result) -> Unit) fun stopDiscovery() - fun connect(peripheralHashCodeArgs: Long, callback: (Result) -> Unit) - fun disconnect(peripheralHashCodeArgs: Long, callback: (Result) -> Unit) - fun getMaximumWriteLength(peripheralHashCodeArgs: Long, typeNumberArgs: Long): Long - fun readRSSI(peripheralHashCodeArgs: Long, callback: (Result) -> Unit) - fun discoverGATT(peripheralHashCodeArgs: Long, callback: (Result>) -> Unit) - fun requestMTU(peripheralHashCodeArgs: Long, mtuArgs: Long, callback: (Result) -> Unit) - fun readCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, callback: (Result) -> Unit) - fun writeCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, typeNumberArgs: Long, callback: (Result) -> Unit) - fun notifyCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, stateArgs: Boolean, callback: (Result) -> Unit) - fun readDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, callback: (Result) -> Unit) - fun writeDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result) -> Unit) + fun connect(addressArgs: String, callback: (Result) -> Unit) + fun disconnect(addressArgs: String, callback: (Result) -> Unit) + fun requestMTU(addressArgs: String, mtuArgs: Long, callback: (Result) -> Unit) + fun readRSSI(addressArgs: String, callback: (Result) -> Unit) + fun discoverServices(addressArgs: String, callback: (Result>) -> Unit) + fun readCharacteristic(addressArgs: String, hashCodeArgs: Long, callback: (Result) -> Unit) + fun writeCharacteristic(addressArgs: String, hashCodeArgs: Long, valueArgs: ByteArray, typeNumberArgs: Long, callback: (Result) -> Unit) + fun setCharacteristicNotifyState(addressArgs: String, hashCodeArgs: Long, stateNumberArgs: Long, callback: (Result) -> Unit) + fun readDescriptor(addressArgs: String, hashCodeArgs: Long, callback: (Result) -> Unit) + fun writeDescriptor(addressArgs: String, hashCodeArgs: Long, valueArgs: ByteArray, callback: (Result) -> Unit) companion object { /** The codec used by MyCentralManagerHostApi. */ @@ -373,15 +353,14 @@ interface MyCentralManagerHostApi { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setUp", codec) if (api != null) { channel.setMessageHandler { _, reply -> - api.setUp() { result: Result -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(wrapResult(data)) - } + var wrapped: List + try { + api.setUp() + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) } + reply.reply(wrapped) } } else { channel.setMessageHandler(null) @@ -426,8 +405,8 @@ interface MyCentralManagerHostApi { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - api.connect(peripheralHashCodeArgsArg) { result: Result -> + val addressArgsArg = args[0] as String + api.connect(addressArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -445,8 +424,8 @@ interface MyCentralManagerHostApi { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - api.disconnect(peripheralHashCodeArgsArg) { result: Result -> + val addressArgsArg = args[0] as String + api.disconnect(addressArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -460,19 +439,21 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.getMaximumWriteLength", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.requestMTU", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val typeNumberArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } - var wrapped: List - try { - wrapped = listOf(api.getMaximumWriteLength(peripheralHashCodeArgsArg, typeNumberArgsArg)) - } catch (exception: Throwable) { - wrapped = wrapError(exception) + val addressArgsArg = args[0] as String + val mtuArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + api.requestMTU(addressArgsArg, mtuArgsArg) { 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) @@ -483,8 +464,8 @@ interface MyCentralManagerHostApi { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - api.readRSSI(peripheralHashCodeArgsArg) { result: Result -> + val addressArgsArg = args[0] as String + api.readRSSI(addressArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -499,33 +480,12 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.discoverGATT", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.discoverServices", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - api.discoverGATT(peripheralHashCodeArgsArg) { result: Result> -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(wrapResult(data)) - } - } - } - } else { - channel.setMessageHandler(null) - } - } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.requestMTU", codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val mtuArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } - api.requestMTU(peripheralHashCodeArgsArg, mtuArgsArg) { result: Result -> + val addressArgsArg = args[0] as String + api.discoverServices(addressArgsArg) { result: Result> -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -544,9 +504,9 @@ interface MyCentralManagerHostApi { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } - api.readCharacteristic(peripheralHashCodeArgsArg, characteristicHashCodeArgsArg) { result: Result -> + val addressArgsArg = args[0] as String + val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + api.readCharacteristic(addressArgsArg, hashCodeArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -565,11 +525,11 @@ interface MyCentralManagerHostApi { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val addressArgsArg = args[0] as String + val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } val valueArgsArg = args[2] as ByteArray val typeNumberArgsArg = args[3].let { if (it is Int) it.toLong() else it as Long } - api.writeCharacteristic(peripheralHashCodeArgsArg, characteristicHashCodeArgsArg, valueArgsArg, typeNumberArgsArg) { result: Result -> + api.writeCharacteristic(addressArgsArg, hashCodeArgsArg, valueArgsArg, typeNumberArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -583,14 +543,14 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.notifyCharacteristic", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setCharacteristicNotifyState", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } - val stateArgsArg = args[2] as Boolean - api.notifyCharacteristic(peripheralHashCodeArgsArg, characteristicHashCodeArgsArg, stateArgsArg) { result: Result -> + val addressArgsArg = args[0] as String + val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val stateNumberArgsArg = args[2].let { if (it is Int) it.toLong() else it as Long } + api.setCharacteristicNotifyState(addressArgsArg, hashCodeArgsArg, stateNumberArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -608,9 +568,9 @@ interface MyCentralManagerHostApi { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val descriptorHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } - api.readDescriptor(peripheralHashCodeArgsArg, descriptorHashCodeArgsArg) { result: Result -> + val addressArgsArg = args[0] as String + val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + api.readDescriptor(addressArgsArg, hashCodeArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -629,10 +589,10 @@ interface MyCentralManagerHostApi { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val descriptorHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val addressArgsArg = args[0] as String + val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } val valueArgsArg = args[2] as ByteArray - api.writeDescriptor(peripheralHashCodeArgsArg, descriptorHashCodeArgsArg, valueArgsArg) { result: Result -> + api.writeDescriptor(addressArgsArg, hashCodeArgsArg, valueArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -658,21 +618,11 @@ private object MyCentralManagerFlutterApiCodec : StandardMessageCodec() { } } 129.toByte() -> { - return (readValue(buffer) as? List)?.let { - MyGattCharacteristicArgs.fromList(it) - } - } - 130.toByte() -> { - return (readValue(buffer) as? List)?.let { - MyGattDescriptorArgs.fromList(it) - } - } - 131.toByte() -> { return (readValue(buffer) as? List)?.let { MyManufacturerSpecificDataArgs.fromList(it) } } - 132.toByte() -> { + 130.toByte() -> { return (readValue(buffer) as? List)?.let { MyPeripheralArgs.fromList(it) } @@ -686,20 +636,12 @@ private object MyCentralManagerFlutterApiCodec : StandardMessageCodec() { stream.write(128) writeValue(stream, value.toList()) } - is MyGattCharacteristicArgs -> { + is MyManufacturerSpecificDataArgs -> { stream.write(129) writeValue(stream, value.toList()) } - is MyGattDescriptorArgs -> { - stream.write(130) - writeValue(stream, value.toList()) - } - is MyManufacturerSpecificDataArgs -> { - stream.write(131) - writeValue(stream, value.toList()) - } is MyPeripheralArgs -> { - stream.write(132) + stream.write(130) writeValue(stream, value.toList()) } else -> super.writeValue(stream, value) @@ -717,58 +659,77 @@ class MyCentralManagerFlutterApi(private val binaryMessenger: BinaryMessenger) { } } fun onStateChanged(stateNumberArgsArg: Long, callback: (Result) -> Unit) { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onStateChanged", codec) + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onStateChanged" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) channel.send(listOf(stateNumberArgsArg)) { if (it is List<*>) { if (it.size > 1) { - callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { - callback(Result.success(Unit)); + callback(Result.success(Unit)) } } else { - callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); + callback(Result.failure(createConnectionError(channelName))) } } } fun onDiscovered(peripheralArgsArg: MyPeripheralArgs, rssiArgsArg: Long, advertisementArgsArg: MyAdvertisementArgs, callback: (Result) -> Unit) { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onDiscovered", codec) + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onDiscovered" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) channel.send(listOf(peripheralArgsArg, rssiArgsArg, advertisementArgsArg)) { if (it is List<*>) { if (it.size > 1) { - callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { - callback(Result.success(Unit)); + callback(Result.success(Unit)) } } else { - callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); + callback(Result.failure(createConnectionError(channelName))) } } } - fun onPeripheralStateChanged(peripheralArgsArg: MyPeripheralArgs, stateArgsArg: Boolean, callback: (Result) -> Unit) { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onPeripheralStateChanged", codec) - channel.send(listOf(peripheralArgsArg, stateArgsArg)) { + fun onConnectionStateChanged(addressArgsArg: String, stateArgsArg: Boolean, callback: (Result) -> Unit) { + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onConnectionStateChanged" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(addressArgsArg, stateArgsArg)) { if (it is List<*>) { if (it.size > 1) { - callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { - callback(Result.success(Unit)); + callback(Result.success(Unit)) } } else { - callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); + callback(Result.failure(createConnectionError(channelName))) } } } - fun onCharacteristicValueChanged(characteristicArgsArg: MyGattCharacteristicArgs, valueArgsArg: ByteArray, callback: (Result) -> Unit) { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicValueChanged", codec) - channel.send(listOf(characteristicArgsArg, valueArgsArg)) { + fun onMtuChanged(addressArgsArg: String, mtuArgsArg: Long, callback: (Result) -> Unit) { + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onMtuChanged" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(addressArgsArg, mtuArgsArg)) { if (it is List<*>) { if (it.size > 1) { - callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { - callback(Result.success(Unit)); + callback(Result.success(Unit)) } } else { - callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); + callback(Result.failure(createConnectionError(channelName))) + } + } + } + fun onCharacteristicNotified(addressArgsArg: String, hashCodeArgsArg: Long, valueArgsArg: ByteArray, callback: (Result) -> Unit) { + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicNotified" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(addressArgsArg, hashCodeArgsArg, valueArgsArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) } } } @@ -802,11 +763,6 @@ private object MyPeripheralManagerHostApiCodec : StandardMessageCodec() { MyManufacturerSpecificDataArgs.fromList(it) } } - 133.toByte() -> { - return (readValue(buffer) as? List)?.let { - MyPeripheralManagerArgs.fromList(it) - } - } else -> super.readValueOfType(type, buffer) } } @@ -832,10 +788,6 @@ private object MyPeripheralManagerHostApiCodec : StandardMessageCodec() { stream.write(132) writeValue(stream, value.toList()) } - is MyPeripheralManagerArgs -> { - stream.write(133) - writeValue(stream, value.toList()) - } else -> super.writeValue(stream, value) } } @@ -843,16 +795,14 @@ private object MyPeripheralManagerHostApiCodec : StandardMessageCodec() { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface MyPeripheralManagerHostApi { - fun setUp(callback: (Result) -> Unit) + fun setUp() fun addService(serviceArgs: MyGattServiceArgs, callback: (Result) -> Unit) - fun removeService(serviceHashCodeArgs: Long) + fun removeService(hashCodeArgs: Long) fun clearServices() fun startAdvertising(advertisementArgs: MyAdvertisementArgs, callback: (Result) -> Unit) fun stopAdvertising() - fun getMaximumWriteLength(centralHashCodeArgs: Long): Long - fun sendReadCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean, valueArgs: ByteArray) - fun sendWriteCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean) - fun notifyCharacteristicValueChanged(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result) -> Unit) + fun sendResponse(addressArgs: String, idArgs: Long, statusNumberArgs: Long, offsetArgs: Long, valueArgs: ByteArray?) + fun notifyCharacteristicChanged(hashCodeArgs: Long, valueArgs: ByteArray, confirmArgs: Boolean, addressArgs: String, callback: (Result) -> Unit) companion object { /** The codec used by MyPeripheralManagerHostApi. */ @@ -866,15 +816,14 @@ interface MyPeripheralManagerHostApi { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.setUp", codec) if (api != null) { channel.setMessageHandler { _, reply -> - api.setUp() { result: Result -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(wrapResult(data)) - } + var wrapped: List + try { + api.setUp() + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) } + reply.reply(wrapped) } } else { channel.setMessageHandler(null) @@ -904,10 +853,10 @@ interface MyPeripheralManagerHostApi { if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val serviceHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } + val hashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } var wrapped: List try { - api.removeService(serviceHashCodeArgsArg) + api.removeService(hashCodeArgsArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -972,37 +921,18 @@ interface MyPeripheralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.getMaximumWriteLength", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.sendResponse", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val centralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - var wrapped: List - try { - wrapped = listOf(api.getMaximumWriteLength(centralHashCodeArgsArg)) - } catch (exception: Throwable) { - wrapped = wrapError(exception) - } - reply.reply(wrapped) - } - } else { - channel.setMessageHandler(null) - } - } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.sendReadCharacteristicReply", codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val centralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } - val idArgsArg = args[2].let { if (it is Int) it.toLong() else it as Long } + val addressArgsArg = args[0] as String + val idArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val statusNumberArgsArg = args[2].let { if (it is Int) it.toLong() else it as Long } val offsetArgsArg = args[3].let { if (it is Int) it.toLong() else it as Long } - val statusArgsArg = args[4] as Boolean - val valueArgsArg = args[5] as ByteArray + val valueArgsArg = args[4] as ByteArray? var wrapped: List try { - api.sendReadCharacteristicReply(centralHashCodeArgsArg, characteristicHashCodeArgsArg, idArgsArg, offsetArgsArg, statusArgsArg, valueArgsArg) + api.sendResponse(addressArgsArg, idArgsArg, statusNumberArgsArg, offsetArgsArg, valueArgsArg) wrapped = listOf(null) } catch (exception: Throwable) { wrapped = wrapError(exception) @@ -1014,37 +944,15 @@ interface MyPeripheralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.sendWriteCharacteristicReply", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.notifyCharacteristicChanged", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val centralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } - val idArgsArg = args[2].let { if (it is Int) it.toLong() else it as Long } - val offsetArgsArg = args[3].let { if (it is Int) it.toLong() else it as Long } - val statusArgsArg = args[4] as Boolean - var wrapped: List - try { - api.sendWriteCharacteristicReply(centralHashCodeArgsArg, characteristicHashCodeArgsArg, idArgsArg, offsetArgsArg, statusArgsArg) - wrapped = listOf(null) - } catch (exception: Throwable) { - wrapped = wrapError(exception) - } - reply.reply(wrapped) - } - } else { - channel.setMessageHandler(null) - } - } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.notifyCharacteristicValueChanged", codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val centralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } - val valueArgsArg = args[2] as ByteArray - api.notifyCharacteristicValueChanged(centralHashCodeArgsArg, characteristicHashCodeArgsArg, valueArgsArg) { result: Result -> + val hashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } + val valueArgsArg = args[1] as ByteArray + val confirmArgsArg = args[2] as Boolean + val addressArgsArg = args[3] as String + api.notifyCharacteristicChanged(hashCodeArgsArg, valueArgsArg, confirmArgsArg, addressArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -1069,16 +977,6 @@ private object MyPeripheralManagerFlutterApiCodec : StandardMessageCodec() { MyCentralArgs.fromList(it) } } - 129.toByte() -> { - return (readValue(buffer) as? List)?.let { - MyGattCharacteristicArgs.fromList(it) - } - } - 130.toByte() -> { - return (readValue(buffer) as? List)?.let { - MyGattDescriptorArgs.fromList(it) - } - } else -> super.readValueOfType(type, buffer) } } @@ -1088,14 +986,6 @@ private object MyPeripheralManagerFlutterApiCodec : StandardMessageCodec() { stream.write(128) writeValue(stream, value.toList()) } - is MyGattCharacteristicArgs -> { - stream.write(129) - writeValue(stream, value.toList()) - } - is MyGattDescriptorArgs -> { - stream.write(130) - writeValue(stream, value.toList()) - } else -> super.writeValue(stream, value) } } @@ -1111,58 +1001,107 @@ class MyPeripheralManagerFlutterApi(private val binaryMessenger: BinaryMessenger } } fun onStateChanged(stateNumberArgsArg: Long, callback: (Result) -> Unit) { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onStateChanged", codec) + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onStateChanged" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) channel.send(listOf(stateNumberArgsArg)) { if (it is List<*>) { if (it.size > 1) { - callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { - callback(Result.success(Unit)); + callback(Result.success(Unit)) } } else { - callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); + callback(Result.failure(createConnectionError(channelName))) } } } - fun onReadCharacteristicCommandReceived(centralArgsArg: MyCentralArgs, characteristicArgsArg: MyGattCharacteristicArgs, idArgsArg: Long, offsetArgsArg: Long, callback: (Result) -> Unit) { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived", codec) - channel.send(listOf(centralArgsArg, characteristicArgsArg, idArgsArg, offsetArgsArg)) { + fun onConnectionStateChanged(centralArgsArg: MyCentralArgs, stateArgsArg: Boolean, callback: (Result) -> Unit) { + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onConnectionStateChanged" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(centralArgsArg, stateArgsArg)) { if (it is List<*>) { if (it.size > 1) { - callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { - callback(Result.success(Unit)); + callback(Result.success(Unit)) } } else { - callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); + callback(Result.failure(createConnectionError(channelName))) } } } - fun onWriteCharacteristicCommandReceived(centralArgsArg: MyCentralArgs, characteristicArgsArg: MyGattCharacteristicArgs, idArgsArg: Long, offsetArgsArg: Long, valueArgsArg: ByteArray, callback: (Result) -> Unit) { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived", codec) - channel.send(listOf(centralArgsArg, characteristicArgsArg, idArgsArg, offsetArgsArg, valueArgsArg)) { + fun onMtuChanged(addressArgsArg: String, mtuArgsArg: Long, callback: (Result) -> Unit) { + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onMtuChanged" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(addressArgsArg, mtuArgsArg)) { if (it is List<*>) { if (it.size > 1) { - callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { - callback(Result.success(Unit)); + callback(Result.success(Unit)) } } else { - callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); + callback(Result.failure(createConnectionError(channelName))) } } } - fun onNotifyCharacteristicCommandReceived(centralArgsArg: MyCentralArgs, characteristicArgsArg: MyGattCharacteristicArgs, stateArgsArg: Boolean, callback: (Result) -> Unit) { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived", codec) - channel.send(listOf(centralArgsArg, characteristicArgsArg, stateArgsArg)) { + fun onCharacteristicReadRequest(addressArgsArg: String, hashCodeArgsArg: Long, idArgsArg: Long, offsetArgsArg: Long, callback: (Result) -> Unit) { + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(addressArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg)) { if (it is List<*>) { if (it.size > 1) { - callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else { - callback(Result.success(Unit)); + callback(Result.success(Unit)) } } else { - callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); + callback(Result.failure(createConnectionError(channelName))) + } + } + } + fun onCharacteristicWriteRequest(addressArgsArg: String, hashCodeArgsArg: Long, idArgsArg: Long, offsetArgsArg: Long, valueArgsArg: ByteArray, preparedWriteArgsArg: Boolean, responseNeededArgsArg: Boolean, callback: (Result) -> Unit) { + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(addressArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg, valueArgsArg, preparedWriteArgsArg, responseNeededArgsArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } + fun onExecuteWrite(addressArgsArg: String, idArgsArg: Long, executeArgsArg: Boolean, callback: (Result) -> Unit) { + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onExecuteWrite" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(addressArgsArg, idArgsArg, executeArgsArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } + fun onCharacteristicNotifyStateChanged(addressArgsArg: String, hashCodeArgsArg: Long, stateNumberArgsArg: Long, callback: (Result) -> Unit) { + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(addressArgsArg, hashCodeArgsArg, stateNumberArgsArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) } } } diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.kt index 7ca8c42..fdfe12a 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.kt @@ -2,108 +2,54 @@ package dev.yanshouwang.bluetooth_low_energy_android import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattDescriptor import android.bluetooth.BluetoothGattService import android.bluetooth.le.AdvertiseData -import android.bluetooth.le.ScanRecord import android.bluetooth.le.ScanResult import android.os.ParcelUuid import android.util.SparseArray import java.util.UUID -val Any.TAG get() = this::class.java.simpleName as String - -val BluetoothAdapter.stateArgs: MyBluetoothLowEnergyStateArgs - get() = state.toBluetoothLowEnergyStateArgs() - -fun Int.toBluetoothLowEnergyStateArgs(): MyBluetoothLowEnergyStateArgs { +//region ToObj +fun MyGattCharacteristicWriteTypeArgs.toType(): Int { return when (this) { - BluetoothAdapter.STATE_ON -> MyBluetoothLowEnergyStateArgs.POWEREDON - else -> MyBluetoothLowEnergyStateArgs.POWEREDOFF + MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE } } -fun BluetoothDevice.toPeripheralArgs(): MyPeripheralArgs { - val hashCodeArgs = hashCode().toLong() - val uuid = this.uuid.toString() - return MyPeripheralArgs(hashCodeArgs, uuid) +fun MyGattCharacteristicNotifyStateArgs.toValue(): ByteArray { + return when (this) { + MyGattCharacteristicNotifyStateArgs.NONE -> BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE + MyGattCharacteristicNotifyStateArgs.NOTIFY -> BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + MyGattCharacteristicNotifyStateArgs.INDICATE -> BluetoothGattDescriptor.ENABLE_INDICATION_VALUE + } } -fun BluetoothDevice.toCentralArgs(): MyCentralArgs { - val hashCodeArgs = hashCode().toLong() - val uuid = this.uuid.toString() - return MyCentralArgs(hashCodeArgs, uuid) +fun MyGattStatusArgs.toStatus(): Int { + return when (this) { + MyGattStatusArgs.SUCCESS -> BluetoothGatt.GATT_SUCCESS + MyGattStatusArgs.READNOTPERMITTED -> BluetoothGatt.GATT_READ_NOT_PERMITTED + MyGattStatusArgs.WRITENOTPERMITTED -> BluetoothGatt.GATT_READ_NOT_PERMITTED + MyGattStatusArgs.REQUESTNOTSUPPORTED -> BluetoothGatt.GATT_READ_NOT_PERMITTED + MyGattStatusArgs.INVALIDOFFSET -> BluetoothGatt.GATT_INVALID_OFFSET + MyGattStatusArgs.INSUFFICIENTAUTHENTICATION -> BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION + MyGattStatusArgs.INSUFFICIENTENCRYPTION -> BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION + MyGattStatusArgs.INVALIDATTRIBUTELENGTH -> BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH + MyGattStatusArgs.CONNECTIONCONGESTED -> BluetoothGatt.GATT_CONNECTION_CONGESTED + MyGattStatusArgs.FAILURE -> BluetoothGatt.GATT_FAILURE + } } -val BluetoothDevice.uuid: UUID - get() { - val node = address.filter { char -> char != ':' } - // We don't know the timestamp of the bluetooth device, use nil UUID as prefix. - return UUID.fromString("00000000-0000-0000-0000-$node") - } - -val ScanResult.advertisementArgs: MyAdvertisementArgs - get() { - val record = scanRecord - return if (record == null) { - val nameArgs = null - val serviceUUIDsArgs = emptyList() - val serviceDataArgs = emptyMap() - val manufacturerSpecificDataArgs = null - MyAdvertisementArgs(nameArgs, serviceUUIDsArgs, serviceDataArgs, manufacturerSpecificDataArgs) - } else { - val nameArgs = record.deviceName - val serviceUUIDsArgs = record.serviceUuids?.map { uuid -> uuid.toString() } - ?: emptyList() - val pairs = record.serviceData.map { (uuid, value) -> - val key = uuid.toString() - return@map Pair(key, value) - }.toTypedArray() - val serviceDataArgs = mapOf(*pairs) - val manufacturerSpecificDataArgs = record.manufacturerSpecificData.toManufacturerSpecificDataArgs() - MyAdvertisementArgs(nameArgs, serviceUUIDsArgs, serviceDataArgs, manufacturerSpecificDataArgs) - } - } - -fun SparseArray.toManufacturerSpecificDataArgs(): MyManufacturerSpecificDataArgs? { - var index = 0 - val size = size() - val itemsArgs = mutableListOf() - while (index < size) { - val idArgs = keyAt(index).toLong() - val dataArgs = valueAt(index) - val itemArgs = MyManufacturerSpecificDataArgs(idArgs, dataArgs) - itemsArgs.add(itemArgs) - index++ - } - return itemsArgs.lastOrNull() -} - -val ScanRecord.rawValues: Map - get() { - val rawValues = mutableMapOf() - var index = 0 - val size = bytes.size - while (index < size) { - val length = bytes[index++].toInt() and 0xff - if (length == 0) { - break - } - val end = index + length - val type = bytes[index++] - val value = bytes.slice(index until end).toByteArray() - rawValues[type] = value - index = end - } - return rawValues.toMap() - } - fun MyAdvertisementArgs.toAdvertiseData(adapter: BluetoothAdapter): AdvertiseData { val advertiseDataBuilder = AdvertiseData.Builder() if (nameArgs == null) { advertiseDataBuilder.setIncludeDeviceName(false) } else { + // TODO: There is an issue that Android will use the cached name before setName takes effect. + // see https://stackoverflow.com/questions/8377558/change-the-android-bluetooth-device-name adapter.name = nameArgs advertiseDataBuilder.setIncludeDeviceName(true) } @@ -124,48 +70,55 @@ fun MyAdvertisementArgs.toAdvertiseData(adapter: BluetoothAdapter): AdvertiseDat return advertiseDataBuilder.build() } -fun BluetoothGattService.toManufacturerSpecificDataArgs(characteristicsArgs: List): MyGattServiceArgs { - val hashCodeArgs = hashCode().toLong() - val uuidArgs = this.uuid.toString() - return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs) +fun MyGattDescriptorArgs.toDescriptor(): BluetoothGattDescriptor { + val uuid = UUID.fromString(uuidArgs) + val permissions = + BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE + return BluetoothGattDescriptor(uuid, permissions) } -fun BluetoothGattCharacteristic.toManufacturerSpecificDataArgs(descriptorsArgs: List): MyGattCharacteristicArgs { - val hashCodeArgs = hashCode().toLong() - val uuidArgs = this.uuid.toString() - return MyGattCharacteristicArgs(hashCodeArgs, uuidArgs, propertyNumbersArgs, descriptorsArgs) +fun MyGattCharacteristicArgs.toCharacteristic(): BluetoothGattCharacteristic { + val uuid = UUID.fromString(uuidArgs) + val properties = getProperties() + val permissions = getPermissions() + return BluetoothGattCharacteristic(uuid, properties, permissions) } -val BluetoothGattCharacteristic.propertyNumbersArgs: List - get() { - val numbersArgs = mutableListOf() - if (properties and BluetoothGattCharacteristic.PROPERTY_READ != 0) { - val number = MyGattCharacteristicPropertyArgs.READ.raw.toLong() - numbersArgs.add(number) - } - if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE != 0) { - val number = MyGattCharacteristicPropertyArgs.WRITE.raw.toLong() - numbersArgs.add(number) - } - if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE != 0) { - val number = MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE.raw.toLong() - numbersArgs.add(number) - } - if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) { - val number = MyGattCharacteristicPropertyArgs.NOTIFY.raw.toLong() - numbersArgs.add(number) - } - if (properties and BluetoothGattCharacteristic.PROPERTY_INDICATE != 0) { - val number = MyGattCharacteristicPropertyArgs.INDICATE.raw.toLong() - numbersArgs.add(number) - } - return numbersArgs +fun MyGattCharacteristicArgs.getProperties(): Int { + val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args -> + val raw = args.toInt() + MyGattCharacteristicPropertyArgs.ofRaw(raw) } + val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ) + val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE) + val writeWithoutResponse = + propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE) + val notify = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.NOTIFY) + val indicate = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.INDICATE) + var properties = 0 + if (read) properties = properties or BluetoothGattCharacteristic.PROPERTY_READ + if (write) properties = properties or BluetoothGattCharacteristic.PROPERTY_WRITE + if (writeWithoutResponse) properties = + properties or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE + if (notify) properties = properties or BluetoothGattCharacteristic.PROPERTY_NOTIFY + if (indicate) properties = properties or BluetoothGattCharacteristic.PROPERTY_INDICATE + return properties +} -fun BluetoothGattDescriptor.toManufacturerSpecificDataArgs(): MyGattDescriptorArgs { - val hashCodeArgs = hashCode().toLong() - val uuidArgs = this.uuid.toString() - return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, null) +fun MyGattCharacteristicArgs.getPermissions(): Int { + val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args -> + val raw = args.toInt() + MyGattCharacteristicPropertyArgs.ofRaw(raw) + } + val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ) + val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE) + val writeWithoutResponse = + propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE) + var permissions = 0 + if (read) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_READ + if (write || writeWithoutResponse) permissions = + permissions or BluetoothGattCharacteristic.PERMISSION_WRITE + return permissions } fun MyGattServiceArgs.toService(): BluetoothGattService { @@ -173,61 +126,142 @@ fun MyGattServiceArgs.toService(): BluetoothGattService { val serviceType = BluetoothGattService.SERVICE_TYPE_PRIMARY return BluetoothGattService(uuid, serviceType) } +//endregion -fun MyGattCharacteristicArgs.toCharacteristic(): BluetoothGattCharacteristic { - val uuid = UUID.fromString(uuidArgs) - return BluetoothGattCharacteristic(uuid, properties, permissions) -} - -val MyGattCharacteristicArgs.properties: Int - get() { - val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args -> - val raw = args.toInt() - MyGattCharacteristicPropertyArgs.ofRaw(raw) - } - val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ) - val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE) - val writeWithoutResponse = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE) - val notify = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.NOTIFY) - val indicate = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.INDICATE) - var properties = 0 - if (read) properties = properties or BluetoothGattCharacteristic.PROPERTY_READ - if (write) properties = properties or BluetoothGattCharacteristic.PROPERTY_WRITE - if (writeWithoutResponse) properties = properties or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE - if (notify) properties = properties or BluetoothGattCharacteristic.PROPERTY_NOTIFY - if (indicate) properties = properties or BluetoothGattCharacteristic.PROPERTY_INDICATE - return properties - } - -val MyGattCharacteristicArgs.permissions: Int - get() { - val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args -> - val raw = args.toInt() - MyGattCharacteristicPropertyArgs.ofRaw(raw) - } - val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ) - val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE) - val writeWithoutResponse = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE) - var permissions = 0 - if (read) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_READ - if (write || writeWithoutResponse) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_WRITE - return permissions - } - -fun MyGattDescriptorArgs.toDescriptor(): BluetoothGattDescriptor { - val uuid = UUID.fromString(uuidArgs) - val permissions = BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE - return BluetoothGattDescriptor(uuid, permissions) -} - -fun Long.toWriteTypeArgs(): MyGattCharacteristicWriteTypeArgs { - val raw = toInt() - return MyGattCharacteristicWriteTypeArgs.ofRaw(raw) ?: throw IllegalArgumentException() -} - -fun MyGattCharacteristicWriteTypeArgs.toType(): Int { +//region ToArgs +fun Int.toBluetoothLowEnergyStateArgs(): MyBluetoothLowEnergyStateArgs { return when (this) { - MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE + BluetoothAdapter.STATE_ON -> MyBluetoothLowEnergyStateArgs.POWEREDON + else -> MyBluetoothLowEnergyStateArgs.POWEREDOFF } -} \ No newline at end of file +} + +fun SparseArray.toManufacturerSpecificDataArgs(): MyManufacturerSpecificDataArgs? { + var index = 0 + val size = size() + val itemsArgs = mutableListOf() + while (index < size) { + val idArgs = keyAt(index).toLong() + val dataArgs = valueAt(index) + val itemArgs = MyManufacturerSpecificDataArgs(idArgs, dataArgs) + itemsArgs.add(itemArgs) + index++ + } + return itemsArgs.lastOrNull() +} + +fun ScanResult.toAdvertisementArgs(): MyAdvertisementArgs { + val record = scanRecord + return if (record == null) { + val nameArgs = null + val serviceUUIDsArgs = emptyList() + val serviceDataArgs = emptyMap() + val manufacturerSpecificDataArgs = null + MyAdvertisementArgs( + nameArgs, serviceUUIDsArgs, serviceDataArgs, manufacturerSpecificDataArgs + ) + } else { + val nameArgs = record.deviceName + val serviceUUIDsArgs = record.serviceUuids?.map { uuid -> uuid.toString() } ?: emptyList() + val pairs = record.serviceData.map { (uuid, value) -> + val key = uuid.toString() + return@map Pair(key, value) + }.toTypedArray() + val serviceDataArgs = mapOf(*pairs) + val manufacturerSpecificDataArgs = + record.manufacturerSpecificData.toManufacturerSpecificDataArgs() + MyAdvertisementArgs( + nameArgs, serviceUUIDsArgs, serviceDataArgs, manufacturerSpecificDataArgs + ) + } +} + +fun BluetoothDevice.toCentralArgs(): MyCentralArgs { + val addressArgs = address + return MyCentralArgs(addressArgs) +} + +fun BluetoothDevice.toPeripheralArgs(): MyPeripheralArgs { + val addressArgs = address + return MyPeripheralArgs(addressArgs) +} + +fun BluetoothGattService.toArgs(): MyGattServiceArgs { + val hashCodeArgs = hashCode().toLong() + val uuidArgs = this.uuid.toString() + val characteristicsArgs = characteristics.map { it.toArgs() } + return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs) +} + +fun BluetoothGattCharacteristic.toArgs(): MyGattCharacteristicArgs { + val hashCodeArgs = hashCode().toLong() + val uuidArgs = this.uuid.toString() + val propertyNumbersArgs = getPropertyNumbersArgs() + val descriptorsArgs = descriptors.map { it.toArgs() } + return MyGattCharacteristicArgs(hashCodeArgs, uuidArgs, propertyNumbersArgs, descriptorsArgs) +} + +fun BluetoothGattCharacteristic.getPropertyNumbersArgs(): List { + val numbersArgs = mutableListOf() + if (properties and BluetoothGattCharacteristic.PROPERTY_READ != 0) { + val number = MyGattCharacteristicPropertyArgs.READ.raw.toLong() + numbersArgs.add(number) + } + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE != 0) { + val number = MyGattCharacteristicPropertyArgs.WRITE.raw.toLong() + numbersArgs.add(number) + } + if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE != 0) { + val number = MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE.raw.toLong() + numbersArgs.add(number) + } + if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) { + val number = MyGattCharacteristicPropertyArgs.NOTIFY.raw.toLong() + numbersArgs.add(number) + } + if (properties and BluetoothGattCharacteristic.PROPERTY_INDICATE != 0) { + val number = MyGattCharacteristicPropertyArgs.INDICATE.raw.toLong() + numbersArgs.add(number) + } + return numbersArgs +} + +fun BluetoothGattDescriptor.toArgs(): MyGattDescriptorArgs { + val hashCodeArgs = hashCode().toLong() + val uuidArgs = this.uuid.toString() + return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, null) +} + +fun ByteArray.toNotifyStateArgs(): MyGattCharacteristicNotifyStateArgs { + return if (this contentEquals BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) { + MyGattCharacteristicNotifyStateArgs.NOTIFY + } else if (this contentEquals BluetoothGattDescriptor.ENABLE_INDICATION_VALUE) { + MyGattCharacteristicNotifyStateArgs.INDICATE + } else if (this contentEquals BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE) { + MyGattCharacteristicNotifyStateArgs.NONE + } else { + throw IllegalArgumentException() + } +} +//endregion + +val Any.TAG get() = this::class.java.simpleName as String + +//val ScanRecord.rawValues: Map +// get() { +// val rawValues = mutableMapOf() +// var index = 0 +// val size = bytes.size +// while (index < size) { +// val length = bytes[index++].toInt() and 0xff +// if (length == 0) { +// break +// } +// val end = index + length +// val type = bytes[index++] +// val value = bytes.slice(index until end).toByteArray() +// rawValues[type] = value +// index = end +// } +// return rawValues.toMap() +// } diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothGattCallback.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothGattCallback.kt index 0efd94b..49f4e70 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothGattCallback.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothGattCallback.kt @@ -7,103 +7,131 @@ import android.bluetooth.BluetoothGattDescriptor import android.os.Build import java.util.concurrent.Executor -class MyBluetoothGattCallback(private val centralManager: MyCentralManager, private val executor: Executor) : BluetoothGattCallback() { +class MyBluetoothGattCallback(manager: MyCentralManager, executor: Executor) : + BluetoothGattCallback() { + private val mManager: MyCentralManager + private val mExecutor: Executor + + init { + mManager = manager + mExecutor = executor + } + override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { super.onConnectionStateChange(gatt, status, newState) - executor.execute { - centralManager.onConnectionStateChange(gatt, status, newState) + mExecutor.execute { + mManager.onConnectionStateChange(gatt, status, newState) } } override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { super.onMtuChanged(gatt, mtu, status) - executor.execute { - centralManager.onMtuChanged(gatt, mtu, status) + mExecutor.execute { + mManager.onMtuChanged(gatt, mtu, status) } } override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) { super.onReadRemoteRssi(gatt, rssi, status) - executor.execute { - centralManager.onReadRemoteRssi(gatt, rssi, status) + mExecutor.execute { + mManager.onReadRemoteRssi(gatt, rssi, status) } } override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { super.onServicesDiscovered(gatt, status) - executor.execute { - centralManager.onServicesDiscovered(gatt, status) + mExecutor.execute { + mManager.onServicesDiscovered(gatt, status) } } - override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray, status: Int) { + override fun onCharacteristicRead( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + value: ByteArray, + status: Int + ) { super.onCharacteristicRead(gatt, characteristic, value, status) - executor.execute { - centralManager.onCharacteristicRead(characteristic, status, value) + mExecutor.execute { + mManager.onCharacteristicRead(gatt, characteristic, status, value) } } // TODO: remove this override when minSdkVersion >= 33 - override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { + override fun onCharacteristicRead( + gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int + ) { super.onCharacteristicRead(gatt, characteristic, status) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return } val value = characteristic.value - executor.execute { - centralManager.onCharacteristicRead(characteristic, status, value) + mExecutor.execute { + mManager.onCharacteristicRead(gatt, characteristic, status, value) } } - override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { + override fun onCharacteristicWrite( + gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int + ) { super.onCharacteristicWrite(gatt, characteristic, status) - executor.execute { - centralManager.onCharacteristicWrite(characteristic, status) + mExecutor.execute { + mManager.onCharacteristicWrite(gatt, characteristic, status) } } - override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) { + override fun onCharacteristicChanged( + gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray + ) { super.onCharacteristicChanged(gatt, characteristic, value) - executor.execute { - centralManager.onCharacteristicChanged(characteristic, value) + mExecutor.execute { + mManager.onCharacteristicChanged(gatt, characteristic, value) } } // TODO: remove this override when minSdkVersion >= 33 - override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { + override fun onCharacteristicChanged( + gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic + ) { super.onCharacteristicChanged(gatt, characteristic) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return } val value = characteristic.value - executor.execute { - centralManager.onCharacteristicChanged(characteristic, value) + mExecutor.execute { + mManager.onCharacteristicChanged(gatt, characteristic, value) } } - override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) { + override fun onDescriptorRead( + gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray + ) { super.onDescriptorRead(gatt, descriptor, status, value) - executor.execute { - centralManager.onDescriptorRead(descriptor, status, value) + mExecutor.execute { + mManager.onDescriptorRead(gatt, descriptor, status, value) } } // TODO: remove this override when minSdkVersion >= 33 - override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { + override fun onDescriptorRead( + gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int + ) { super.onDescriptorRead(gatt, descriptor, status) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return } val value = descriptor.value - executor.execute { - centralManager.onDescriptorRead(descriptor, status, value) + mExecutor.execute { + mManager.onDescriptorRead(gatt, descriptor, status, value) } } - override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { + override fun onDescriptorWrite( + gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int + ) { super.onDescriptorWrite(gatt, descriptor, status) - executor.execute { - centralManager.onDescriptorWrite(descriptor, status) + mExecutor.execute { + mManager.onDescriptorWrite(gatt, descriptor, status) } } } \ No newline at end of file diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothGattServerCallback.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothGattServerCallback.kt index 2a5cbc1..d66cfe9 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothGattServerCallback.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothGattServerCallback.kt @@ -7,67 +7,107 @@ import android.bluetooth.BluetoothGattServerCallback import android.bluetooth.BluetoothGattService import java.util.concurrent.Executor -class MyBluetoothGattServerCallback(private val peripheralManager: MyPeripheralManager, private val executor: Executor) : BluetoothGattServerCallback() { +class MyBluetoothGattServerCallback(manager: MyPeripheralManager, executor: Executor) : + BluetoothGattServerCallback() { + private val mManager: MyPeripheralManager + private val mExecutor: Executor + + init { + mManager = manager + mExecutor = executor + } + override fun onServiceAdded(status: Int, service: BluetoothGattService) { super.onServiceAdded(status, service) - executor.execute { - peripheralManager.onServiceAdded(status, service) + mExecutor.execute { + mManager.onServiceAdded(status, service) } } override fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) { super.onConnectionStateChange(device, status, newState) - executor.execute { - peripheralManager.onConnectionStateChange(device, status, newState) + mExecutor.execute { + mManager.onConnectionStateChange(device, status, newState) } } override fun onMtuChanged(device: BluetoothDevice, mtu: Int) { super.onMtuChanged(device, mtu) - executor.execute { - peripheralManager.onMtuChanged(device, mtu) + mExecutor.execute { + mManager.onMtuChanged(device, mtu) } } - override fun onCharacteristicReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, characteristic: BluetoothGattCharacteristic) { + override fun onCharacteristicReadRequest( + device: BluetoothDevice, + requestId: Int, + offset: Int, + characteristic: BluetoothGattCharacteristic + ) { super.onCharacteristicReadRequest(device, requestId, offset, characteristic) - executor.execute { - peripheralManager.onCharacteristicReadRequest(device, requestId, offset, characteristic) + mExecutor.execute { + mManager.onCharacteristicReadRequest(device, requestId, offset, characteristic) } } - override fun onCharacteristicWriteRequest(device: BluetoothDevice, requestId: Int, characteristic: BluetoothGattCharacteristic, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) { - super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value) - executor.execute { - peripheralManager.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value) + override fun onCharacteristicWriteRequest( + device: BluetoothDevice, + requestId: Int, + characteristic: BluetoothGattCharacteristic, + preparedWrite: Boolean, + responseNeeded: Boolean, + offset: Int, + value: ByteArray + ) { + super.onCharacteristicWriteRequest( + device, requestId, characteristic, preparedWrite, responseNeeded, offset, value + ) + mExecutor.execute { + mManager.onCharacteristicWriteRequest( + device, requestId, characteristic, preparedWrite, responseNeeded, offset, value + ) } } override fun onExecuteWrite(device: BluetoothDevice, requestId: Int, execute: Boolean) { super.onExecuteWrite(device, requestId, execute) - executor.execute { - peripheralManager.onExecuteWrite(device, requestId, execute) + mExecutor.execute { + mManager.onExecuteWrite(device, requestId, execute) } } override fun onNotificationSent(device: BluetoothDevice, status: Int) { super.onNotificationSent(device, status) - executor.execute { - peripheralManager.onNotificationSent(device, status) + mExecutor.execute { + mManager.onNotificationSent(device, status) } } - override fun onDescriptorReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor) { + override fun onDescriptorReadRequest( + device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor + ) { super.onDescriptorReadRequest(device, requestId, offset, descriptor) - executor.execute { - peripheralManager.onDescriptorReadRequest(device, requestId, offset, descriptor) + mExecutor.execute { + mManager.onDescriptorReadRequest(device, requestId, offset, descriptor) } } - override fun onDescriptorWriteRequest(device: BluetoothDevice, requestId: Int, descriptor: BluetoothGattDescriptor, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) { - super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value) - executor.execute { - peripheralManager.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value) + override fun onDescriptorWriteRequest( + device: BluetoothDevice, + requestId: Int, + descriptor: BluetoothGattDescriptor, + preparedWrite: Boolean, + responseNeeded: Boolean, + offset: Int, + value: ByteArray + ) { + super.onDescriptorWriteRequest( + device, requestId, descriptor, preparedWrite, responseNeeded, offset, value + ) + mExecutor.execute { + mManager.onDescriptorWriteRequest( + device, requestId, descriptor, preparedWrite, responseNeeded, offset, value + ) } } } \ No newline at end of file diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothLowEnergyManager.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothLowEnergyManager.kt index 6148c5c..b0a986c 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothLowEnergyManager.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothLowEnergyManager.kt @@ -2,62 +2,123 @@ package dev.yanshouwang.bluetooth_low_energy_android import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothManager +import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager -import androidx.annotation.CallSuper import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding +import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener import java.util.UUID import java.util.concurrent.Executor -abstract class MyBluetoothLowEnergyManager(private val context: Context) { - companion object { - const val DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xff.toByte() - const val REQUEST_CODE = 443 +typealias MyBluetoothLowEnergyState = MyBluetoothLowEnergyStateArgs - val HEART_RATE_MEASUREMENT_UUID: UUID = UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb") - val CLIENT_CHARACTERISTIC_CONFIG_UUID: UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb") +abstract class MyBluetoothLowEnergyManager(context: Context) { + companion object { + val CLIENT_CHARACTERISTIC_CONFIG_UUID = + UUID.fromString("00002902-0000-1000-8000-00805f9b34fb") as UUID } - private lateinit var binding: ActivityPluginBinding + private val mContext: Context - protected val unsupported = !context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) - protected val executor = ContextCompat.getMainExecutor(context) as Executor + private val mRequestPermissionsResultListener: RequestPermissionsResultListener by lazy { + MyRequestPermissionResultListener(this) + } + private val mBroadcastReceiver: BroadcastReceiver by lazy { + MyBroadcastReceiver(this) + } - protected val manager get() = ContextCompat.getSystemService(context, BluetoothManager::class.java) as BluetoothManager + private var mRegistered: Boolean + private lateinit var mBinding: ActivityPluginBinding + + init { + mContext = context + mRegistered = false + } + + protected val executor get() = ContextCompat.getMainExecutor(mContext) as Executor + protected val manager + get() = ContextCompat.getSystemService( + mContext, BluetoothManager::class.java + ) as BluetoothManager protected val adapter get() = manager.adapter as BluetoothAdapter - private val listener by lazy { MyRequestPermissionResultListener(this) } - private val receiver by lazy { MyBroadcastReceiver(this) } + protected fun initialize() { + val hasFeature = + mContext.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) + if (hasFeature) { + val authorized = permissions.all { permission -> + ActivityCompat.checkSelfPermission( + mContext, permission + ) == PackageManager.PERMISSION_GRANTED + } + if (authorized) { + mOnAuthorizationStateChanged(true) + } else { + val activity = mBinding.activity + ActivityCompat.requestPermissions(activity, permissions, requestCode) + } + } else { + val state = MyBluetoothLowEnergyState.UNSUPPORTED + onStateChanged(state) + } + } fun onAttachedToActivity(binding: ActivityPluginBinding) { - binding.addRequestPermissionsResultListener(listener) - this.binding = binding + binding.addRequestPermissionsResultListener(mRequestPermissionsResultListener) + mBinding = binding } fun onDetachedFromActivity() { - binding.removeRequestPermissionsResultListener(listener) + mBinding.removeRequestPermissionsResultListener(mRequestPermissionsResultListener) } - protected fun authorize(permissions: Array) { - val activity = binding.activity - ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE) + fun onRequestPermissionsResult( + requestCode: Int, permissions: Array, results: IntArray + ): Boolean { + if (this.requestCode != requestCode) { + return false + } + val authorized = results.all { r -> r == PackageManager.PERMISSION_GRANTED } + mOnAuthorizationStateChanged(authorized) + return true } - @CallSuper - protected open fun register() { + fun onReceive(context: Context, intent: Intent) { + val action = intent.action + if (action != BluetoothAdapter.ACTION_STATE_CHANGED) { + return + } + val extra = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) + val state = extra.toBluetoothLowEnergyStateArgs() + onStateChanged(state) + } + + private fun mOnAuthorizationStateChanged(authorized: Boolean) { + val state = if (authorized) { + mRegisterReceiver() + if (adapter.state == BluetoothAdapter.STATE_ON) MyBluetoothLowEnergyState.POWEREDON + else MyBluetoothLowEnergyState.POWEREDOFF + } else { + MyBluetoothLowEnergyState.UNAUTHORIZED + } + onStateChanged(state) + } + + private fun mRegisterReceiver() { + if (mRegistered) { + return + } val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) - context.registerReceiver(receiver, filter) + mContext.registerReceiver(mBroadcastReceiver, filter) + mRegistered = true } - @CallSuper - protected open fun unregister() { - context.unregisterReceiver(receiver) - } + abstract val permissions: Array + abstract val requestCode: Int + abstract fun onStateChanged(state: MyBluetoothLowEnergyState) +} - abstract fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean - abstract fun onReceive(intent: Intent) -} \ No newline at end of file diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBroadcastReceiver.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBroadcastReceiver.kt index 767c269..6895840 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBroadcastReceiver.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBroadcastReceiver.kt @@ -4,8 +4,14 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -class MyBroadcastReceiver(private val bluetoothLowEnergyManager: MyBluetoothLowEnergyManager) : BroadcastReceiver() { +class MyBroadcastReceiver(manager: MyBluetoothLowEnergyManager) : BroadcastReceiver() { + private val mManager: MyBluetoothLowEnergyManager + + init { + mManager = manager + } + override fun onReceive(context: Context, intent: Intent) { - bluetoothLowEnergyManager.onReceive(intent) + mManager.onReceive(context, intent) } } diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyCentralManager.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyCentralManager.kt index 6f56e1b..4c5cbbb 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyCentralManager.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyCentralManager.kt @@ -1,267 +1,224 @@ package dev.yanshouwang.bluetooth_low_energy_android -import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCallback import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattDescriptor -import android.bluetooth.BluetoothGattService import android.bluetooth.BluetoothProfile import android.bluetooth.BluetoothStatusCodes +import android.bluetooth.le.BluetoothLeScanner +import android.bluetooth.le.ScanCallback import android.bluetooth.le.ScanFilter import android.bluetooth.le.ScanResult import android.bluetooth.le.ScanSettings import android.content.Context -import android.content.Intent -import android.content.pm.PackageManager import android.os.Build import io.flutter.plugin.common.BinaryMessenger -class MyCentralManager(private val context: Context, binaryMessenger: BinaryMessenger) : MyBluetoothLowEnergyManager(context), MyCentralManagerHostApi { - private val scanner get() = adapter.bluetoothLeScanner - - private val api = MyCentralManagerFlutterApi(binaryMessenger) - private val scanCallback = MyScanCallback(this) - private val bluetoothGattCallback = MyBluetoothGattCallback(this, executor) - - private val devices = mutableMapOf() - private val bluetoothGATTs = mutableMapOf() - private val services = mutableMapOf() - private val characteristics = mutableMapOf() - private val descriptors = mutableMapOf() - private val mtus = mutableMapOf() - - private val peripheralsArgs = mutableMapOf() - private val servicesArgsOfPeripherals = mutableMapOf>() - private val servicesArgs = mutableMapOf() - private val characteristicsArgs = mutableMapOf() - private val descriptorsArgs = mutableMapOf() - - private var registered = false - private var discovering = false - - private var setUpCallback: ((Result) -> Unit)? = null - private var startDiscoveryCallback: ((Result) -> Unit)? = null - private val connectCallbacks = mutableMapOf) -> Unit>() - private val disconnectCallbacks = mutableMapOf) -> Unit>() - private val requestMtuCallbacks = mutableMapOf) -> Unit>() - private val readRssiCallbacks = mutableMapOf) -> Unit>() - private val discoverGattCallbacks = mutableMapOf>) -> Unit>() - private val readCharacteristicCallbacks = mutableMapOf) -> Unit>() - private val writeCharacteristicCallbacks = mutableMapOf) -> Unit>() - private val readDescriptorCallbacks = mutableMapOf) -> Unit>() - private val writeDescriptorCallbacks = mutableMapOf) -> Unit>() - - override fun setUp(callback: (Result) -> Unit) { - try { - if (setUpCallback != null) { - throw IllegalStateException() - } - tearDown() - if (unsupported) { - val stateNumberArgs = MyBluetoothLowEnergyStateArgs.UNSUPPORTED.raw.toLong() - val args = MyCentralManagerArgs(stateNumberArgs) - callback(Result.success(args)) - } - val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_CONNECT) - } else { - arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION) - } - authorize(permissions) - setUpCallback = callback - } catch (e: Throwable) { - callback(Result.failure(e)) - } +class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : + MyBluetoothLowEnergyManager(context), MyCentralManagerHostApi { + companion object { + const val REQUEST_CODE = 443 } - private fun tearDown() { - if (registered) { - unregister() - } - if (discovering) { - stopDiscovery() - } - for (gatt in bluetoothGATTs.values) { - gatt.disconnect() - } - devices.clear() - bluetoothGATTs.clear() - services.clear() - characteristics.clear() - descriptors.clear() - mtus.clear() - peripheralsArgs.clear() - servicesArgsOfPeripherals.clear() - servicesArgs.clear() - characteristicsArgs.clear() - descriptorsArgs.clear() - setUpCallback = null - startDiscoveryCallback = null - connectCallbacks.clear() - disconnectCallbacks.clear() - requestMtuCallbacks.clear() - readRssiCallbacks.clear() - discoverGattCallbacks.clear() - readCharacteristicCallbacks.clear() - writeCharacteristicCallbacks.clear() - readDescriptorCallbacks.clear() - writeDescriptorCallbacks.clear() + private val mContext: Context + private val mApi: MyCentralManagerFlutterApi + + private val mScanCallback: ScanCallback by lazy { + MyScanCallback(this) + } + private val mBluetoothGattCallback: BluetoothGattCallback by lazy { + MyBluetoothGattCallback(this, executor) } - override fun register() { - super.register() - registered = true + private var mDiscovering: Boolean + + private val mDevices: MutableMap + private val mGATTs: MutableMap + private val mCharacteristics: MutableMap> + private val mDescriptors: MutableMap> + + private var mStartDiscoveryCallback: ((Result) -> Unit)? + private val mConnectCallbacks: MutableMap) -> Unit> + private val mDisconnectCallbacks: MutableMap) -> Unit> + private val mRequestMtuCallbacks: MutableMap) -> Unit> + private val mReadRssiCallbacks: MutableMap) -> Unit> + private val mDiscoverServicesCallbacks: MutableMap>) -> Unit> + private val mReadCharacteristicCallbacks: MutableMap) -> Unit>> + private val mWriteCharacteristicCallbacks: MutableMap) -> Unit>> + private val mReadDescriptorCallbacks: MutableMap) -> Unit>> + private val mWriteDescriptorCallbacks: MutableMap) -> Unit>> + + init { + mContext = context + mApi = MyCentralManagerFlutterApi(binaryMessenger) + + mDiscovering = false + + mDevices = mutableMapOf() + mGATTs = mutableMapOf() + mCharacteristics = mutableMapOf() + mDescriptors = mutableMapOf() + + mStartDiscoveryCallback = null + mConnectCallbacks = mutableMapOf() + mDisconnectCallbacks = mutableMapOf() + mRequestMtuCallbacks = mutableMapOf() + mReadRssiCallbacks = mutableMapOf() + mDiscoverServicesCallbacks = mutableMapOf() + mReadCharacteristicCallbacks = mutableMapOf() + mWriteCharacteristicCallbacks = mutableMapOf() + mReadDescriptorCallbacks = mutableMapOf() + mWriteDescriptorCallbacks = mutableMapOf() } - override fun unregister() { - super.unregister() - registered = false + private val mScanner: BluetoothLeScanner get() = adapter.bluetoothLeScanner + + override val permissions: Array + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + arrayOf( + android.Manifest.permission.ACCESS_COARSE_LOCATION, + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.BLUETOOTH_SCAN, + android.Manifest.permission.BLUETOOTH_CONNECT + ) + } else { + arrayOf( + android.Manifest.permission.ACCESS_COARSE_LOCATION, + android.Manifest.permission.ACCESS_FINE_LOCATION + ) + } + + override val requestCode: Int + get() = REQUEST_CODE + + override fun setUp() { + mClearState() + initialize() } override fun startDiscovery(callback: (Result) -> Unit) { try { - if (startDiscoveryCallback != null) { - throw IllegalStateException() - } val filters = emptyList() - val settings = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build() - scanner.startScan(filters, settings, scanCallback) - executor.execute { onScanSucceed() } - startDiscoveryCallback = callback + val settings = + ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build() + mScanner.startScan(filters, settings, mScanCallback) + mStartDiscoveryCallback = callback + executor.execute { + onScanSucceed() + } } catch (e: Throwable) { callback(Result.failure(e)) } } override fun stopDiscovery() { - scanner.stopScan(scanCallback) - discovering = false + mScanner.stopScan(mScanCallback) + mDiscovering = false } - override fun connect(peripheralHashCodeArgs: Long, callback: (Result) -> Unit) { + override fun connect(addressArgs: String, callback: (Result) -> Unit) { try { - val unfinishedCallback = connectCallbacks[peripheralHashCodeArgs] - if (unfinishedCallback != null) { - throw IllegalStateException() - } - val device = devices[peripheralHashCodeArgs] as BluetoothDevice + val device = mDevices[addressArgs] as BluetoothDevice val autoConnect = false // Add to bluetoothGATTs cache. - bluetoothGATTs[peripheralHashCodeArgs] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + mGATTs[addressArgs] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val transport = BluetoothDevice.TRANSPORT_LE - device.connectGatt(context, autoConnect, bluetoothGattCallback, transport) + device.connectGatt(mContext, autoConnect, mBluetoothGattCallback, transport) } else { - device.connectGatt(context, autoConnect, bluetoothGattCallback) + device.connectGatt(mContext, autoConnect, mBluetoothGattCallback) } - connectCallbacks[peripheralHashCodeArgs] = callback + mConnectCallbacks[addressArgs] = callback } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun disconnect(peripheralHashCodeArgs: Long, callback: (Result) -> Unit) { + override fun disconnect(addressArgs: String, callback: (Result) -> Unit) { try { - val unfinishedCallback = disconnectCallbacks[peripheralHashCodeArgs] - if (unfinishedCallback != null) { - throw IllegalStateException() - } - val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt + val gatt = mGATTs[addressArgs] as BluetoothGatt gatt.disconnect() - disconnectCallbacks[peripheralHashCodeArgs] = callback + mDisconnectCallbacks[addressArgs] = callback } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun requestMTU(peripheralHashCodeArgs: Long, mtuArgs: Long, callback: (Result) -> Unit) { + override fun requestMTU(addressArgs: String, mtuArgs: Long, callback: (Result) -> Unit) { try { - val unfinishedCallback = requestMtuCallbacks[peripheralHashCodeArgs] - if (unfinishedCallback != null) { - throw IllegalStateException() - } - val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt + val gatt = mGATTs[addressArgs] as BluetoothGatt val mtu = mtuArgs.toInt() val requesting = gatt.requestMtu(mtu) if (!requesting) { throw IllegalStateException() } - requestMtuCallbacks[peripheralHashCodeArgs] = callback + mRequestMtuCallbacks[addressArgs] = callback } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun getMaximumWriteLength(peripheralHashCodeArgs: Long, typeNumberArgs: Long): Long { - val mtu = mtus[peripheralHashCodeArgs] ?: 23 - val maximumWriteLength = when (typeNumberArgs.toWriteTypeArgs()) { - MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> 512 - MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> (mtu - 3).coerceIn(20, 512) - } - return maximumWriteLength.toLong() - } - - override fun readRSSI(peripheralHashCodeArgs: Long, callback: (Result) -> Unit) { + override fun readRSSI(addressArgs: String, callback: (Result) -> Unit) { try { - val unfinishedCallback = readRssiCallbacks[peripheralHashCodeArgs] - if (unfinishedCallback != null) { - throw IllegalStateException() - } - val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt + val gatt = mGATTs[addressArgs] as BluetoothGatt val reading = gatt.readRemoteRssi() if (!reading) { throw IllegalStateException() } - readRssiCallbacks[peripheralHashCodeArgs] = callback + mReadRssiCallbacks[addressArgs] = callback } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun discoverGATT(peripheralHashCodeArgs: Long, callback: (Result>) -> Unit) { + override fun discoverServices( + addressArgs: String, callback: (Result>) -> Unit + ) { try { - val unfinishedCallback = discoverGattCallbacks[peripheralHashCodeArgs] - if (unfinishedCallback != null) { - throw IllegalStateException() - } - val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt + val gatt = mGATTs[addressArgs] as BluetoothGatt val discovering = gatt.discoverServices() if (!discovering) { throw IllegalStateException() } - discoverGattCallbacks[peripheralHashCodeArgs] = callback + mDiscoverServicesCallbacks[addressArgs] = callback } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun readCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, callback: (Result) -> Unit) { + override fun readCharacteristic( + addressArgs: String, hashCodeArgs: Long, callback: (Result) -> Unit + ) { try { - val unfinishedCallback = readCharacteristicCallbacks[characteristicHashCodeArgs] - if (unfinishedCallback != null) { - throw IllegalStateException() - } - val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt - val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic + val gatt = mGATTs[addressArgs] as BluetoothGatt + val characteristic = + mRetrieveCharacteristic(addressArgs, hashCodeArgs) as BluetoothGattCharacteristic val reading = gatt.readCharacteristic(characteristic) if (!reading) { throw IllegalStateException() } - readCharacteristicCallbacks[characteristicHashCodeArgs] = callback + val callbacks = mReadCharacteristicCallbacks.getOrPut(addressArgs) { mutableMapOf() } + callbacks[hashCodeArgs] = callback } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun writeCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, typeNumberArgs: Long, callback: (Result) -> Unit) { + override fun writeCharacteristic( + addressArgs: String, + hashCodeArgs: Long, + valueArgs: ByteArray, + typeNumberArgs: Long, + callback: (Result) -> Unit + ) { try { - val unfinishedCallback = writeCharacteristicCallbacks[characteristicHashCodeArgs] - if (unfinishedCallback != null) { - throw IllegalStateException() - } - val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt - val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic - val typeArgs = typeNumberArgs.toWriteTypeArgs() + val gatt = mGATTs[addressArgs] as BluetoothGatt + val characteristic = + mRetrieveCharacteristic(addressArgs, hashCodeArgs) as BluetoothGattCharacteristic + val typeRawArgs = typeNumberArgs.toInt() + val typeArgs = MyGattCharacteristicWriteTypeArgs.ofRaw(typeRawArgs) + ?: throw IllegalArgumentException() val type = typeArgs.toType() val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val code = gatt.writeCharacteristic(characteristic, valueArgs, type) @@ -275,80 +232,75 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess if (!writing) { throw IllegalStateException() } - writeCharacteristicCallbacks[characteristicHashCodeArgs] = callback + val callbacks = mWriteCharacteristicCallbacks.getOrPut(addressArgs) { mutableMapOf() } + callbacks[hashCodeArgs] = callback } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun notifyCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, stateArgs: Boolean, callback: (Result) -> Unit) { + override fun setCharacteristicNotifyState( + addressArgs: String, + hashCodeArgs: Long, + stateNumberArgs: Long, + callback: (Result) -> Unit + ) { try { - val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt - val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic - val notifying = gatt.setCharacteristicNotification(characteristic, stateArgs) + val gatt = mGATTs[addressArgs] as BluetoothGatt + val characteristic = + mRetrieveCharacteristic(addressArgs, hashCodeArgs) as BluetoothGattCharacteristic + val stateRawArgs = stateNumberArgs.toInt() + val stateArgs = MyGattCharacteristicNotifyStateArgs.ofRaw(stateRawArgs) + ?: throw IllegalArgumentException() + val enable = stateArgs != MyGattCharacteristicNotifyStateArgs.NONE + val notifying = gatt.setCharacteristicNotification(characteristic, enable) if (!notifying) { throw IllegalStateException() } // TODO: Seems the docs is not correct, this operation is necessary for all characteristics. // https://developer.android.com/guide/topics/connectivity/bluetooth/transfer-ble-data#notification // This is specific to Heart Rate Measurement. -// if (characteristic.uuid == UUID_HEART_RATE_MEASUREMENT) { - val descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID) - val descriptorHashCode = descriptor.hashCode() - val descriptorArgs = descriptorsArgs[descriptorHashCode] as MyGattDescriptorArgs - val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - val unfinishedCallback = writeDescriptorCallbacks[descriptorHashCodeArgs] - if (unfinishedCallback != null) { - throw IllegalStateException() - } - val value = if (stateArgs) BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE - else BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE - val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - val code = gatt.writeDescriptor(descriptor, value) - code == BluetoothStatusCodes.SUCCESS - } else { - // TODO: remove this when minSdkVersion >= 33 - descriptor.value = value - gatt.writeDescriptor(descriptor) - } - if (!writing) { - throw IllegalStateException() - } - writeDescriptorCallbacks[descriptorHashCodeArgs] = callback -// } else { -// callback(Result.success(Unit)) -// } + //if (characteristic.uuid == UUID_HEART_RATE_MEASUREMENT) { + val cccDescriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID) + val cccHashCodeArgs = cccDescriptor.hashCode().toLong() + val valueArgs = stateArgs.toValue() + writeDescriptor(addressArgs, cccHashCodeArgs, valueArgs, callback) + //} else { + // callback(Result.success(Unit)) + //} } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun readDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, callback: (Result) -> Unit) { + override fun readDescriptor( + addressArgs: String, hashCodeArgs: Long, callback: (Result) -> Unit + ) { try { - val unfinishedCallback = readDescriptorCallbacks[descriptorHashCodeArgs] - if (unfinishedCallback != null) { - throw IllegalStateException() - } - val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt - val descriptor = descriptors[descriptorHashCodeArgs] as BluetoothGattDescriptor + val gatt = mGATTs[addressArgs] as BluetoothGatt + val descriptor = + mRetrieveDescriptor(addressArgs, hashCodeArgs) as BluetoothGattDescriptor val reading = gatt.readDescriptor(descriptor) if (!reading) { throw IllegalStateException() } - readDescriptorCallbacks[descriptorHashCodeArgs] = callback + val callbacks = mReadDescriptorCallbacks.getOrPut(addressArgs) { mutableMapOf() } + callbacks[hashCodeArgs] = callback } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun writeDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result) -> Unit) { + override fun writeDescriptor( + addressArgs: String, + hashCodeArgs: Long, + valueArgs: ByteArray, + callback: (Result) -> Unit + ) { try { - val unfinishedCallback = writeDescriptorCallbacks[descriptorHashCodeArgs] - if (unfinishedCallback != null) { - throw IllegalStateException() - } - val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt - val descriptor = descriptors[descriptorHashCodeArgs] as BluetoothGattDescriptor + val gatt = mGATTs[addressArgs] as BluetoothGatt + val descriptor = + mRetrieveDescriptor(addressArgs, hashCodeArgs) as BluetoothGattDescriptor val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val code = gatt.writeDescriptor(descriptor, valueArgs) code == BluetoothStatusCodes.SUCCESS @@ -360,51 +312,28 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess if (!writing) { throw IllegalStateException() } - writeDescriptorCallbacks[descriptorHashCodeArgs] = callback + val callbacks = mWriteDescriptorCallbacks.getOrPut(addressArgs) { mutableMapOf() } + callbacks[hashCodeArgs] = callback } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean { - if (requestCode != REQUEST_CODE) { - return false - } - val authorized = results.all { r -> r == PackageManager.PERMISSION_GRANTED } - val callback = setUpCallback ?: return false - setUpCallback = null - val stateArgs = if (authorized) adapter.stateArgs - else MyBluetoothLowEnergyStateArgs.UNAUTHORIZED - val stateNumberArgs = stateArgs.raw.toLong() - val args = MyCentralManagerArgs(stateNumberArgs) - callback(Result.success(args)) - if (authorized) { - register() - } - return true - } - - override fun onReceive(intent: Intent) { - val action = intent.action - if (action != BluetoothAdapter.ACTION_STATE_CHANGED) { - return - } - val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) - val stateArgs = state.toBluetoothLowEnergyStateArgs() - val stateNumberArgs = stateArgs.raw.toLong() - api.onStateChanged(stateNumberArgs) {} + override fun onStateChanged(state: MyBluetoothLowEnergyState) { + val stateNumberArgs = state.raw.toLong() + mApi.onStateChanged(stateNumberArgs) {} } private fun onScanSucceed() { - discovering = true - val callback = startDiscoveryCallback ?: return - startDiscoveryCallback = null + mDiscovering = true + val callback = mStartDiscoveryCallback ?: return + mStartDiscoveryCallback = null callback(Result.success(Unit)) } fun onScanFailed(errorCode: Int) { - val callback = startDiscoveryCallback ?: return - startDiscoveryCallback = null + val callback = mStartDiscoveryCallback ?: return + mStartDiscoveryCallback = null val error = IllegalStateException("Start discovery failed with error code: $errorCode") callback(Result.failure(error)) } @@ -412,82 +341,68 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess fun onScanResult(result: ScanResult) { val device = result.device val peripheralArgs = device.toPeripheralArgs() - val hashCode = device.hashCode() - val hashCodeArgs = peripheralArgs.hashCodeArgs - this.devices[hashCodeArgs] = device - this.peripheralsArgs[hashCode] = peripheralArgs + val addressArgs = peripheralArgs.addressArgs val rssiArgs = result.rssi.toLong() - val advertisementArgs = result.advertisementArgs - api.onDiscovered(peripheralArgs, rssiArgs, advertisementArgs) {} + val advertisementArgs = result.toAdvertisementArgs() + mDevices[addressArgs] = device + mApi.onDiscovered(peripheralArgs, rssiArgs, advertisementArgs) {} } fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { val device = gatt.device - val deviceHashCode = device.hashCode() - val peripheralArgs = peripheralsArgs[deviceHashCode] as MyPeripheralArgs - val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs + val addressArgs = device.address // check connection state. if (newState == BluetoothProfile.STATE_DISCONNECTED) { gatt.close() - bluetoothGATTs.remove(peripheralHashCodeArgs) - mtus.remove(peripheralHashCodeArgs) + mGATTs.remove(addressArgs) + mCharacteristics.remove(addressArgs) + mDescriptors.remove(addressArgs) val error = IllegalStateException("GATT is disconnected with status: $status") - val requestMtuCallback = requestMtuCallbacks.remove(peripheralHashCodeArgs) + val requestMtuCallback = mRequestMtuCallbacks.remove(addressArgs) if (requestMtuCallback != null) { requestMtuCallback(Result.failure(error)) } - val readRssiCallback = readRssiCallbacks.remove(peripheralHashCodeArgs) + val readRssiCallback = mReadRssiCallbacks.remove(addressArgs) if (readRssiCallback != null) { readRssiCallback(Result.failure(error)) } - val discoverGattCallback = discoverGattCallbacks.remove(peripheralHashCodeArgs) - if (discoverGattCallback != null) { - discoverGattCallback(Result.failure(error)) + val discoverServicesCallback = mDiscoverServicesCallbacks.remove(addressArgs) + if (discoverServicesCallback != null) { + discoverServicesCallback(Result.failure(error)) } - val servicesArgs = servicesArgsOfPeripherals.remove(peripheralHashCodeArgs) - ?: emptyList() - for (serviceArgs in servicesArgs) { - val serviceHashCodeArgs = serviceArgs.hashCodeArgs - val service = services.remove(serviceHashCodeArgs) as BluetoothGattService - val serviceHashCode = service.hashCode() - this.servicesArgs.remove(serviceHashCode) - val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull() - for (characteristicArgs in characteristicsArgs) { - val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - val characteristic = characteristics.remove(characteristicHashCodeArgs) as BluetoothGattCharacteristic - val characteristicHashCode = characteristic.hashCode() - this.characteristicsArgs.remove(characteristicHashCode) - val readCharacteristicCallback = readCharacteristicCallbacks.remove(characteristicHashCodeArgs) - val writeCharacteristicCallback = writeCharacteristicCallbacks.remove(characteristicHashCodeArgs) - if (readCharacteristicCallback != null) { - readCharacteristicCallback(Result.failure(error)) - } - if (writeCharacteristicCallback != null) { - writeCharacteristicCallback(Result.failure(error)) - } - val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull() - for (descriptorArgs in descriptorsArgs) { - val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - val descriptor = descriptors.remove(descriptorHashCodeArgs) as BluetoothGattDescriptor - val descriptorHashCode = descriptor.hashCode() - this.descriptorsArgs.remove(descriptorHashCode) - val readDescriptorCallback = readDescriptorCallbacks.remove(descriptorHashCodeArgs) - val writeDescriptorCallback = writeDescriptorCallbacks.remove(descriptorHashCodeArgs) - if (readDescriptorCallback != null) { - readDescriptorCallback(Result.failure(error)) - } - if (writeDescriptorCallback != null) { - writeDescriptorCallback(Result.failure(error)) - } - } + val readCharacteristicCallbacks = mReadCharacteristicCallbacks.remove(addressArgs) + if (readCharacteristicCallbacks != null) { + val callbacks = readCharacteristicCallbacks.values + for (callback in callbacks) { + callback(Result.failure(error)) + } + } + val writeCharacteristicCallbacks = mWriteCharacteristicCallbacks.remove(addressArgs) + if (writeCharacteristicCallbacks != null) { + val callbacks = writeCharacteristicCallbacks.values + for (callback in callbacks) { + callback(Result.failure(error)) + } + } + val readDescriptorCallbacks = mReadDescriptorCallbacks.remove(addressArgs) + if (readDescriptorCallbacks != null) { + val callbacks = readDescriptorCallbacks.values + for (callback in callbacks) { + callback(Result.failure(error)) + } + } + val writeDescriptorCallbacks = mWriteDescriptorCallbacks.remove(addressArgs) + if (writeDescriptorCallbacks != null) { + val callbacks = writeDescriptorCallbacks.values + for (callback in callbacks) { + callback(Result.failure(error)) } } } val stateArgs = newState == BluetoothProfile.STATE_CONNECTED - api.onPeripheralStateChanged(peripheralArgs, stateArgs) {} + mApi.onConnectionStateChanged(addressArgs, stateArgs) {} // check connect & disconnect callbacks. - val connectCallback = connectCallbacks.remove(peripheralHashCodeArgs) - val disconnectCallback = disconnectCallbacks.remove(peripheralHashCodeArgs) + val connectCallback = mConnectCallbacks.remove(addressArgs) if (connectCallback != null) { if (status == BluetoothGatt.GATT_SUCCESS) { connectCallback(Result.success(Unit)) @@ -496,6 +411,7 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess connectCallback(Result.failure(error)) } } + val disconnectCallback = mDisconnectCallbacks.remove(addressArgs) if (disconnectCallback != null) { if (status == BluetoothGatt.GATT_SUCCESS) { disconnectCallback(Result.success(Unit)) @@ -508,75 +424,43 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { val device = gatt.device - val hashCode = device.hashCode() - val peripheralArgs = peripheralsArgs[hashCode] as MyPeripheralArgs - val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs - if (status == BluetoothGatt.GATT_SUCCESS) { - mtus[peripheralHashCodeArgs] = mtu - } - val callback = requestMtuCallbacks.remove(peripheralHashCodeArgs) ?: return - if (status == BluetoothGatt.GATT_SUCCESS) { + val addressArgs = device.address + val result = if (status == BluetoothGatt.GATT_SUCCESS) { val mtuArgs = mtu.toLong() - callback(Result.success(mtuArgs)) + mApi.onMtuChanged(addressArgs, mtuArgs) {} + Result.success(mtuArgs) } else { - val error = IllegalStateException("Request MTU failed with status: $status") - callback(Result.failure(error)) + val error = IllegalStateException("Read RSSI failed with status: $status") + Result.failure(error) } + val callback = mRequestMtuCallbacks.remove(addressArgs) ?: return + callback(result) } fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) { val device = gatt.device - val hashCode = device.hashCode() - val peripheralArgs = peripheralsArgs[hashCode] as MyPeripheralArgs - val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs - val callback = readRssiCallbacks.remove(peripheralHashCodeArgs) ?: return + val addressArgs = device.address + val callback = mReadRssiCallbacks.remove(addressArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { val rssiArgs = rssi.toLong() callback(Result.success(rssiArgs)) } else { - val error = IllegalStateException("Read rssi failed with status: $status") + val error = IllegalStateException("Read RSSI failed with status: $status") callback(Result.failure(error)) } } fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { val device = gatt.device - val deviceHashCode = device.hashCode() - val peripheralArgs = peripheralsArgs[deviceHashCode] as MyPeripheralArgs - val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs - val callback = discoverGattCallbacks.remove(peripheralHashCodeArgs) ?: return + val addressArgs = device.address + val callback = mDiscoverServicesCallbacks.remove(addressArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { val services = gatt.services - val servicesArgs = mutableListOf() - for (service in services) { - val characteristics = service.characteristics - val characteristicsArgs = mutableListOf() - for (characteristic in characteristics) { - val descriptors = characteristic.descriptors - val descriptorsArgs = mutableListOf() - for (descriptor in descriptors) { - val descriptorArgs = descriptor.toManufacturerSpecificDataArgs() - val descriptorHashCode = descriptor.hashCode() - val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - this.descriptors[descriptorHashCodeArgs] = descriptor - this.descriptorsArgs[descriptorHashCode] = descriptorArgs - descriptorsArgs.add(descriptorArgs) - } - val characteristicArgs = characteristic.toManufacturerSpecificDataArgs(descriptorsArgs) - val characteristicHashCode = characteristic.hashCode() - val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - this.characteristics[characteristicHashCodeArgs] = characteristic - this.characteristicsArgs[characteristicHashCode] = characteristicArgs - characteristicsArgs.add(characteristicArgs) - } - val serviceArgs = service.toManufacturerSpecificDataArgs(characteristicsArgs) - val serviceHashCode = service.hashCode() - val serviceHashCodeArgs = serviceArgs.hashCodeArgs - this.services[serviceHashCodeArgs] = service - this.servicesArgs[serviceHashCode] = serviceArgs - servicesArgs.add(serviceArgs) - } - servicesArgsOfPeripherals[peripheralHashCodeArgs] = servicesArgs + val characteristics = services.flatMap { it.characteristics } + val descriptors = characteristics.flatMap { it.descriptors } + mCharacteristics[addressArgs] = characteristics.associateBy { it.hashCode().toLong() } + mDescriptors[addressArgs] = descriptors.associateBy { it.hashCode().toLong() } + val servicesArgs = services.map { it.toArgs() } callback(Result.success(servicesArgs)) } else { val error = IllegalStateException("Discover GATT failed with status: $status") @@ -584,11 +468,17 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess } } - fun onCharacteristicRead(characteristic: BluetoothGattCharacteristic, status: Int, value: ByteArray) { - val hashCode = characteristic.hashCode() - val characteristicArgs = characteristicsArgs[hashCode] as MyGattCharacteristicArgs - val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - val callback = readCharacteristicCallbacks.remove(characteristicHashCodeArgs) ?: return + fun onCharacteristicRead( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int, + value: ByteArray + ) { + val device = gatt.device + val addressArgs = device.address + val hashCodeArgs = characteristic.hashCode().toLong() + val callbacks = mReadCharacteristicCallbacks[addressArgs] ?: return + val callback = callbacks.remove(hashCodeArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { callback(Result.success(value)) } else { @@ -597,11 +487,14 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess } } - fun onCharacteristicWrite(characteristic: BluetoothGattCharacteristic, status: Int) { - val hashCode = characteristic.hashCode() - val characteristicArgs = characteristicsArgs[hashCode] as MyGattCharacteristicArgs - val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - val callback = writeCharacteristicCallbacks.remove(characteristicHashCodeArgs) ?: return + fun onCharacteristicWrite( + gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int + ) { + val device = gatt.device + val addressArgs = device.address + val hashCodeArgs = characteristic.hashCode().toLong() + val callbacks = mWriteCharacteristicCallbacks[addressArgs] ?: return + val callback = callbacks.remove(hashCodeArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { callback(Result.success(Unit)) } else { @@ -610,17 +503,23 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess } } - fun onCharacteristicChanged(characteristic: BluetoothGattCharacteristic, value: ByteArray) { - val hashCode = characteristic.hashCode() - val characteristicArgs = characteristicsArgs[hashCode] as MyGattCharacteristicArgs - api.onCharacteristicValueChanged(characteristicArgs, value) {} + fun onCharacteristicChanged( + gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray + ) { + val device = gatt.device + val addressArgs = device.address + val hashCodeArgs = characteristic.hashCode().toLong() + mApi.onCharacteristicNotified(addressArgs, hashCodeArgs, value) {} } - fun onDescriptorRead(descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) { - val hashCode = descriptor.hashCode() - val descriptorArgs = descriptorsArgs[hashCode] as MyGattDescriptorArgs - val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - val callback = readDescriptorCallbacks.remove(descriptorHashCodeArgs) ?: return + fun onDescriptorRead( + gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray + ) { + val device = gatt.device + val addressArgs = device.address + val hashCodeArgs = descriptor.hashCode().toLong() + val callbacks = mReadDescriptorCallbacks[addressArgs] ?: return + val callback = callbacks.remove(hashCodeArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { callback(Result.success(value)) } else { @@ -629,11 +528,12 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess } } - fun onDescriptorWrite(descriptor: BluetoothGattDescriptor, status: Int) { - val hashCode = descriptor.hashCode() - val descriptorArgs = descriptorsArgs[hashCode] as MyGattDescriptorArgs - val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - val callback = writeDescriptorCallbacks.remove(descriptorHashCodeArgs) ?: return + fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { + val device = gatt.device + val addressArgs = device.address + val hashCodeArgs = descriptor.hashCode().toLong() + val callbacks = mWriteDescriptorCallbacks[addressArgs] ?: return + val callback = callbacks.remove(hashCodeArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { callback(Result.success(Unit)) } else { @@ -641,4 +541,42 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess callback(Result.failure(error)) } } + + private fun mClearState() { + if (mDiscovering) { + stopDiscovery() + } + for (gatt in mGATTs.values) { + gatt.disconnect() + } + mDevices.clear() + mGATTs.clear() + mCharacteristics.clear() + mDescriptors.clear() + + mStartDiscoveryCallback = null + mConnectCallbacks.clear() + mDisconnectCallbacks.clear() + mRequestMtuCallbacks.clear() + mReadRssiCallbacks.clear() + mDiscoverServicesCallbacks.clear() + mReadCharacteristicCallbacks.clear() + mWriteCharacteristicCallbacks.clear() + mReadDescriptorCallbacks.clear() + mWriteDescriptorCallbacks.clear() + } + + private fun mRetrieveCharacteristic( + addressArgs: String, hashCodeArgs: Long + ): BluetoothGattCharacteristic? { + val characteristics = mCharacteristics[addressArgs] ?: return null + return characteristics[hashCodeArgs] + } + + private fun mRetrieveDescriptor( + addressArgs: String, hashCodeArgs: Long + ): BluetoothGattDescriptor? { + val descriptors = mDescriptors[addressArgs] ?: return null + return descriptors[hashCodeArgs] + } } diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyPeripheralManager.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyPeripheralManager.kt index 9dee30d..45a63c9 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyPeripheralManager.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyPeripheralManager.kt @@ -1,123 +1,109 @@ package dev.yanshouwang.bluetooth_low_energy_android -import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattDescriptor import android.bluetooth.BluetoothGattServer +import android.bluetooth.BluetoothGattServerCallback import android.bluetooth.BluetoothGattService +import android.bluetooth.BluetoothProfile import android.bluetooth.BluetoothStatusCodes +import android.bluetooth.le.AdvertiseCallback import android.bluetooth.le.AdvertiseSettings import android.content.Context -import android.content.Intent -import android.content.pm.PackageManager import android.os.Build import android.util.Log import io.flutter.plugin.common.BinaryMessenger -class MyPeripheralManager(private val context: Context, binaryMessenger: BinaryMessenger) : MyBluetoothLowEnergyManager(context), MyPeripheralManagerHostApi { +class MyPeripheralManager(context: Context, binaryMessenger: BinaryMessenger) : + MyBluetoothLowEnergyManager(context), MyPeripheralManagerHostApi { + companion object { + const val REQUEST_CODE = 444 + } + private val advertiser get() = adapter.bluetoothLeAdvertiser - private val api = MyPeripheralManagerFlutterApi(binaryMessenger) - private val bluetoothGattServerCallback = MyBluetoothGattServerCallback(this, executor) - private val advertiseCallback = MyAdvertiseCallback(this) + private val mContext: Context + private val mApi: MyPeripheralManagerFlutterApi - private val devices = mutableMapOf() - private val services = mutableMapOf() - private val characteristics = mutableMapOf() - private val descriptors = mutableMapOf() - private val mtus = mutableMapOf() - private val confirms = mutableMapOf() - private val preparedCharacteristics = mutableMapOf() - private val preparedValues = mutableMapOf() - private val values = mutableMapOf() - - private val centralsArgs = mutableMapOf() - private val servicesArgs = mutableMapOf() - private val characteristicsArgs = mutableMapOf() - private val descriptorsArgs = mutableMapOf() - - private lateinit var server: BluetoothGattServer - private var registered = false - private var advertising = false - - private var setUpCallback: ((Result) -> Unit)? = null - private var addServiceCallback: ((Result) -> Unit)? = null - private var startAdvertisingCallback: ((Result) -> Unit)? = null - private val notifyCharacteristicValueChangedCallbacks = mutableMapOf) -> Unit>() - - override fun setUp(callback: (Result) -> Unit) { - try { - val unfinishedCallback = setUpCallback - if (unfinishedCallback != null) { - throw IllegalStateException() - } - tearDown() - if (unsupported) { - val stateNumberArgs = MyBluetoothLowEnergyStateArgs.UNSUPPORTED.raw.toLong() - val args = MyPeripheralManagerArgs(stateNumberArgs) - callback(Result.success(args)) - } - val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT) - } else { - arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION) - } - authorize(permissions) - setUpCallback = callback - } catch (e: Throwable) { - callback(Result.failure(e)) - } + private val bluetoothGattServerCallback: BluetoothGattServerCallback by lazy { + MyBluetoothGattServerCallback(this, executor) + } + private val advertiseCallback: AdvertiseCallback by lazy { + MyAdvertiseCallback(this) } - private fun tearDown() { - if (registered) { - unregister() - } - if (advertising) { - stopAdvertising() - } - devices.clear() - services.clear() - characteristics.clear() - descriptors.clear() - mtus.clear() - confirms.clear() - preparedCharacteristics.clear() - preparedValues.clear() - values.clear() - centralsArgs.clear() - servicesArgs.clear() - characteristicsArgs.clear() - descriptorsArgs.clear() - setUpCallback = null - addServiceCallback = null - startAdvertisingCallback = null - notifyCharacteristicValueChangedCallbacks.clear() + private lateinit var mServer: BluetoothGattServer + private var mOpening = false + private var mAdvertising = false + + private val mServicesArgs: MutableMap + private val mCharacteristicsArgs: MutableMap + private val mDescriptorsArgs: MutableMap + + private val mDevices: MutableMap + private val mServices: MutableMap + private val mCharacteristics: MutableMap + private val mDescriptors: MutableMap + + private var mSetUpCallback: ((Result) -> Unit)? + private var mAddServiceCallback: ((Result) -> Unit)? + private var mStartAdvertisingCallback: ((Result) -> Unit)? + private val mNotifyCharacteristicValueChangedCallbacks: MutableMap) -> Unit> + + init { + mContext = context + mApi = MyPeripheralManagerFlutterApi(binaryMessenger) + + mServicesArgs = mutableMapOf() + mCharacteristicsArgs = mutableMapOf() + mDescriptorsArgs = mutableMapOf() + + mDevices = mutableMapOf() + mServices = mutableMapOf() + mCharacteristics = mutableMapOf() + mDescriptors = mutableMapOf() + + mSetUpCallback = null + mAddServiceCallback = null + mStartAdvertisingCallback = null + mNotifyCharacteristicValueChangedCallbacks = mutableMapOf() } - override fun register() { - super.register() - registered = true - } + override val permissions: Array + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + arrayOf( + android.Manifest.permission.ACCESS_COARSE_LOCATION, + android.Manifest.permission.ACCESS_FINE_LOCATION, + android.Manifest.permission.BLUETOOTH_ADVERTISE, + android.Manifest.permission.BLUETOOTH_CONNECT + ) + } else { + arrayOf( + android.Manifest.permission.ACCESS_COARSE_LOCATION, + android.Manifest.permission.ACCESS_FINE_LOCATION + ) + } - override fun unregister() { - super.unregister() - registered = false + override val requestCode: Int + get() = REQUEST_CODE + + override fun setUp() { + mClearState() + initialize() } override fun addService(serviceArgs: MyGattServiceArgs, callback: (Result) -> Unit) { try { - val unfinishedCallback = addServiceCallback - if (unfinishedCallback != null) { - throw IllegalStateException() - } val service = serviceArgs.toService() val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull() for (characteristicArgs in characteristicsArgs) { val characteristic = characteristicArgs.toCharacteristic() - val cccDescriptor = BluetoothGattDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID, BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE) + val cccDescriptor = BluetoothGattDescriptor( + CLIENT_CHARACTERISTIC_CONFIG_UUID, + BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE + ) val cccDescriptorAdded = characteristic.addDescriptor(cccDescriptor) if (!cccDescriptorAdded) { throw IllegalStateException() @@ -129,89 +115,68 @@ class MyPeripheralManager(private val context: Context, binaryMessenger: BinaryM // Already added. continue } + val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs + val descriptorHashCode = descriptor.hashCode() + this.mDescriptorsArgs[descriptorHashCode] = descriptorArgs + this.mDescriptors[descriptorHashCodeArgs] = descriptor val descriptorAdded = characteristic.addDescriptor(descriptor) if (!descriptorAdded) { throw IllegalStateException() } - val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - val descriptorHashCode = descriptor.hashCode() - this.descriptorsArgs[descriptorHashCode] = descriptorArgs - this.descriptors[descriptorHashCodeArgs] = descriptor } + val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs + val characteristicHashCode = characteristic.hashCode() + this.mCharacteristicsArgs[characteristicHashCode] = characteristicArgs + this.mCharacteristics[characteristicHashCodeArgs] = characteristic val characteristicAdded = service.addCharacteristic(characteristic) if (!characteristicAdded) { throw IllegalStateException() } - val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - val characteristicHashCode = characteristic.hashCode() - this.characteristicsArgs[characteristicHashCode] = characteristicArgs - this.characteristics[characteristicHashCodeArgs] = characteristic - } - val adding = server.addService(service) - if (!adding) { - throw IllegalStateException() } val serviceHashCodeArgs = serviceArgs.hashCodeArgs val serviceHashCode = service.hashCode() - this.servicesArgs[serviceHashCode] = serviceArgs - this.services[serviceHashCodeArgs] = service - addServiceCallback = callback + this.mServicesArgs[serviceHashCode] = serviceArgs + this.mServices[serviceHashCodeArgs] = service + val adding = mServer.addService(service) + if (!adding) { + throw IllegalStateException() + } + mAddServiceCallback = callback } catch (e: Throwable) { - freeService(serviceArgs) + mClearService(serviceArgs) callback(Result.failure(e)) } } - override fun removeService(serviceHashCodeArgs: Long) { - val service = services[serviceHashCodeArgs] as BluetoothGattService - val serviceHashCode = service.hashCode() - val serviceArgs = servicesArgs[serviceHashCode] as MyGattServiceArgs - val removed = server.removeService(service) + override fun removeService(hashCodeArgs: Long) { + val service = mServices[hashCodeArgs] as BluetoothGattService + val hashCode = service.hashCode() + val serviceArgs = mServicesArgs[hashCode] as MyGattServiceArgs + val removed = mServer.removeService(service) if (!removed) { throw IllegalStateException() } - freeService(serviceArgs) - } - - private fun freeService(serviceArgs: MyGattServiceArgs) { - val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull() - for (characteristicArgs in characteristicsArgs) { - val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull() - for (descriptorArgs in descriptorsArgs) { - val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - val descriptor = this.descriptors.remove(descriptorHashCodeArgs) ?: continue - val descriptorHashCode = descriptor.hashCode() - this.descriptorsArgs.remove(descriptorHashCode) - } - val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - val characteristic = this.characteristics.remove(characteristicHashCodeArgs) ?: continue - this.confirms.remove(characteristicHashCodeArgs) - val characteristicHashCode = characteristic.hashCode() - this.characteristicsArgs.remove(characteristicHashCode) - } - val serviceHashCodeArgs = serviceArgs.hashCodeArgs - val service = services.remove(serviceHashCodeArgs) ?: return - val serviceHashCode = service.hashCode() - servicesArgs.remove(serviceHashCode) + mClearService(serviceArgs) } override fun clearServices() { - server.clearServices() - val servicesArgs = this.servicesArgs.values + mServer.clearServices() + val servicesArgs = this.mServicesArgs.values for (serviceArgs in servicesArgs) { - freeService(serviceArgs) + mClearService(serviceArgs) } } - override fun startAdvertising(advertisementArgs: MyAdvertisementArgs, callback: (Result) -> Unit) { + override fun startAdvertising( + advertisementArgs: MyAdvertisementArgs, callback: (Result) -> Unit + ) { try { - if (startAdvertisingCallback != null) { - throw IllegalStateException() - } - val settings = AdvertiseSettings.Builder().setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED).setConnectable(true).build() + val settings = AdvertiseSettings.Builder() + .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED).setConnectable(true) + .build() val advertiseData = advertisementArgs.toAdvertiseData(adapter) advertiser.startAdvertising(settings, advertiseData, advertiseCallback) - startAdvertisingCallback = callback + mStartAdvertisingCallback = callback } catch (e: Throwable) { callback(Result.failure(e)) } @@ -219,264 +184,259 @@ class MyPeripheralManager(private val context: Context, binaryMessenger: BinaryM override fun stopAdvertising() { advertiser.stopAdvertising(advertiseCallback) - advertising = false + mAdvertising = false } - override fun getMaximumWriteLength(centralHashCodeArgs: Long): Long { - val mtu = mtus[centralHashCodeArgs] ?: 23 - val maximumWriteLength = (mtu - 3).coerceIn(20, 512) - return maximumWriteLength.toLong() - } - - override fun sendReadCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean, valueArgs: ByteArray) { - val device = devices[centralHashCodeArgs] as BluetoothDevice + override fun sendResponse( + addressArgs: String, + idArgs: Long, + statusNumberArgs: Long, + offsetArgs: Long, + valueArgs: ByteArray? + ) { + val device = mDevices[addressArgs] as BluetoothDevice val requestId = idArgs.toInt() - val status = if (statusArgs) BluetoothGatt.GATT_SUCCESS - else BluetoothGatt.GATT_FAILURE + val statusRawArgs = statusNumberArgs.toInt() + val statusArgs = MyGattStatusArgs.ofRaw(statusRawArgs) ?: throw IllegalArgumentException() + val status = statusArgs.toStatus() val offset = offsetArgs.toInt() - val sent = server.sendResponse(device, requestId, status, offset, valueArgs) + val sent = mServer.sendResponse(device, requestId, status, offset, valueArgs) if (!sent) { throw IllegalStateException("Send read characteristic reply failed.") } } - override fun sendWriteCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean) { - val device = devices[centralHashCodeArgs] as BluetoothDevice - val requestId = idArgs.toInt() - val status = if (statusArgs) BluetoothGatt.GATT_SUCCESS - else BluetoothGatt.GATT_FAILURE - val offset = offsetArgs.toInt() - val value = values.remove(idArgs) as ByteArray - val sent = server.sendResponse(device, requestId, status, offset, value) - if (!sent) { - throw IllegalStateException("Send write characteristic reply failed.") - } - } - - override fun notifyCharacteristicValueChanged(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result) -> Unit) { + override fun notifyCharacteristicChanged( + hashCodeArgs: Long, + valueArgs: ByteArray, + confirmArgs: Boolean, + addressArgs: String, + callback: (Result) -> Unit + ) { try { - val unfinishedCallback = notifyCharacteristicValueChangedCallbacks[centralHashCodeArgs] - if (unfinishedCallback != null) { - throw IllegalStateException() - } - val device = devices[centralHashCodeArgs] as BluetoothDevice - val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic - val confirm = confirms[characteristicHashCodeArgs] - ?: throw IllegalStateException("The characteristic is not subscribed.") + val device = mDevices[addressArgs] as BluetoothDevice + val characteristic = mCharacteristics[hashCodeArgs] as BluetoothGattCharacteristic val notifying = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - val statusCode = server.notifyCharacteristicChanged(device, characteristic, confirm, valueArgs) + val statusCode = mServer.notifyCharacteristicChanged( + device, characteristic, confirmArgs, valueArgs + ) statusCode == BluetoothStatusCodes.SUCCESS } else { + // TODO: remove this when minSdkVersion >= 33 characteristic.value = valueArgs - server.notifyCharacteristicChanged(device, characteristic, confirm) + mServer.notifyCharacteristicChanged(device, characteristic, confirmArgs) } if (!notifying) { throw IllegalStateException() } - notifyCharacteristicValueChangedCallbacks[centralHashCodeArgs] = callback + mNotifyCharacteristicValueChangedCallbacks[addressArgs] = callback } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean { - if (requestCode != REQUEST_CODE) { - return false + override fun onStateChanged(state: MyBluetoothLowEnergyState) { + when (state) { + MyBluetoothLowEnergyStateArgs.POWEREDOFF -> mCloseGattServer() + MyBluetoothLowEnergyStateArgs.POWEREDON -> mOpenGattServer() + else -> {} } - val authorized = results.all { r -> r == PackageManager.PERMISSION_GRANTED } - val callback = setUpCallback ?: return false - setUpCallback = null - val stateArgs = if (authorized) adapter.stateArgs - else MyBluetoothLowEnergyStateArgs.UNAUTHORIZED - val stateNumberArgs = stateArgs.raw.toLong() - val args = MyPeripheralManagerArgs(stateNumberArgs) - if (stateArgs == MyBluetoothLowEnergyStateArgs.POWEREDON) { - server = manager.openGattServer(context, bluetoothGattServerCallback) - } - callback(Result.success(args)) - if (authorized) { - register() - } - return true - } - - override fun onReceive(intent: Intent) { - val action = intent.action - if (action != BluetoothAdapter.ACTION_STATE_CHANGED) { - return - } - val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) - val stateArgs = state.toBluetoothLowEnergyStateArgs() - if (stateArgs == MyBluetoothLowEnergyStateArgs.POWEREDON) { - server = manager.openGattServer(context, bluetoothGattServerCallback) - } - val stateNumberArgs = stateArgs.raw.toLong() - api.onStateChanged(stateNumberArgs) {} + val stateNumberArgs = state.raw.toLong() + mApi.onStateChanged(stateNumberArgs) {} } fun onServiceAdded(status: Int, service: BluetoothGattService) { - val callback = addServiceCallback ?: return - addServiceCallback = null + val callback = mAddServiceCallback ?: return + mAddServiceCallback = null if (status == BluetoothGatt.GATT_SUCCESS) { callback(Result.success(Unit)) } else { - val hashCode = service.hashCode() - val serviceArgs = servicesArgs[hashCode] as MyGattServiceArgs - freeService(serviceArgs) val error = IllegalStateException("Read rssi failed with status: $status") callback(Result.failure(error)) + val hashCode = service.hashCode() + val serviceArgs = mServicesArgs[hashCode] ?: return + mClearService(serviceArgs) } } fun onStartSuccess(settingsInEffect: AdvertiseSettings) { - advertising = true - val callback = startAdvertisingCallback ?: return - startAdvertisingCallback = null + mAdvertising = true + val callback = mStartAdvertisingCallback ?: return + mStartAdvertisingCallback = null callback(Result.success(Unit)) } fun onStartFailure(errorCode: Int) { - val callback = startAdvertisingCallback ?: return - startAdvertisingCallback = null + val callback = mStartAdvertisingCallback ?: return + mStartAdvertisingCallback = null val error = IllegalStateException("Start advertising failed with error code: $errorCode") callback(Result.failure(error)) } fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) { - val hashCode = device.hashCode() - val centralArgs = centralsArgs.getOrPut(hashCode) { device.toCentralArgs() } - val centralHashCodeArgs = centralArgs.hashCodeArgs - devices[centralHashCodeArgs] = device + val centralArgs = device.toCentralArgs() + val addressArgs = centralArgs.addressArgs + val stateArgs = newState == BluetoothProfile.STATE_CONNECTED + mDevices[addressArgs] = device + mApi.onConnectionStateChanged(centralArgs, stateArgs) {} } fun onMtuChanged(device: BluetoothDevice, mtu: Int) { - val hashCode = device.hashCode() - val centralArgs = centralsArgs.getOrPut(hashCode) { device.toCentralArgs() } - val centralHashCodeArgs = centralArgs.hashCodeArgs - devices[centralHashCodeArgs] = device - mtus[centralHashCodeArgs] = mtu + val addressArgs = device.address + val mtuArgs = mtu.toLong() + mApi.onMtuChanged(addressArgs, mtuArgs) {} } - fun onCharacteristicReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, characteristic: BluetoothGattCharacteristic) { - val deviceHashCode = device.hashCode() - val centralArgs = centralsArgs.getOrPut(deviceHashCode) { device.toCentralArgs() } - val centralHashCodeArgs = centralArgs.hashCodeArgs - devices[centralHashCodeArgs] = device - val characteristicHashCode = characteristic.hashCode() - val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs + fun onCharacteristicReadRequest( + device: BluetoothDevice, + requestId: Int, + offset: Int, + characteristic: BluetoothGattCharacteristic + ) { + val addressArgs = device.address + val hashCode = characteristic.hashCode() + val characteristicArgs = mCharacteristicsArgs[hashCode] ?: return + val hashCodeArgs = characteristicArgs.hashCodeArgs val idArgs = requestId.toLong() val offsetArgs = offset.toLong() - api.onReadCharacteristicCommandReceived(centralArgs, characteristicArgs, idArgs, offsetArgs) {} + mApi.onCharacteristicReadRequest(addressArgs, hashCodeArgs, idArgs, offsetArgs) {} } - fun onCharacteristicWriteRequest(device: BluetoothDevice, requestId: Int, characteristic: BluetoothGattCharacteristic, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) { - val deviceHashCode = device.hashCode() - val centralArgs = centralsArgs.getOrPut(deviceHashCode) { device.toCentralArgs() } - val centralHashCodeArgs = centralArgs.hashCodeArgs - devices[centralHashCodeArgs] = device - if (preparedWrite) { - val preparedCharacteristic = preparedCharacteristics[deviceHashCode] - if (preparedCharacteristic != null && preparedCharacteristic != characteristic) { - val status = BluetoothGatt.GATT_CONNECTION_CONGESTED - server.sendResponse(device, requestId, status, offset, value) - return - } - val preparedValue = preparedValues[deviceHashCode] - if (preparedValue == null) { - preparedCharacteristics[deviceHashCode] = characteristic - preparedValues[deviceHashCode] = value - } else { - preparedValues[deviceHashCode] = preparedValue.plus(value) - } - val status = BluetoothGatt.GATT_SUCCESS - server.sendResponse(device, requestId, status, offset, value) - } else { - val characteristicHashCode = characteristic.hashCode() - val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs - val idArgs = requestId.toLong() - val offsetArgs = offset.toLong() - values[idArgs] = value - api.onWriteCharacteristicCommandReceived(centralArgs, characteristicArgs, idArgs, offsetArgs, value) {} - } + fun onCharacteristicWriteRequest( + device: BluetoothDevice, + requestId: Int, + characteristic: BluetoothGattCharacteristic, + preparedWrite: Boolean, + responseNeeded: Boolean, + offset: Int, + value: ByteArray + ) { + val addressArgs = device.address + val hashCode = characteristic.hashCode() + val characteristicArgs = mCharacteristicsArgs[hashCode] ?: return + val hashCodeArgs = characteristicArgs.hashCodeArgs + val idArgs = requestId.toLong() + val offsetArgs = offset.toLong() + mApi.onCharacteristicWriteRequest( + addressArgs, hashCodeArgs, idArgs, offsetArgs, value, preparedWrite, responseNeeded + ) {} } fun onExecuteWrite(device: BluetoothDevice, requestId: Int, execute: Boolean) { - val deviceHashCode = device.hashCode() - val centralArgs = centralsArgs[deviceHashCode] as MyCentralArgs - val characteristic = preparedCharacteristics.remove(deviceHashCode) as BluetoothGattCharacteristic - val characteristicHashCode = characteristic.hashCode() - val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs - val value = preparedValues.remove(deviceHashCode) as ByteArray - if (execute) { - val idArgs = requestId.toLong() - val offsetArgs = 0L - values[idArgs] = value - api.onWriteCharacteristicCommandReceived(centralArgs, characteristicArgs, idArgs, offsetArgs, value) {} - } else { - val status = BluetoothGatt.GATT_SUCCESS - val offset = 0 - server.sendResponse(device, requestId, status, offset, value) - } + val addressArgs = device.address + val idArgs = requestId.toLong() + mApi.onExecuteWrite(addressArgs, idArgs, execute) {} } - fun onDescriptorReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor) { + fun onDescriptorReadRequest( + device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor + ) { val status = BluetoothGatt.GATT_SUCCESS - val descriptorHashCode = descriptor.hashCode() - val descriptorArgs = descriptorsArgs[descriptorHashCode] as MyGattDescriptorArgs + val hashCode = descriptor.hashCode() + val descriptorArgs = mDescriptorsArgs[hashCode] as MyGattDescriptorArgs val value = descriptorArgs.valueArgs - val sent = server.sendResponse(device, requestId, status, offset, value) + val sent = mServer.sendResponse(device, requestId, status, offset, value) if (!sent) { Log.e(TAG, "onDescriptorReadRequest: send response failed.") } } - fun onDescriptorWriteRequest(device: BluetoothDevice, requestId: Int, descriptor: BluetoothGattDescriptor, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) { - val status = if (descriptor.uuid == CLIENT_CHARACTERISTIC_CONFIG_UUID) { - val deviceHashCode = device.hashCode() - val centralArgs = centralsArgs.getOrPut(deviceHashCode) { device.toCentralArgs() } - val centralHashCodeArgs = centralArgs.hashCodeArgs - devices[centralHashCodeArgs] = device + fun onDescriptorWriteRequest( + device: BluetoothDevice, + requestId: Int, + descriptor: BluetoothGattDescriptor, + preparedWrite: Boolean, + responseNeeded: Boolean, + offset: Int, + value: ByteArray + ) { + if (descriptor.uuid == CLIENT_CHARACTERISTIC_CONFIG_UUID) { + val addressArgs = device.address val characteristic = descriptor.characteristic - val characteristicHashCode = characteristic.hashCode() - val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs - val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - // TODO: what is 中缀? - if (value contentEquals BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) { - confirms[characteristicHashCodeArgs] = false - val stateArgs = true - api.onNotifyCharacteristicCommandReceived(centralArgs, characteristicArgs, stateArgs) {} - BluetoothGatt.GATT_SUCCESS - } else if (value contentEquals BluetoothGattDescriptor.ENABLE_INDICATION_VALUE) { - confirms[characteristicHashCodeArgs] = true - val stateArgs = true - api.onNotifyCharacteristicCommandReceived(centralArgs, characteristicArgs, stateArgs) {} - BluetoothGatt.GATT_SUCCESS - } else if (value contentEquals BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE) { - confirms.remove(characteristicHashCodeArgs) - val stateArgs = false - api.onNotifyCharacteristicCommandReceived(centralArgs, characteristicArgs, stateArgs) {} - BluetoothGatt.GATT_SUCCESS - } else { - BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED + val hashCode = characteristic.hashCode() + val characteristicArgs = mCharacteristicsArgs[hashCode] ?: return + val hashCodeArgs = characteristicArgs.hashCodeArgs + val stateArgs = value.toNotifyStateArgs() + val stateNumberArgs = stateArgs.raw.toLong() + mApi.onCharacteristicNotifyStateChanged( + addressArgs, hashCodeArgs, stateNumberArgs + ) {} + } + if (responseNeeded) { + val status = BluetoothGatt.GATT_SUCCESS + val sent = mServer.sendResponse(device, requestId, status, offset, value) + if (!sent) { + Log.e(TAG, "onDescriptorReadRequest: send response failed.") } - } else BluetoothGatt.GATT_SUCCESS - val sent = server.sendResponse(device, requestId, status, offset, value) - if (!sent) { - Log.e(TAG, "onDescriptorReadRequest: send response failed.") } } fun onNotificationSent(device: BluetoothDevice, status: Int) { - val deviceHashCode = device.hashCode() - val centralArgs = centralsArgs[deviceHashCode] as MyCentralArgs - val centralHashCodeArgs = centralArgs.hashCodeArgs - val callback = notifyCharacteristicValueChangedCallbacks.remove(centralHashCodeArgs) - ?: return + val addressArgs = device.address + val callback = mNotifyCharacteristicValueChangedCallbacks.remove(addressArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { callback(Result.success(Unit)) } else { - val error = IllegalStateException("Notify characteristic value changed failed with status: $status") + val error = + IllegalStateException("Notify characteristic value changed failed with status: $status") callback(Result.failure(error)) } } + + private fun mClearState() { + if (mAdvertising) { + stopAdvertising() + } + + mServicesArgs.clear() + mCharacteristicsArgs.clear() + mDescriptorsArgs.clear() + + mDevices.clear() + mServices.clear() + mCharacteristics.clear() + mDescriptors.clear() + + mSetUpCallback = null + mAddServiceCallback = null + mStartAdvertisingCallback = null + mNotifyCharacteristicValueChangedCallbacks.clear() + } + + private fun mOpenGattServer() { + if (mOpening) { + return + } + mServer = manager.openGattServer(mContext, bluetoothGattServerCallback) + mOpening = true + } + + private fun mCloseGattServer() { + if (!mOpening) { + return + } + mServer.close() + mOpening = false + } + + private fun mClearService(serviceArgs: MyGattServiceArgs) { + val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull() + for (characteristicArgs in characteristicsArgs) { + val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull() + for (descriptorArgs in descriptorsArgs) { + val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs + val descriptor = mDescriptors.remove(descriptorHashCodeArgs) ?: continue + val descriptorHashCode = descriptor.hashCode() + mDescriptorsArgs.remove(descriptorHashCode) + } + val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs + val characteristic = mCharacteristics.remove(characteristicHashCodeArgs) ?: continue + val characteristicHashCode = characteristic.hashCode() + mCharacteristicsArgs.remove(characteristicHashCode) + } + val serviceHashCodeArgs = serviceArgs.hashCodeArgs + val service = mServices.remove(serviceHashCodeArgs) ?: return + val serviceHashCode = service.hashCode() + mServicesArgs.remove(serviceHashCode) + } } \ No newline at end of file diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyRequestPermissionResultListener.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyRequestPermissionResultListener.kt index a16e994..a3350fe 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyRequestPermissionResultListener.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyRequestPermissionResultListener.kt @@ -2,8 +2,17 @@ package dev.yanshouwang.bluetooth_low_energy_android import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener -class MyRequestPermissionResultListener(private val bluetoothLowEnergyManager: MyBluetoothLowEnergyManager) : RequestPermissionsResultListener { - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, results: IntArray): Boolean { - return bluetoothLowEnergyManager.onRequestPermissionsResult(requestCode, results) +class MyRequestPermissionResultListener(manager: MyBluetoothLowEnergyManager) : + RequestPermissionsResultListener { + private val mManager: MyBluetoothLowEnergyManager + + init { + mManager = manager + } + + override fun onRequestPermissionsResult( + requestCode: Int, permissions: Array, results: IntArray + ): Boolean { + return mManager.onRequestPermissionsResult(requestCode, permissions, results) } } \ No newline at end of file diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyScanCallback.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyScanCallback.kt index cf0ace4..155b078 100644 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyScanCallback.kt +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyScanCallback.kt @@ -3,14 +3,20 @@ package dev.yanshouwang.bluetooth_low_energy_android import android.bluetooth.le.ScanCallback import android.bluetooth.le.ScanResult -class MyScanCallback(private val centralManager: MyCentralManager) : ScanCallback() { +class MyScanCallback(manager: MyCentralManager) : ScanCallback() { + private val mManager: MyCentralManager + + init { + mManager = manager + } + override fun onScanFailed(errorCode: Int) { super.onScanFailed(errorCode) - centralManager.onScanFailed(errorCode) + mManager.onScanFailed(errorCode) } override fun onScanResult(callbackType: Int, result: ScanResult) { super.onScanResult(callbackType, result) - centralManager.onScanResult(result) + mManager.onScanResult(result) } } \ No newline at end of file diff --git a/bluetooth_low_energy_android/example/lib/main.dart b/bluetooth_low_energy_android/example/lib/main.dart index 813aa77..f6e2074 100644 --- a/bluetooth_low_energy_android/example/lib/main.dart +++ b/bluetooth_low_energy_android/example/lib/main.dart @@ -8,18 +8,17 @@ import 'package:convert/convert.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; -CentralManager get centralManager => CentralManager.instance; -PeripheralManager get peripheralManager => PeripheralManager.instance; - void main() { runZonedGuarded(onStartUp, onCrashed); } void onStartUp() async { Logger.root.onRecord.listen(onLogRecord); + // hierarchicalLoggingEnabled = true; + // CentralManager.instance.logLevel = Level.WARNING; WidgetsFlutterBinding.ensureInitialized(); - await centralManager.setUp(); - await peripheralManager.setUp(); + await CentralManager.instance.setUp(); + await PeripheralManager.instance.setUp(); runApp(const MyApp()); } @@ -58,6 +57,8 @@ class _MyAppState extends State { return MaterialApp( theme: ThemeData.light( useMaterial3: true, + ).copyWith( + materialTapTargetSize: MaterialTapTargetSize.padded, ), home: const HomeView(), routes: { @@ -168,15 +169,15 @@ class _ScannerViewState extends State { @override void initState() { super.initState(); - state = ValueNotifier(centralManager.state); + state = ValueNotifier(BluetoothLowEnergyState.unknown); discovering = ValueNotifier(false); discoveredEventArgs = ValueNotifier([]); - stateChangedSubscription = centralManager.stateChanged.listen( + stateChangedSubscription = CentralManager.instance.stateChanged.listen( (eventArgs) { state.value = eventArgs.state; }, ); - discoveredSubscription = centralManager.discovered.listen( + discoveredSubscription = CentralManager.instance.discovered.listen( (eventArgs) { final items = discoveredEventArgs.value; final i = items.indexWhere( @@ -190,6 +191,11 @@ class _ScannerViewState extends State { } }, ); + _initialize(); + } + + void _initialize() async { + state.value = await CentralManager.instance.getState(); } @override @@ -233,12 +239,13 @@ class _ScannerViewState extends State { } Future startDiscovery() async { - await centralManager.startDiscovery(); + discoveredEventArgs.value = []; + await CentralManager.instance.startDiscovery(); discovering.value = true; } Future stopDiscovery() async { - await centralManager.stopDiscovery(); + await CentralManager.instance.stopDiscovery(); discovering.value = false; } @@ -340,7 +347,13 @@ class _ScannerViewState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, ), - trailing: RssiWidget(rssi), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RssiWidget(rssi), + Text('$rssi'), + ], + ), ); }, separatorBuilder: (context, i) { @@ -378,44 +391,39 @@ class PeripheralView extends StatefulWidget { } class _PeripheralViewState extends State { - late final ValueNotifier state; + late final ValueNotifier connectionState; late final DiscoveredEventArgs eventArgs; late final ValueNotifier> services; late final ValueNotifier> characteristics; late final ValueNotifier service; late final ValueNotifier characteristic; late final ValueNotifier writeType; - late final ValueNotifier maximumWriteLength; - late final ValueNotifier rssi; late final ValueNotifier> logs; late final TextEditingController writeController; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription valueChangedSubscription; - late final StreamSubscription rssiChangedSubscription; - late final Timer rssiTimer; + late final StreamSubscription connectionStateChangedSubscription; + late final StreamSubscription characteristicNotifiedSubscription; @override void initState() { super.initState(); eventArgs = widget.eventArgs; - state = ValueNotifier(false); + connectionState = ValueNotifier(false); services = ValueNotifier([]); characteristics = ValueNotifier([]); service = ValueNotifier(null); characteristic = ValueNotifier(null); writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); - maximumWriteLength = ValueNotifier(0); - rssi = ValueNotifier(-100); logs = ValueNotifier([]); writeController = TextEditingController(); - stateChangedSubscription = centralManager.peripheralStateChanged.listen( + connectionStateChangedSubscription = + CentralManager.instance.connectionStateChanged.listen( (eventArgs) { if (eventArgs.peripheral != this.eventArgs.peripheral) { return; } - final state = eventArgs.state; - this.state.value = state; - if (!state) { + final connectionState = eventArgs.connectionState; + this.connectionState.value = connectionState; + if (!connectionState) { services.value = []; characteristics.value = []; service.value = null; @@ -424,12 +432,13 @@ class _PeripheralViewState extends State { } }, ); - valueChangedSubscription = centralManager.characteristicValueChanged.listen( + characteristicNotifiedSubscription = + CentralManager.instance.characteristicNotified.listen( (eventArgs) { - final characteristic = this.characteristic.value; - if (eventArgs.characteristic != characteristic) { - return; - } + // final characteristic = this.characteristic.value; + // if (eventArgs.characteristic != characteristic) { + // return; + // } const type = LogType.notify; final log = Log(type, eventArgs.value); logs.value = [ @@ -438,28 +447,16 @@ class _PeripheralViewState extends State { ]; }, ); - rssiTimer = Timer.periodic( - const Duration(seconds: 5), - (timer) async { - final state = this.state.value; - if (state) { - rssi.value = await centralManager.readRSSI(eventArgs.peripheral); - } else { - rssi.value = -100; - } - }, - ); } @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () async { - if (state.value) { + return PopScope( + onPopInvoked: (didPop) async { + if (connectionState.value) { final peripheral = eventArgs.peripheral; - await centralManager.disconnect(peripheral); + await CentralManager.instance.disconnect(peripheral); } - return true; }, child: Scaffold( appBar: buildAppBar(context), @@ -474,25 +471,17 @@ class _PeripheralViewState extends State { title: Text(title), actions: [ ValueListenableBuilder( - valueListenable: state, + valueListenable: connectionState, builder: (context, state, child) { return TextButton( onPressed: () async { final peripheral = eventArgs.peripheral; if (state) { - await centralManager.disconnect(peripheral); - maximumWriteLength.value = 0; - rssi.value = 0; + await CentralManager.instance.disconnect(peripheral); } else { - await centralManager.connect(peripheral); + await CentralManager.instance.connect(peripheral); services.value = - await centralManager.discoverGATT(peripheral); - maximumWriteLength.value = - await centralManager.getMaximumWriteLength( - peripheral, - type: writeType.value, - ); - rssi.value = await centralManager.readRSSI(peripheral); + await CentralManager.instance.discoverGATT(peripheral); } }, child: Text(state ? 'DISCONNECT' : 'CONNECT'), @@ -566,7 +555,32 @@ class _PeripheralViewState extends State { hint: const Text('CHOOSE A CHARACTERISTIC'), value: characteristic, onChanged: (characteristic) { + if (characteristic == null) { + return; + } this.characteristic.value = characteristic; + final writeType = this.writeType.value; + final canWrite = characteristic.properties.contains( + GattCharacteristicProperty.write, + ); + final canWriteWithoutResponse = + characteristic.properties.contains( + GattCharacteristicProperty.writeWithoutResponse, + ); + if (writeType == + GattCharacteristicWriteType.withResponse && + !canWrite && + canWriteWithoutResponse) { + this.writeType.value = + GattCharacteristicWriteType.withoutResponse; + } + if (writeType == + GattCharacteristicWriteType.withoutResponse && + !canWriteWithoutResponse && + canWrite) { + this.writeType.value = + GattCharacteristicWriteType.withResponse; + } }, ); }, @@ -624,129 +638,45 @@ class _PeripheralViewState extends State { }, ), ), - Row( - children: [ - ValueListenableBuilder( - valueListenable: writeType, - builder: (context, writeType, child) { - return ToggleButtons( - onPressed: (i) async { - final type = GattCharacteristicWriteType.values[i]; - this.writeType.value = type; - maximumWriteLength.value = - await centralManager.getMaximumWriteLength( - eventArgs.peripheral, - type: type, - ); - }, - constraints: const BoxConstraints( - minWidth: 0.0, - minHeight: 0.0, - ), - borderRadius: BorderRadius.circular(4.0), - isSelected: GattCharacteristicWriteType.values - .map((type) => type == writeType) - .toList(), - children: GattCharacteristicWriteType.values.map((type) { - return Container( - margin: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 4.0, - ), - child: Text(type.name), - ); - }).toList(), - ); - // final segments = - // GattCharacteristicWriteType.values.map((type) { - // return ButtonSegment( - // value: type, - // label: Text(type.name), - // ); - // }).toList(); - // return SegmentedButton( - // segments: segments, - // selected: {writeType}, - // showSelectedIcon: false, - // style: OutlinedButton.styleFrom( - // tapTargetSize: MaterialTapTargetSize.shrinkWrap, - // padding: EdgeInsets.zero, - // visualDensity: VisualDensity.compact, - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(8.0), - // ), - // ), - // ); - }, - ), - const SizedBox(width: 8.0), - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: maximumWriteLength, - builder: (context, maximumWriteLength, child) { - return Text('$maximumWriteLength'); - }, - ); - }, - ), - const Spacer(), - ValueListenableBuilder( - valueListenable: rssi, - builder: (context, rssi, child) { - return RssiWidget(rssi); - }, - ), - ], - ), - Container( - margin: const EdgeInsets.only(bottom: 16.0), - height: 160.0, - child: ValueListenableBuilder( - valueListenable: characteristic, - builder: (context, characteristic, child) { - final bool canNotify, canRead, canWrite; - if (characteristic == null) { - canNotify = canRead = canWrite = false; - } else { - final properties = characteristic.properties; - canNotify = properties.contains( - GattCharacteristicProperty.notify, - ); - canRead = properties.contains( - GattCharacteristicProperty.read, - ); - canWrite = properties.contains( - GattCharacteristicProperty.write, - ); - } - return Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded( - child: TextField( - controller: writeController, - enabled: canWrite, - expands: true, - maxLines: null, - textAlignVertical: TextAlignVertical.top, - decoration: const InputDecoration( - border: OutlineInputBorder(), - contentPadding: EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 8.0, - ), - ), - ), - ), - Column( - mainAxisSize: MainAxisSize.min, + ValueListenableBuilder( + valueListenable: characteristic, + builder: (context, characteristic, chld) { + final bool canNotify, canRead, canWrite, canWriteWithoutResponse; + if (characteristic == null) { + canNotify = + canRead = canWrite = canWriteWithoutResponse = false; + } else { + final properties = characteristic.properties; + canNotify = properties.contains( + GattCharacteristicProperty.notify, + ) || + properties.contains( + GattCharacteristicProperty.indicate, + ); + canRead = properties.contains( + GattCharacteristicProperty.read, + ); + canWrite = properties.contains( + GattCharacteristicProperty.write, + ); + canWriteWithoutResponse = properties.contains( + GattCharacteristicProperty.writeWithoutResponse, + ); + } + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + margin: const EdgeInsets.symmetric(vertical: 4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ - TextButton( + ElevatedButton( onPressed: characteristic != null && canNotify ? () async { - await centralManager.notifyCharacteristic( + await CentralManager.instance + .setCharacteristicNotifyState( characteristic, state: true, ); @@ -754,10 +684,11 @@ class _PeripheralViewState extends State { : null, child: const Text('NOTIFY'), ), - TextButton( + const SizedBox(width: 8.0), + ElevatedButton( onPressed: characteristic != null && canRead ? () async { - final value = await centralManager + final value = await CentralManager.instance .readCharacteristic(characteristic); const type = LogType.read; final log = Log(type, value); @@ -765,31 +696,124 @@ class _PeripheralViewState extends State { } : null, child: const Text('READ'), - ), - TextButton( - onPressed: characteristic != null && canWrite - ? () async { - final text = writeController.text; - final elements = utf8.encode(text); - final value = Uint8List.fromList(elements); - final type = writeType.value; - await centralManager.writeCharacteristic( - characteristic, - value: value, - type: type, - ); - final log = Log(LogType.write, value); - logs.value = [...logs.value, log]; - } - : null, - child: const Text('WRITE'), - ), + ) ], ), - ], - ); - }, - ), + ), + SizedBox( + height: 160.0, + child: TextField( + controller: writeController, + enabled: canWrite || canWriteWithoutResponse, + expands: true, + maxLines: null, + textAlignVertical: TextAlignVertical.top, + decoration: const InputDecoration( + border: OutlineInputBorder(), + contentPadding: EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 8.0, + ), + ), + ), + ), + Row( + children: [ + ValueListenableBuilder( + valueListenable: writeType, + builder: (context, writeType, child) { + return ToggleButtons( + onPressed: canWrite || canWriteWithoutResponse + ? (i) { + if (!canWrite || !canWriteWithoutResponse) { + return; + } + final type = + GattCharacteristicWriteType.values[i]; + this.writeType.value = type; + } + : null, + constraints: const BoxConstraints( + minWidth: 0.0, + minHeight: 0.0, + ), + borderRadius: BorderRadius.circular(4.0), + isSelected: GattCharacteristicWriteType.values + .map((type) => type == writeType) + .toList(), + children: GattCharacteristicWriteType.values.map( + (type) { + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, + ), + child: Text(type.name), + ); + }, + ).toList(), + ); + // final segments = + // GattCharacteristicWriteType.values.map((type) { + // return ButtonSegment( + // value: type, + // label: Text(type.name), + // ); + // }).toList(); + // return SegmentedButton( + // segments: segments, + // selected: {writeType}, + // showSelectedIcon: false, + // style: OutlinedButton.styleFrom( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, + // padding: EdgeInsets.zero, + // visualDensity: VisualDensity.compact, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8.0), + // ), + // ), + // ); + }, + ), + const Spacer(), + ElevatedButton( + onPressed: characteristic != null && canWrite + ? () async { + final text = writeController.text; + final elements = utf8.encode(text); + final value = Uint8List.fromList(elements); + final type = writeType.value; + // Fragments the value by 512 bytes. + const fragmentSize = 512; + var start = 0; + while (start < value.length) { + final end = start + fragmentSize; + final fragmentedValue = end < value.length + ? value.sublist(start, end) + : value.sublist(start); + await CentralManager.instance + .writeCharacteristic( + characteristic, + value: fragmentedValue, + type: type, + ); + final log = Log( + LogType.write, + fragmentedValue, + ); + logs.value = [...logs.value, log]; + start = end; + } + } + : null, + child: const Text('WRITE'), + ), + ], + ), + const SizedBox(height: 16.0), + ], + ); + }, ), ], ), @@ -799,17 +823,14 @@ class _PeripheralViewState extends State { @override void dispose() { super.dispose(); - rssiTimer.cancel(); - stateChangedSubscription.cancel(); - valueChangedSubscription.cancel(); - state.dispose(); + connectionStateChangedSubscription.cancel(); + characteristicNotifiedSubscription.cancel(); + connectionState.dispose(); services.dispose(); characteristics.dispose(); service.dispose(); characteristic.dispose(); writeType.dispose(); - maximumWriteLength.dispose(); - rssi.dispose(); logs.dispose(); writeController.dispose(); } @@ -828,87 +849,63 @@ class _AdvertiserViewState extends State late final ValueNotifier advertising; late final ValueNotifier> logs; late final StreamSubscription stateChangedSubscription; - late final StreamSubscription readCharacteristicCommandReceivedSubscription; - late final StreamSubscription writeCharacteristicCommandReceivedSubscription; - late final StreamSubscription notifyCharacteristicCommandReceivedSubscription; + late final StreamSubscription characteristicReadSubscription; + late final StreamSubscription characteristicWrittenSubscription; + late final StreamSubscription characteristicNotifyStateChangedSubscription; @override void initState() { super.initState(); - state = ValueNotifier(peripheralManager.state); + state = ValueNotifier(BluetoothLowEnergyState.unknown); advertising = ValueNotifier(false); logs = ValueNotifier([]); - stateChangedSubscription = peripheralManager.stateChanged.listen( + stateChangedSubscription = PeripheralManager.instance.stateChanged.listen( (eventArgs) { state.value = eventArgs.state; }, ); - readCharacteristicCommandReceivedSubscription = - peripheralManager.readCharacteristicCommandReceived.listen( + characteristicReadSubscription = + PeripheralManager.instance.characteristicRead.listen( (eventArgs) async { final central = eventArgs.central; final characteristic = eventArgs.characteristic; - final id = eventArgs.id; - final offset = eventArgs.offset; + final value = eventArgs.value; final log = Log( LogType.read, - Uint8List.fromList([]), - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', + value, + 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', ); logs.value = [ ...logs.value, log, ]; - // final maximumWriteLength = peripheralManager.getMaximumWriteLength( - // central, - // ); - const status = true; - final value = Uint8List.fromList([0x01, 0x02, 0x03]); - await peripheralManager.sendReadCharacteristicReply( - central, - characteristic: characteristic, - id: id, - offset: offset, - status: status, - value: value, - ); }, ); - writeCharacteristicCommandReceivedSubscription = - peripheralManager.writeCharacteristicCommandReceived.listen( + characteristicWrittenSubscription = + PeripheralManager.instance.characteristicWritten.listen( (eventArgs) async { final central = eventArgs.central; final characteristic = eventArgs.characteristic; - final id = eventArgs.id; - final offset = eventArgs.offset; final value = eventArgs.value; final log = Log( LogType.write, value, - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', + 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', ); logs.value = [ ...logs.value, log, ]; - const status = true; - await peripheralManager.sendWriteCharacteristicReply( - central, - characteristic: characteristic, - id: id, - offset: offset, - status: status, - ); }, ); - notifyCharacteristicCommandReceivedSubscription = - peripheralManager.notifyCharacteristicCommandReceived.listen( + characteristicNotifyStateChangedSubscription = + PeripheralManager.instance.characteristicNotifyStateChanged.listen( (eventArgs) async { final central = eventArgs.central; final characteristic = eventArgs.characteristic; final state = eventArgs.state; final log = Log( - LogType.write, + LogType.notify, Uint8List.fromList([]), 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state', ); @@ -918,15 +915,21 @@ class _AdvertiserViewState extends State ]; // Write someting to the central when notify started. if (state) { - final value = Uint8List.fromList([0x03, 0x02, 0x01]); - await peripheralManager.notifyCharacteristicValueChanged( - central, - characteristic: characteristic, + final elements = List.generate(2000, (i) => i % 256); + final value = Uint8List.fromList(elements); + await PeripheralManager.instance.writeCharacteristic( + characteristic, value: value, + central: central, ); } }, ); + _initialize(); + } + + void _initialize() async { + state.value = await PeripheralManager.instance.getState(); } @override @@ -970,7 +973,9 @@ class _AdvertiserViewState extends State } Future startAdvertising() async { - await peripheralManager.clearServices(); + await PeripheralManager.instance.clearServices(); + final elements = List.generate(1000, (i) => i % 256); + final value = Uint8List.fromList(elements); final service = GattService( uuid: UUID.short(100), characteristics: [ @@ -979,15 +984,16 @@ class _AdvertiserViewState extends State properties: [ GattCharacteristicProperty.read, ], + value: value, descriptors: [], ), GattCharacteristic( uuid: UUID.short(201), properties: [ - GattCharacteristicProperty.read, GattCharacteristicProperty.write, GattCharacteristicProperty.writeWithoutResponse, ], + value: Uint8List.fromList([]), descriptors: [], ), GattCharacteristic( @@ -996,24 +1002,41 @@ class _AdvertiserViewState extends State GattCharacteristicProperty.notify, GattCharacteristicProperty.indicate, ], + value: Uint8List.fromList([]), + descriptors: [], + ), + GattCharacteristic( + uuid: UUID.short(203), + properties: [ + GattCharacteristicProperty.notify, + ], + value: Uint8List.fromList([]), + descriptors: [], + ), + GattCharacteristic( + uuid: UUID.short(204), + properties: [ + GattCharacteristicProperty.indicate, + ], + value: Uint8List.fromList([]), descriptors: [], ), ], ); - await peripheralManager.addService(service); + await PeripheralManager.instance.addService(service); final advertisement = Advertisement( - name: 'flutter', + name: 'le12138', manufacturerSpecificData: ManufacturerSpecificData( id: 0x2e19, data: Uint8List.fromList([0x01, 0x02, 0x03]), ), ); - await peripheralManager.startAdvertising(advertisement); + await PeripheralManager.instance.startAdvertising(advertisement); advertising.value = true; } Future stopAdvertising() async { - await peripheralManager.stopAdvertising(); + await PeripheralManager.instance.stopAdvertising(); advertising.value = false; } @@ -1074,9 +1097,9 @@ class _AdvertiserViewState extends State void dispose() { super.dispose(); stateChangedSubscription.cancel(); - readCharacteristicCommandReceivedSubscription.cancel(); - writeCharacteristicCommandReceivedSubscription.cancel(); - notifyCharacteristicCommandReceivedSubscription.cancel(); + characteristicReadSubscription.cancel(); + characteristicWrittenSubscription.cancel(); + characteristicNotifyStateChangedSubscription.cancel(); state.dispose(); advertising.dispose(); logs.dispose(); diff --git a/bluetooth_low_energy_android/example/pubspec.lock b/bluetooth_low_energy_android/example/pubspec.lock index 72308a2..1eb50d2 100644 --- a/bluetooth_low_energy_android/example/pubspec.lock +++ b/bluetooth_low_energy_android/example/pubspec.lock @@ -15,15 +15,15 @@ packages: path: ".." relative: true source: path - version: "4.0.0" + version: "5.0.0" bluetooth_low_energy_platform_interface: dependency: "direct main" description: name: bluetooth_low_energy_platform_interface - sha256: a01819f4ef89d033edaa979465ec8c3af13b2618dc718d90fe681be91b6c4356 + sha256: "54f92ab2d7746fb6f2b4a40a48cd7eb8e3bf772f3ee89e1979d4d7b741fb2a05" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" boolean_selector: dependency: transitive description: @@ -52,10 +52,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: "direct main" description: @@ -102,10 +102,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -125,10 +125,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" lints: dependency: transitive description: @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -189,18 +189,18 @@ packages: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" process: dependency: transitive description: @@ -226,18 +226,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -266,10 +266,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" typed_data: dependency: transitive description: @@ -290,18 +290,18 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 url: "https://pub.dev" source: hosted - version: "11.7.1" + version: "11.10.0" web: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" webdriver: dependency: transitive description: @@ -311,5 +311,5 @@ packages: source: hosted version: "3.0.2" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.3.0" diff --git a/bluetooth_low_energy_android/example/pubspec.yaml b/bluetooth_low_energy_android/example/pubspec.yaml index 692aa5a..28077ca 100644 --- a/bluetooth_low_energy_android/example/pubspec.yaml +++ b/bluetooth_low_energy_android/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^4.0.0 + bluetooth_low_energy_platform_interface: ^5.0.0 bluetooth_low_energy_android: # When depending on this package from a real application you should use: # bluetooth_low_energy: ^x.y.z @@ -30,7 +30,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 convert: ^3.1.1 - intl: ^0.18.1 + intl: ^0.19.0 dev_dependencies: integration_test: diff --git a/bluetooth_low_energy_android/lib/bluetooth_low_energy_android.dart b/bluetooth_low_energy_android/lib/bluetooth_low_energy_android.dart index c5effe3..9984ca1 100644 --- a/bluetooth_low_energy_android/lib/bluetooth_low_energy_android.dart +++ b/bluetooth_low_energy_android/lib/bluetooth_low_energy_android.dart @@ -1,11 +1,11 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'src/my_central_manager2.dart'; -import 'src/my_peripheral_manager2.dart'; +import 'src/my_central_manager.dart'; +import 'src/my_peripheral_manager.dart'; abstract class BluetoothLowEnergyAndroid { static void registerWith() { - MyCentralManager.instance = MyCentralManager2(); - MyPeripheralManager.instance = MyPeripheralManager2(); + CentralManager.instance = MyCentralManager(); + PeripheralManager.instance = MyPeripheralManager(); } } diff --git a/bluetooth_low_energy_android/lib/src/my_api.dart b/bluetooth_low_energy_android/lib/src/my_api.dart index a5f4075..1dc16d2 100644 --- a/bluetooth_low_energy_android/lib/src/my_api.dart +++ b/bluetooth_low_energy_android/lib/src/my_api.dart @@ -3,28 +3,47 @@ import 'dart:typed_data'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'my_api.g.dart'; +import 'my_central2.dart'; import 'my_gatt_characteristic2.dart'; import 'my_gatt_descriptor2.dart'; import 'my_gatt_service2.dart'; +import 'my_peripheral2.dart'; export 'my_api.g.dart'; +// ToObject extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { BluetoothLowEnergyState toState() { return BluetoothLowEnergyState.values[index]; } } +extension MyGattCharacteristicPropertyArgsX + on MyGattCharacteristicPropertyArgs { + GattCharacteristicProperty toProperty() { + return GattCharacteristicProperty.values[index]; + } +} + +extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs { + ManufacturerSpecificData toManufacturerSpecificData() { + final id = idArgs; + final data = dataArgs; + return ManufacturerSpecificData( + id: id, + data: data, + ); + } +} + extension MyAdvertisementArgsX on MyAdvertisementArgs { Advertisement toAdvertisement() { final name = nameArgs; - final serviceUUIDs = serviceUUIDsArgs - .cast() - .map((args) => UUID.fromString(args)) - .toList(); + final serviceUUIDs = + serviceUUIDsArgs.cast().map((args) => args.toUUID()).toList(); final serviceData = serviceDataArgs.cast().map( (uuidArgs, dataArgs) { - final uuid = UUID.fromString(uuidArgs); + final uuid = uuidArgs.toUUID(); final data = dataArgs; return MapEntry(uuid, data); }, @@ -40,61 +59,38 @@ extension MyAdvertisementArgsX on MyAdvertisementArgs { } } -extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs { - ManufacturerSpecificData toManufacturerSpecificData() { - final id = idArgs; - final data = dataArgs; - return ManufacturerSpecificData( - id: id, - data: data, +extension MyCentralArgsX on MyCentralArgs { + MyCentral2 toCentral() { + return MyCentral2( + address: addressArgs, ); } } -extension MyGattCharacteristicPropertyArgsX - on MyGattCharacteristicPropertyArgs { - GattCharacteristicProperty toProperty() { - return GattCharacteristicProperty.values[index]; - } -} - -extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType { - MyGattCharacteristicWriteTypeArgs toArgs() { - return MyGattCharacteristicWriteTypeArgs.values[index]; - } -} - extension MyPeripheralArgsX on MyPeripheralArgs { - MyPeripheral toPeripheral() { - final hashCode = hashCodeArgs; - final uuid = UUID.fromString(uuidArgs); - return MyPeripheral( - hashCode: hashCode, - uuid: uuid, + MyPeripheral2 toPeripheral() { + return MyPeripheral2( + address: addressArgs, ); } } -extension MyGattServiceArgsX on MyGattServiceArgs { - MyGattService2 toService2() { +extension MyGattDescriptorArgsX on MyGattDescriptorArgs { + MyGattDescriptor2 toDescriptor2(MyPeripheral2 peripheral) { final hashCode = hashCodeArgs; - final uuid = UUID.fromString(uuidArgs); - final characteristics = characteristicsArgs - .cast() - .map((args) => args.toCharacteristic2()) - .toList(); - return MyGattService2( + final uuid = uuidArgs.toUUID(); + return MyGattDescriptor2( + peripheral: peripheral, hashCode: hashCode, uuid: uuid, - characteristics: characteristics, ); } } extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs { - MyGattCharacteristic2 toCharacteristic2() { + MyGattCharacteristic2 toCharacteristic2(MyPeripheral2 peripheral) { final hashCode = hashCodeArgs; - final uuid = UUID.fromString(uuidArgs); + final uuid = uuidArgs.toUUID(); final properties = propertyNumbersArgs.cast().map( (args) { final propertyArgs = MyGattCharacteristicPropertyArgs.values[args]; @@ -103,9 +99,10 @@ extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs { ).toList(); final descriptors = descriptorsArgs .cast() - .map((args) => args.toDescriptor2()) + .map((args) => args.toDescriptor2(peripheral)) .toList(); return MyGattCharacteristic2( + peripheral: peripheral, hashCode: hashCode, uuid: uuid, properties: properties, @@ -114,45 +111,39 @@ extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs { } } -extension MyGattDescriptorArgsX on MyGattDescriptorArgs { - MyGattDescriptor2 toDescriptor2() { +extension MyGattServiceArgsX on MyGattServiceArgs { + MyGattService2 toService2(MyPeripheral2 peripheral) { final hashCode = hashCodeArgs; - final uuid = UUID.fromString(uuidArgs); - return MyGattDescriptor2( + final uuid = uuidArgs.toUUID(); + final characteristics = characteristicsArgs + .cast() + .map((args) => args.toCharacteristic2(peripheral)) + .toList(); + return MyGattService2( + peripheral: peripheral, hashCode: hashCode, uuid: uuid, + characteristics: characteristics, ); } } -extension MyCentralArgsX on MyCentralArgs { - MyCentral toCentral() { - final hashCode = hashCodeArgs; - final uuid = UUID.fromString(uuidArgs); - return MyCentral( - hashCode: hashCode, - uuid: uuid, - ); +extension MyUuidArgsX on String { + UUID toUUID() { + return UUID.fromString(this); } } -extension AdvertisementX on Advertisement { - MyAdvertisementArgs toArgs() { - final nameArgs = name; - final serviceUUIDsArgs = - serviceUUIDs.map((uuid) => uuid.toString()).toList(); - final serviceDataArgs = serviceData.map((uuid, data) { - final uuidArgs = uuid.toString(); - final dataArgs = data; - return MapEntry(uuidArgs, dataArgs); - }); - final manufacturerSpecificDataArgs = manufacturerSpecificData?.toArgs(); - return MyAdvertisementArgs( - nameArgs: nameArgs, - serviceUUIDsArgs: serviceUUIDsArgs, - serviceDataArgs: serviceDataArgs, - manufacturerSpecificDataArgs: manufacturerSpecificDataArgs, - ); +// ToArgs +extension GattCharacteristicPropertyX on GattCharacteristicProperty { + MyGattCharacteristicPropertyArgs toArgs() { + return MyGattCharacteristicPropertyArgs.values[index]; + } +} + +extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType { + MyGattCharacteristicWriteTypeArgs toArgs() { + return MyGattCharacteristicWriteTypeArgs.values[index]; } } @@ -167,18 +158,34 @@ extension ManufacturerSpecificDataX on ManufacturerSpecificData { } } -extension MyGattServiceX on MyGattService { - MyGattServiceArgs toArgs() { +extension AdvertisementX on Advertisement { + MyAdvertisementArgs toArgs() { + final nameArgs = name; + final serviceUUIDsArgs = serviceUUIDs.map((uuid) => uuid.toArgs()).toList(); + final serviceDataArgs = serviceData.map((uuid, data) { + final uuidArgs = uuid.toArgs(); + final dataArgs = data; + return MapEntry(uuidArgs, dataArgs); + }); + final manufacturerSpecificDataArgs = manufacturerSpecificData?.toArgs(); + return MyAdvertisementArgs( + nameArgs: nameArgs, + serviceUUIDsArgs: serviceUUIDsArgs, + serviceDataArgs: serviceDataArgs, + manufacturerSpecificDataArgs: manufacturerSpecificDataArgs, + ); + } +} + +extension MyGattDescriptorX on MyGattDescriptor { + MyGattDescriptorArgs toArgs() { final hashCodeArgs = hashCode; - final uuidArgs = uuid.toString(); - final characteristicsArgs = characteristics - .cast() - .map((characteristic) => characteristic.toArgs()) - .toList(); - return MyGattServiceArgs( + final uuidArgs = uuid.toArgs(); + final valueArgs = value; + return MyGattDescriptorArgs( hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, - characteristicsArgs: characteristicsArgs, + valueArgs: valueArgs, ); } } @@ -186,7 +193,7 @@ extension MyGattServiceX on MyGattService { extension MyGattCharacteristicX on MyGattCharacteristic { MyGattCharacteristicArgs toArgs() { final hashCodeArgs = hashCode; - final uuidArgs = uuid.toString(); + final uuidArgs = uuid.toArgs(); final propertyNumbersArgs = properties.map((property) { final propertyArgs = property.toArgs(); return propertyArgs.index; @@ -204,21 +211,24 @@ extension MyGattCharacteristicX on MyGattCharacteristic { } } -extension MyGattDescriptorX on MyGattDescriptor { - MyGattDescriptorArgs toArgs() { +extension MyGattServiceX on MyGattService { + MyGattServiceArgs toArgs() { final hashCodeArgs = hashCode; - final uuidArgs = uuid.toString(); - final valueArgs = value; - return MyGattDescriptorArgs( + final uuidArgs = uuid.toArgs(); + final characteristicsArgs = characteristics + .cast() + .map((characteristic) => characteristic.toArgs()) + .toList(); + return MyGattServiceArgs( hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, - valueArgs: valueArgs, + characteristicsArgs: characteristicsArgs, ); } } -extension GattCharacteristicPropertyX on GattCharacteristicProperty { - MyGattCharacteristicPropertyArgs toArgs() { - return MyGattCharacteristicPropertyArgs.values[index]; +extension UuidX on UUID { + String toArgs() { + return toString(); } } diff --git a/bluetooth_low_energy_android/lib/src/my_api.g.dart b/bluetooth_low_energy_android/lib/src/my_api.g.dart index beab20d..cffc72a 100644 --- a/bluetooth_low_energy_android/lib/src/my_api.g.dart +++ b/bluetooth_low_energy_android/lib/src/my_api.g.dart @@ -1,12 +1,20 @@ -// Autogenerated from Pigeon (v12.0.1), do not edit directly. +// Autogenerated from Pigeon (v15.0.2), 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 +// 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 import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; @@ -38,96 +46,47 @@ enum MyGattCharacteristicWriteTypeArgs { withoutResponse, } -class MyCentralManagerArgs { - MyCentralManagerArgs({ - required this.stateNumberArgs, - }); - - int stateNumberArgs; - - Object encode() { - return [ - stateNumberArgs, - ]; - } - - static MyCentralManagerArgs decode(Object result) { - result as List; - return MyCentralManagerArgs( - stateNumberArgs: result[0]! as int, - ); - } +enum MyGattCharacteristicNotifyStateArgs { + none, + notify, + indicate, } -class MyPeripheralManagerArgs { - MyPeripheralManagerArgs({ - required this.stateNumberArgs, - }); - - int stateNumberArgs; - - Object encode() { - return [ - stateNumberArgs, - ]; - } - - static MyPeripheralManagerArgs decode(Object result) { - result as List; - return MyPeripheralManagerArgs( - stateNumberArgs: result[0]! as int, - ); - } +enum MyGattStatusArgs { + success, + readNotPermitted, + writeNotPermitted, + requestNotSupported, + invalidOffset, + insufficientAuthentication, + insufficientEncryption, + invalidAttributeLength, + connectionCongested, + failure, } -class MyCentralArgs { - MyCentralArgs({ - required this.hashCodeArgs, - required this.uuidArgs, +class MyManufacturerSpecificDataArgs { + MyManufacturerSpecificDataArgs({ + required this.idArgs, + required this.dataArgs, }); - int hashCodeArgs; + int idArgs; - String uuidArgs; + Uint8List dataArgs; Object encode() { return [ - hashCodeArgs, - uuidArgs, + idArgs, + dataArgs, ]; } - static MyCentralArgs decode(Object result) { + static MyManufacturerSpecificDataArgs decode(Object result) { result as List; - return MyCentralArgs( - hashCodeArgs: result[0]! as int, - uuidArgs: result[1]! as String, - ); - } -} - -class MyPeripheralArgs { - MyPeripheralArgs({ - required this.hashCodeArgs, - required this.uuidArgs, - }); - - int hashCodeArgs; - - String uuidArgs; - - Object encode() { - return [ - hashCodeArgs, - uuidArgs, - ]; - } - - static MyPeripheralArgs decode(Object result) { - result as List; - return MyPeripheralArgs( - hashCodeArgs: result[0]! as int, - uuidArgs: result[1]! as String, + return MyManufacturerSpecificDataArgs( + idArgs: result[0]! as int, + dataArgs: result[1]! as Uint8List, ); } } @@ -170,59 +129,75 @@ class MyAdvertisementArgs { } } -class MyManufacturerSpecificDataArgs { - MyManufacturerSpecificDataArgs({ - required this.idArgs, - required this.dataArgs, +class MyCentralArgs { + MyCentralArgs({ + required this.addressArgs, }); - int idArgs; - - Uint8List dataArgs; + String addressArgs; Object encode() { return [ - idArgs, - dataArgs, + addressArgs, ]; } - static MyManufacturerSpecificDataArgs decode(Object result) { + static MyCentralArgs decode(Object result) { result as List; - return MyManufacturerSpecificDataArgs( - idArgs: result[0]! as int, - dataArgs: result[1]! as Uint8List, + return MyCentralArgs( + addressArgs: result[0]! as String, ); } } -class MyGattServiceArgs { - MyGattServiceArgs({ +class MyPeripheralArgs { + MyPeripheralArgs({ + required this.addressArgs, + }); + + String addressArgs; + + Object encode() { + return [ + addressArgs, + ]; + } + + static MyPeripheralArgs decode(Object result) { + result as List; + return MyPeripheralArgs( + addressArgs: result[0]! as String, + ); + } +} + +class MyGattDescriptorArgs { + MyGattDescriptorArgs({ required this.hashCodeArgs, required this.uuidArgs, - required this.characteristicsArgs, + this.valueArgs, }); int hashCodeArgs; String uuidArgs; - List characteristicsArgs; + Uint8List? valueArgs; Object encode() { return [ hashCodeArgs, uuidArgs, - characteristicsArgs, + valueArgs, ]; } - static MyGattServiceArgs decode(Object result) { + static MyGattDescriptorArgs decode(Object result) { result as List; - return MyGattServiceArgs( + return MyGattDescriptorArgs( hashCodeArgs: result[0]! as int, uuidArgs: result[1]! as String, - characteristicsArgs: (result[2] as List?)!.cast(), + valueArgs: result[2] as Uint8List?, ); } } @@ -263,33 +238,33 @@ class MyGattCharacteristicArgs { } } -class MyGattDescriptorArgs { - MyGattDescriptorArgs({ +class MyGattServiceArgs { + MyGattServiceArgs({ required this.hashCodeArgs, required this.uuidArgs, - this.valueArgs, + required this.characteristicsArgs, }); int hashCodeArgs; String uuidArgs; - Uint8List? valueArgs; + List characteristicsArgs; Object encode() { return [ hashCodeArgs, uuidArgs, - valueArgs, + characteristicsArgs, ]; } - static MyGattDescriptorArgs decode(Object result) { + static MyGattServiceArgs decode(Object result) { result as List; - return MyGattDescriptorArgs( + return MyGattServiceArgs( hashCodeArgs: result[0]! as int, uuidArgs: result[1]! as String, - valueArgs: result[2] as Uint8List?, + characteristicsArgs: (result[2] as List?)!.cast(), ); } } @@ -298,17 +273,14 @@ class _MyCentralManagerHostApiCodec extends StandardMessageCodec { const _MyCentralManagerHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is MyCentralManagerArgs) { + if (value is MyGattCharacteristicArgs) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is MyGattCharacteristicArgs) { + } else if (value is MyGattDescriptorArgs) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is MyGattDescriptorArgs) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); } else if (value is MyGattServiceArgs) { - buffer.putUint8(131); + buffer.putUint8(130); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -319,12 +291,10 @@ class _MyCentralManagerHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return MyCentralManagerArgs.decode(readValue(buffer)!); - case 129: return MyGattCharacteristicArgs.decode(readValue(buffer)!); - case 130: + case 129: return MyGattDescriptorArgs.decode(readValue(buffer)!); - case 131: + case 130: return MyGattServiceArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -337,54 +307,49 @@ class MyCentralManagerHostApi { /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. MyCentralManagerHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec codec = _MyCentralManagerHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyCentralManagerHostApiCodec(); - Future setUp() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setUp', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + Future setUp() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setUp'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { - return (replyList[0] as MyCentralManagerArgs?)!; + return; } } Future startDiscovery() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.startDiscovery', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.startDiscovery'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; @@ -392,293 +357,266 @@ class MyCentralManagerHostApi { } Future stopDiscovery() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.stopDiscovery', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.stopDiscovery'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future connect(int arg_peripheralHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.connect', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs]) as List?; - if (replyList == null) { + Future connect(String addressArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.connect'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future disconnect(int arg_peripheralHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.disconnect', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs]) as List?; - if (replyList == null) { + Future disconnect(String addressArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.disconnect'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future getMaximumWriteLength(int arg_peripheralHashCodeArgs, int arg_typeNumberArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.getMaximumWriteLength', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_typeNumberArgs]) as List?; - if (replyList == null) { + Future requestMTU(String addressArgs, int mtuArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.requestMTU'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, mtuArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as int?)!; + return (__pigeon_replyList[0] as int?)!; } } - Future readRSSI(int arg_peripheralHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readRSSI', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs]) as List?; - if (replyList == null) { + Future readRSSI(String addressArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readRSSI'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as int?)!; + return (__pigeon_replyList[0] as int?)!; } } - Future> discoverGATT(int arg_peripheralHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.discoverGATT', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs]) as List?; - if (replyList == null) { + Future> discoverServices(String addressArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.discoverServices'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as List?)!.cast(); + return (__pigeon_replyList[0] as List?)!.cast(); } } - Future requestMTU(int arg_peripheralHashCodeArgs, int arg_mtuArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.requestMTU', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_mtuArgs]) as List?; - if (replyList == null) { + Future readCharacteristic(String addressArgs, int hashCodeArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readCharacteristic'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, hashCodeArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as int?)!; + return (__pigeon_replyList[0] as Uint8List?)!; } } - Future readCharacteristic(int arg_peripheralHashCodeArgs, int arg_characteristicHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readCharacteristic', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_characteristicHashCodeArgs]) as List?; - if (replyList == null) { + Future writeCharacteristic(String addressArgs, int hashCodeArgs, Uint8List valueArgs, int typeNumberArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.writeCharacteristic'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, hashCodeArgs, valueArgs, typeNumberArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as Uint8List?)!; - } - } - - Future writeCharacteristic(int arg_peripheralHashCodeArgs, int arg_characteristicHashCodeArgs, Uint8List arg_valueArgs, int arg_typeNumberArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.writeCharacteristic', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_characteristicHashCodeArgs, arg_valueArgs, arg_typeNumberArgs]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future notifyCharacteristic(int arg_peripheralHashCodeArgs, int arg_characteristicHashCodeArgs, bool arg_stateArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.notifyCharacteristic', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_characteristicHashCodeArgs, arg_stateArgs]) as List?; - if (replyList == null) { + Future setCharacteristicNotifyState(String addressArgs, int hashCodeArgs, int stateNumberArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setCharacteristicNotifyState'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, hashCodeArgs, stateNumberArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future readDescriptor(int arg_peripheralHashCodeArgs, int arg_descriptorHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readDescriptor', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_descriptorHashCodeArgs]) as List?; - if (replyList == null) { + Future readDescriptor(String addressArgs, int hashCodeArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readDescriptor'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, hashCodeArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as Uint8List?)!; + return (__pigeon_replyList[0] as Uint8List?)!; } } - Future writeDescriptor(int arg_peripheralHashCodeArgs, int arg_descriptorHashCodeArgs, Uint8List arg_valueArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.writeDescriptor', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_descriptorHashCodeArgs, arg_valueArgs]) as List?; - if (replyList == null) { + Future writeDescriptor(String addressArgs, int hashCodeArgs, Uint8List valueArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.writeDescriptor'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, hashCodeArgs, valueArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; @@ -693,17 +631,11 @@ class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { if (value is MyAdvertisementArgs) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is MyGattCharacteristicArgs) { + } else if (value is MyManufacturerSpecificDataArgs) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is MyGattDescriptorArgs) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is MyManufacturerSpecificDataArgs) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); } else if (value is MyPeripheralArgs) { - buffer.putUint8(132); + buffer.putUint8(130); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -716,12 +648,8 @@ class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { case 128: return MyAdvertisementArgs.decode(readValue(buffer)!); case 129: - return MyGattCharacteristicArgs.decode(readValue(buffer)!); - case 130: - return MyGattDescriptorArgs.decode(readValue(buffer)!); - case 131: return MyManufacturerSpecificDataArgs.decode(readValue(buffer)!); - case 132: + case 130: return MyPeripheralArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -730,25 +658,27 @@ class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { } abstract class MyCentralManagerFlutterApi { - static const MessageCodec codec = _MyCentralManagerFlutterApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyCentralManagerFlutterApiCodec(); void onStateChanged(int stateNumberArgs); void onDiscovered(MyPeripheralArgs peripheralArgs, int rssiArgs, MyAdvertisementArgs advertisementArgs); - void onPeripheralStateChanged(MyPeripheralArgs peripheralArgs, bool stateArgs); + void onConnectionStateChanged(String addressArgs, bool stateArgs); - void onCharacteristicValueChanged(MyGattCharacteristicArgs characteristicArgs, Uint8List valueArgs); + void onMtuChanged(String addressArgs, int mtuArgs); + + void onCharacteristicNotified(String addressArgs, int hashCodeArgs, Uint8List valueArgs); static void setup(MyCentralManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onStateChanged', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onStateChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onStateChanged was null.'); final List args = (message as List?)!; @@ -767,13 +697,13 @@ abstract class MyCentralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onDiscovered', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onDiscovered', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onDiscovered was null.'); final List args = (message as List?)!; @@ -798,24 +728,24 @@ abstract class MyCentralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onPeripheralStateChanged', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onConnectionStateChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onPeripheralStateChanged was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onConnectionStateChanged was null.'); final List args = (message as List?)!; - final MyPeripheralArgs? arg_peripheralArgs = (args[0] as MyPeripheralArgs?); - assert(arg_peripheralArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onPeripheralStateChanged was null, expected non-null MyPeripheralArgs.'); + final String? arg_addressArgs = (args[0] as String?); + assert(arg_addressArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onConnectionStateChanged was null, expected non-null String.'); final bool? arg_stateArgs = (args[1] as bool?); assert(arg_stateArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onPeripheralStateChanged was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onConnectionStateChanged was null, expected non-null bool.'); try { - api.onPeripheralStateChanged(arg_peripheralArgs!, arg_stateArgs!); + api.onConnectionStateChanged(arg_addressArgs!, arg_stateArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -826,24 +756,55 @@ abstract class MyCentralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicValueChanged', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onMtuChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicValueChanged was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onMtuChanged was null.'); final List args = (message as List?)!; - final MyGattCharacteristicArgs? arg_characteristicArgs = (args[0] as MyGattCharacteristicArgs?); - assert(arg_characteristicArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicValueChanged was null, expected non-null MyGattCharacteristicArgs.'); - final Uint8List? arg_valueArgs = (args[1] as Uint8List?); - assert(arg_valueArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicValueChanged was null, expected non-null Uint8List.'); + final String? arg_addressArgs = (args[0] as String?); + assert(arg_addressArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onMtuChanged was null, expected non-null String.'); + final int? arg_mtuArgs = (args[1] as int?); + assert(arg_mtuArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onMtuChanged was null, expected non-null int.'); try { - api.onCharacteristicValueChanged(arg_characteristicArgs!, arg_valueArgs!); + api.onMtuChanged(arg_addressArgs!, arg_mtuArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicNotified', 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.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicNotified was null.'); + final List args = (message as List?)!; + final String? arg_addressArgs = (args[0] as String?); + assert(arg_addressArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicNotified was null, expected non-null String.'); + final int? arg_hashCodeArgs = (args[1] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicNotified was null, expected non-null int.'); + final Uint8List? arg_valueArgs = (args[2] as Uint8List?); + assert(arg_valueArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicNotified was null, expected non-null Uint8List.'); + try { + api.onCharacteristicNotified(arg_addressArgs!, arg_hashCodeArgs!, arg_valueArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -875,9 +836,6 @@ class _MyPeripheralManagerHostApiCodec extends StandardMessageCodec { } else if (value is MyManufacturerSpecificDataArgs) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is MyPeripheralManagerArgs) { - buffer.putUint8(133); - writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -896,8 +854,6 @@ class _MyPeripheralManagerHostApiCodec extends StandardMessageCodec { return MyGattServiceArgs.decode(readValue(buffer)!); case 132: return MyManufacturerSpecificDataArgs.decode(readValue(buffer)!); - case 133: - return MyPeripheralManagerArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -909,76 +865,71 @@ class MyPeripheralManagerHostApi { /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. MyPeripheralManagerHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec codec = _MyPeripheralManagerHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerHostApiCodec(); - Future setUp() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.setUp', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + Future setUp() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.setUp'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as MyPeripheralManagerArgs?)!; - } - } - - Future addService(MyGattServiceArgs arg_serviceArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.addService', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_serviceArgs]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future removeService(int arg_serviceHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.removeService', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_serviceHashCodeArgs]) as List?; - if (replyList == null) { + Future addService(MyGattServiceArgs serviceArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.addService'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([serviceArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { + } else { + return; + } + } + + Future removeService(int hashCodeArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.removeService'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([hashCodeArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; @@ -986,43 +937,43 @@ class MyPeripheralManagerHostApi { } Future clearServices() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.clearServices', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.clearServices'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future startAdvertising(MyAdvertisementArgs arg_advertisementArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.startAdvertising', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_advertisementArgs]) as List?; - if (replyList == null) { + Future startAdvertising(MyAdvertisementArgs advertisementArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.startAdvertising'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([advertisementArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; @@ -1030,114 +981,65 @@ class MyPeripheralManagerHostApi { } Future stopAdvertising() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.stopAdvertising', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.stopAdvertising'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future getMaximumWriteLength(int arg_centralHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.getMaximumWriteLength', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_centralHashCodeArgs]) as List?; - if (replyList == null) { + Future sendResponse(String addressArgs, int idArgs, int statusNumberArgs, int offsetArgs, Uint8List? valueArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.sendResponse'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, idArgs, statusNumberArgs, offsetArgs, valueArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as int?)!; - } - } - - Future sendReadCharacteristicReply(int arg_centralHashCodeArgs, int arg_characteristicHashCodeArgs, int arg_idArgs, int arg_offsetArgs, bool arg_statusArgs, Uint8List arg_valueArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.sendReadCharacteristicReply', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_centralHashCodeArgs, arg_characteristicHashCodeArgs, arg_idArgs, arg_offsetArgs, arg_statusArgs, arg_valueArgs]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future sendWriteCharacteristicReply(int arg_centralHashCodeArgs, int arg_characteristicHashCodeArgs, int arg_idArgs, int arg_offsetArgs, bool arg_statusArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.sendWriteCharacteristicReply', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_centralHashCodeArgs, arg_characteristicHashCodeArgs, arg_idArgs, arg_offsetArgs, arg_statusArgs]) as List?; - if (replyList == null) { + Future notifyCharacteristicChanged(int hashCodeArgs, Uint8List valueArgs, bool confirmArgs, String addressArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.notifyCharacteristicChanged'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([hashCodeArgs, valueArgs, confirmArgs, addressArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } - } - - Future notifyCharacteristicValueChanged(int arg_centralHashCodeArgs, int arg_characteristicHashCodeArgs, Uint8List arg_valueArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.notifyCharacteristicValueChanged', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_centralHashCodeArgs, arg_characteristicHashCodeArgs, arg_valueArgs]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; @@ -1152,12 +1054,6 @@ class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { if (value is MyCentralArgs) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is MyGattCharacteristicArgs) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is MyGattDescriptorArgs) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -1168,10 +1064,6 @@ class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { switch (type) { case 128: return MyCentralArgs.decode(readValue(buffer)!); - case 129: - return MyGattCharacteristicArgs.decode(readValue(buffer)!); - case 130: - return MyGattDescriptorArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -1179,25 +1071,31 @@ class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { } abstract class MyPeripheralManagerFlutterApi { - static const MessageCodec codec = _MyPeripheralManagerFlutterApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerFlutterApiCodec(); void onStateChanged(int stateNumberArgs); - void onReadCharacteristicCommandReceived(MyCentralArgs centralArgs, MyGattCharacteristicArgs characteristicArgs, int idArgs, int offsetArgs); + void onConnectionStateChanged(MyCentralArgs centralArgs, bool stateArgs); - void onWriteCharacteristicCommandReceived(MyCentralArgs centralArgs, MyGattCharacteristicArgs characteristicArgs, int idArgs, int offsetArgs, Uint8List valueArgs); + void onMtuChanged(String addressArgs, int mtuArgs); - void onNotifyCharacteristicCommandReceived(MyCentralArgs centralArgs, MyGattCharacteristicArgs characteristicArgs, bool stateArgs); + void onCharacteristicReadRequest(String addressArgs, int hashCodeArgs, int idArgs, int offsetArgs); + + void onCharacteristicWriteRequest(String addressArgs, int hashCodeArgs, int idArgs, int offsetArgs, Uint8List valueArgs, bool preparedWriteArgs, bool responseNeededArgs); + + void onExecuteWrite(String addressArgs, int idArgs, bool executeArgs); + + void onCharacteristicNotifyStateChanged(String addressArgs, int hashCodeArgs, int stateNumberArgs); static void setup(MyPeripheralManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onStateChanged', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onStateChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onStateChanged was null.'); final List args = (message as List?)!; @@ -1216,30 +1114,24 @@ abstract class MyPeripheralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onConnectionStateChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onConnectionStateChanged was null.'); final List args = (message as List?)!; final MyCentralArgs? arg_centralArgs = (args[0] as MyCentralArgs?); assert(arg_centralArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null, expected non-null MyCentralArgs.'); - final MyGattCharacteristicArgs? arg_characteristicArgs = (args[1] as MyGattCharacteristicArgs?); - assert(arg_characteristicArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null, expected non-null MyGattCharacteristicArgs.'); - final int? arg_idArgs = (args[2] as int?); - assert(arg_idArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null, expected non-null int.'); - final int? arg_offsetArgs = (args[3] as int?); - assert(arg_offsetArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onConnectionStateChanged was null, expected non-null MyCentralArgs.'); + final bool? arg_stateArgs = (args[1] as bool?); + assert(arg_stateArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onConnectionStateChanged was null, expected non-null bool.'); try { - api.onReadCharacteristicCommandReceived(arg_centralArgs!, arg_characteristicArgs!, arg_idArgs!, arg_offsetArgs!); + api.onConnectionStateChanged(arg_centralArgs!, arg_stateArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1250,33 +1142,101 @@ abstract class MyPeripheralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onMtuChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onMtuChanged was null.'); final List args = (message as List?)!; - final MyCentralArgs? arg_centralArgs = (args[0] as MyCentralArgs?); - assert(arg_centralArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null MyCentralArgs.'); - final MyGattCharacteristicArgs? arg_characteristicArgs = (args[1] as MyGattCharacteristicArgs?); - assert(arg_characteristicArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null MyGattCharacteristicArgs.'); + final String? arg_addressArgs = (args[0] as String?); + assert(arg_addressArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onMtuChanged was null, expected non-null String.'); + final int? arg_mtuArgs = (args[1] as int?); + assert(arg_mtuArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onMtuChanged was null, expected non-null int.'); + try { + api.onMtuChanged(arg_addressArgs!, arg_mtuArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest', 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.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was null.'); + final List args = (message as List?)!; + final String? arg_addressArgs = (args[0] as String?); + assert(arg_addressArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was null, expected non-null String.'); + final int? arg_hashCodeArgs = (args[1] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was null, expected non-null int.'); final int? arg_idArgs = (args[2] as int?); assert(arg_idArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was null, expected non-null int.'); final int? arg_offsetArgs = (args[3] as int?); assert(arg_offsetArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was null, expected non-null int.'); + try { + api.onCharacteristicReadRequest(arg_addressArgs!, arg_hashCodeArgs!, arg_idArgs!, arg_offsetArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest', 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.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null.'); + final List args = (message as List?)!; + final String? arg_addressArgs = (args[0] as String?); + assert(arg_addressArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null String.'); + final int? arg_hashCodeArgs = (args[1] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null int.'); + final int? arg_idArgs = (args[2] as int?); + assert(arg_idArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null int.'); + final int? arg_offsetArgs = (args[3] as int?); + assert(arg_offsetArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null int.'); final Uint8List? arg_valueArgs = (args[4] as Uint8List?); assert(arg_valueArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null Uint8List.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null Uint8List.'); + final bool? arg_preparedWriteArgs = (args[5] as bool?); + assert(arg_preparedWriteArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null bool.'); + final bool? arg_responseNeededArgs = (args[6] as bool?); + assert(arg_responseNeededArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null bool.'); try { - api.onWriteCharacteristicCommandReceived(arg_centralArgs!, arg_characteristicArgs!, arg_idArgs!, arg_offsetArgs!, arg_valueArgs!); + api.onCharacteristicWriteRequest(arg_addressArgs!, arg_hashCodeArgs!, arg_idArgs!, arg_offsetArgs!, arg_valueArgs!, arg_preparedWriteArgs!, arg_responseNeededArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1287,27 +1247,58 @@ abstract class MyPeripheralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onExecuteWrite', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onExecuteWrite was null.'); final List args = (message as List?)!; - final MyCentralArgs? arg_centralArgs = (args[0] as MyCentralArgs?); - assert(arg_centralArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived was null, expected non-null MyCentralArgs.'); - final MyGattCharacteristicArgs? arg_characteristicArgs = (args[1] as MyGattCharacteristicArgs?); - assert(arg_characteristicArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived was null, expected non-null MyGattCharacteristicArgs.'); - final bool? arg_stateArgs = (args[2] as bool?); - assert(arg_stateArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived was null, expected non-null bool.'); + final String? arg_addressArgs = (args[0] as String?); + assert(arg_addressArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onExecuteWrite was null, expected non-null String.'); + final int? arg_idArgs = (args[1] as int?); + assert(arg_idArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onExecuteWrite was null, expected non-null int.'); + final bool? arg_executeArgs = (args[2] as bool?); + assert(arg_executeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onExecuteWrite was null, expected non-null bool.'); try { - api.onNotifyCharacteristicCommandReceived(arg_centralArgs!, arg_characteristicArgs!, arg_stateArgs!); + api.onExecuteWrite(arg_addressArgs!, arg_idArgs!, arg_executeArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged', 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.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null.'); + final List args = (message as List?)!; + final String? arg_addressArgs = (args[0] as String?); + assert(arg_addressArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null, expected non-null String.'); + final int? arg_hashCodeArgs = (args[1] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null, expected non-null int.'); + final int? arg_stateNumberArgs = (args[2] as int?); + assert(arg_stateNumberArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null, expected non-null int.'); + try { + api.onCharacteristicNotifyStateChanged(arg_addressArgs!, arg_hashCodeArgs!, arg_stateNumberArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/bluetooth_low_energy_android/lib/src/my_central2.dart b/bluetooth_low_energy_android/lib/src/my_central2.dart new file mode 100644 index 0000000..e569b98 --- /dev/null +++ b/bluetooth_low_energy_android/lib/src/my_central2.dart @@ -0,0 +1,11 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +class MyCentral2 extends MyCentral { + final String address; + + MyCentral2({ + required this.address, + }) : super( + uuid: UUID.fromAddress(address), + ); +} diff --git a/bluetooth_low_energy_android/lib/src/my_central_manager.dart b/bluetooth_low_energy_android/lib/src/my_central_manager.dart new file mode 100644 index 0000000..a9c285e --- /dev/null +++ b/bluetooth_low_energy_android/lib/src/my_central_manager.dart @@ -0,0 +1,341 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:flutter/foundation.dart'; + +import 'my_api.dart'; +import 'my_gatt_characteristic2.dart'; +import 'my_gatt_descriptor2.dart'; +import 'my_peripheral2.dart'; + +class MyCentralManager extends CentralManager + implements MyCentralManagerFlutterApi { + final MyCentralManagerHostApi _api; + final StreamController + _stateChangedController; + final StreamController _discoveredController; + final StreamController + _connectionStateChangedController; + final StreamController + _characteristicNotifiedController; + + final Map _peripherals; + final Map> _characteristics; + final Map _mtus; + + BluetoothLowEnergyState _state; + + MyCentralManager() + : _api = MyCentralManagerHostApi(), + _stateChangedController = StreamController.broadcast(), + _discoveredController = StreamController.broadcast(), + _connectionStateChangedController = StreamController.broadcast(), + _characteristicNotifiedController = StreamController.broadcast(), + _peripherals = {}, + _characteristics = {}, + _mtus = {}, + _state = BluetoothLowEnergyState.unknown; + + @override + Stream get stateChanged => + _stateChangedController.stream; + @override + Stream get discovered => _discoveredController.stream; + @override + Stream get connectionStateChanged => + _connectionStateChangedController.stream; + @override + Stream get characteristicNotified => + _characteristicNotifiedController.stream; + + @override + Future setUp() async { + logger.info('setUp'); + await _api.setUp(); + MyCentralManagerFlutterApi.setup(this); + } + + @override + Future getState() { + logger.info('getState'); + return Future.value(_state); + } + + @override + Future startDiscovery() async { + logger.info('startDiscovery'); + await _api.startDiscovery(); + } + + @override + Future stopDiscovery() async { + logger.info('stopDiscovery'); + await _api.stopDiscovery(); + } + + @override + Future connect(Peripheral peripheral) async { + if (peripheral is! MyPeripheral2) { + throw TypeError(); + } + final addressArgs = peripheral.address; + logger.info('connect: $addressArgs'); + await _api.connect(addressArgs); + try { + await _api.requestMTU(addressArgs, 517); + } catch (error, stackTrace) { + // 忽略协商 MTU 错误 + logger.warning('requstMTU failed.', error, stackTrace); + } + } + + @override + Future disconnect(Peripheral peripheral) async { + if (peripheral is! MyPeripheral2) { + throw TypeError(); + } + final addressArgs = peripheral.address; + logger.info('disconnect: $addressArgs'); + await _api.disconnect(addressArgs); + } + + @override + Future readRSSI(Peripheral peripheral) async { + if (peripheral is! MyPeripheral2) { + throw TypeError(); + } + final addressArgs = peripheral.address; + logger.info('readRSSI: $addressArgs'); + final rssi = await _api.readRSSI(addressArgs); + return rssi; + } + + @override + Future> discoverGATT(Peripheral peripheral) async { + if (peripheral is! MyPeripheral2) { + throw TypeError(); + } + final addressArgs = peripheral.address; + logger.info('discoverServices: $addressArgs'); + final servicesArgs = await _api.discoverServices(addressArgs); + final services = servicesArgs + .cast() + .map((args) => args.toService2(peripheral)) + .toList(); + final characteristics = + services.expand((service) => service.characteristics).toList(); + _characteristics[addressArgs] = { + for (var characteristic in characteristics) + characteristic.hashCode: characteristic + }; + return services; + } + + @override + Future readCharacteristic( + GattCharacteristic characteristic, + ) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final peripheral = characteristic.peripheral; + final addressArgs = peripheral.address; + final hashCodeArgs = characteristic.hashCode; + logger.info('readCharacteristic: $addressArgs.$hashCodeArgs'); + final value = await _api.readCharacteristic(addressArgs, hashCodeArgs); + return value; + } + + @override + Future writeCharacteristic( + GattCharacteristic characteristic, { + required Uint8List value, + required GattCharacteristicWriteType type, + }) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final peripheral = characteristic.peripheral; + final addressArgs = peripheral.address; + final hashCodeArgs = characteristic.hashCode; + final trimmedValueArgs = value.trimGATT(); + final typeArgs = type.toArgs(); + final typeNumberArgs = typeArgs.index; + // When write without response, fragments the value by MTU - 3 size. + // If mtu is null, use 23 as default MTU size. + if (type == GattCharacteristicWriteType.withResponse) { + logger.info( + 'writeCharacteristic: $addressArgs.$hashCodeArgs - $trimmedValueArgs, $typeArgs'); + await _api.writeCharacteristic( + addressArgs, + hashCodeArgs, + trimmedValueArgs, + typeNumberArgs, + ); + } else { + final mtu = _mtus[addressArgs] ?? 23; + final fragmentSize = (mtu - 3).clamp(20, 512); + var start = 0; + while (start < trimmedValueArgs.length) { + final end = start + fragmentSize; + final fragmentedValueArgs = end < trimmedValueArgs.length + ? trimmedValueArgs.sublist(start, end) + : trimmedValueArgs.sublist(start); + logger.info( + 'writeCharacteristic: $addressArgs.$hashCodeArgs - $fragmentedValueArgs, $typeArgs'); + await _api.writeCharacteristic( + addressArgs, + hashCodeArgs, + fragmentedValueArgs, + typeNumberArgs, + ); + start = end; + } + } + } + + @override + Future setCharacteristicNotifyState( + GattCharacteristic characteristic, { + required bool state, + }) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final peripheral = characteristic.peripheral; + final addressArgs = peripheral.address; + final hashCodeArgs = characteristic.hashCode; + final stateArgs = state + ? characteristic.properties.contains(GattCharacteristicProperty.notify) + ? MyGattCharacteristicNotifyStateArgs.notify + : MyGattCharacteristicNotifyStateArgs.indicate + : MyGattCharacteristicNotifyStateArgs.none; + final stateNumberArgs = stateArgs.index; + logger.info( + 'setCharacteristicNotifyState: $addressArgs.$hashCodeArgs - $stateArgs'); + await _api.setCharacteristicNotifyState( + addressArgs, + hashCodeArgs, + stateNumberArgs, + ); + } + + @override + Future readDescriptor(GattDescriptor descriptor) async { + if (descriptor is! MyGattDescriptor2) { + throw TypeError(); + } + final peripheral = descriptor.peripheral; + final addressArgs = peripheral.address; + final hashCodeArgs = descriptor.hashCode; + logger.info('readDescriptor: $addressArgs.$hashCodeArgs'); + final value = await _api.readDescriptor(addressArgs, hashCodeArgs); + return value; + } + + @override + Future writeDescriptor( + GattDescriptor descriptor, { + required Uint8List value, + }) async { + if (descriptor is! MyGattDescriptor2) { + throw TypeError(); + } + final peripheral = descriptor.peripheral; + final addressArgs = peripheral.address; + final hashCodeArgs = descriptor.hashCode; + final trimmedValueArgs = value.trimGATT(); + logger.info( + 'writeDescriptor: $addressArgs.$hashCodeArgs - $trimmedValueArgs'); + await _api.writeDescriptor(addressArgs, hashCodeArgs, trimmedValueArgs); + } + + @override + void onStateChanged(int stateNumberArgs) { + final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; + logger.info('onStateChanged: $stateArgs'); + final state = stateArgs.toState(); + if (_state == state) { + return; + } + _state = state; + final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); + _stateChangedController.add(eventArgs); + } + + @override + void onDiscovered( + MyPeripheralArgs peripheralArgs, + int rssiArgs, + MyAdvertisementArgs advertisementArgs, + ) { + final addressArgs = peripheralArgs.addressArgs; + logger.info('onDiscovered: $addressArgs - $rssiArgs, $advertisementArgs'); + final peripheral = _peripherals.putIfAbsent( + peripheralArgs.addressArgs, + () => peripheralArgs.toPeripheral(), + ); + final rssi = rssiArgs; + final advertisement = advertisementArgs.toAdvertisement(); + final eventArgs = DiscoveredEventArgs( + peripheral, + rssi, + advertisement, + ); + _discoveredController.add(eventArgs); + } + + @override + void onConnectionStateChanged(String addressArgs, bool stateArgs) { + logger.info('onConnectionStateChanged: $addressArgs - $stateArgs'); + final peripheral = _peripherals[addressArgs]; + if (peripheral == null) { + return; + } + final state = stateArgs; + final eventArgs = ConnectionStateChangedEventArgs(peripheral, state); + _connectionStateChangedController.add(eventArgs); + if (!state) { + _characteristics.remove(addressArgs); + _mtus.remove(addressArgs); + } + } + + @override + void onMtuChanged(String addressArgs, int mtuArgs) { + logger.info('onMtuChanged: $addressArgs - $mtuArgs'); + final mtu = mtuArgs; + _mtus[addressArgs] = mtu; + } + + @override + void onCharacteristicNotified( + String addressArgs, + int hashCodeArgs, + Uint8List valueArgs, + ) { + logger.info( + 'onCharacteristicNotified: $addressArgs.$hashCodeArgs - $valueArgs'); + final characteristic = _retrieveCharacteristic(addressArgs, hashCodeArgs); + if (characteristic == null) { + return; + } + final value = valueArgs; + final eventArgs = GattCharacteristicNotifiedEventArgs( + characteristic, + value, + ); + _characteristicNotifiedController.add(eventArgs); + } + + MyGattCharacteristic2? _retrieveCharacteristic( + String addressArgs, + int hashCodeArgs, + ) { + final characteristics = _characteristics[addressArgs]; + if (characteristics == null) { + return null; + } + return characteristics[hashCodeArgs]; + } +} diff --git a/bluetooth_low_energy_android/lib/src/my_central_manager2.dart b/bluetooth_low_energy_android/lib/src/my_central_manager2.dart deleted file mode 100644 index dd66c07..0000000 --- a/bluetooth_low_energy_android/lib/src/my_central_manager2.dart +++ /dev/null @@ -1,308 +0,0 @@ -import 'dart:async'; - -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:flutter/foundation.dart'; - -import 'my_api.dart'; -import 'my_gatt_characteristic2.dart'; -import 'my_gatt_descriptor2.dart'; - -class MyCentralManager2 extends MyCentralManager - implements MyCentralManagerFlutterApi { - final MyCentralManagerHostApi _api; - BluetoothLowEnergyState _state; - final StreamController - _stateChangedController; - final StreamController _discoveredController; - final StreamController - _peripheralStateChangedController; - final StreamController - _characteristicValueChangedController; - - MyCentralManager2() - : _api = MyCentralManagerHostApi(), - _state = BluetoothLowEnergyState.unknown, - _stateChangedController = StreamController.broadcast(), - _discoveredController = StreamController.broadcast(), - _peripheralStateChangedController = StreamController.broadcast(), - _characteristicValueChangedController = StreamController.broadcast(); - - @override - BluetoothLowEnergyState get state => _state; - @protected - set state(BluetoothLowEnergyState value) { - if (_state == value) { - return; - } - _state = value; - final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); - _stateChangedController.add(eventArgs); - } - - @override - Stream get stateChanged => - _stateChangedController.stream; - @override - Stream get discovered => _discoveredController.stream; - @override - Stream get peripheralStateChanged => - _peripheralStateChangedController.stream; - @override - Stream - get characteristicValueChanged => - _characteristicValueChangedController.stream; - - Future _throwWithoutState(BluetoothLowEnergyState state) async { - if (this.state != state) { - throw StateError( - '$state is expected, but current state is ${this.state}.'); - } - } - - @override - Future setUp() async { - final args = await _api.setUp(); - final stateArgs = - MyBluetoothLowEnergyStateArgs.values[args.stateNumberArgs]; - state = stateArgs.toState(); - MyCentralManagerFlutterApi.setup(this); - } - - @override - Future startDiscovery() async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - await _api.startDiscovery(); - } - - @override - Future stopDiscovery() async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - await _api.stopDiscovery(); - } - - @override - Future connect(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final peripheralHashCodeArgs = peripheral.hashCode; - await _api.connect(peripheralHashCodeArgs); - } - - @override - Future disconnect(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final peripheralHashCodeArgs = peripheral.hashCode; - await _api.disconnect(peripheralHashCodeArgs); - } - - @override - Future getMaximumWriteLength( - Peripheral peripheral, { - required GattCharacteristicWriteType type, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final peripheralHashCodeArgs = peripheral.hashCode; - final typeArgs = type.toArgs(); - final typeNumberArgs = typeArgs.index; - final maximumWriteLength = await _api.getMaximumWriteLength( - peripheralHashCodeArgs, - typeNumberArgs, - ); - return maximumWriteLength; - } - - @override - Future readRSSI(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final peripheralHashCodeArgs = peripheral.hashCode; - final rssi = await _api.readRSSI(peripheralHashCodeArgs); - return rssi; - } - - @override - Future> discoverGATT(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (peripheral is! MyPeripheral) { - throw TypeError(); - } - final peripheralHashCodeArgs = peripheral.hashCode; - final servicesArgs = await _api.discoverGATT(peripheralHashCodeArgs); - final services = servicesArgs - .cast() - .map((args) => args.toService2()) - .toList(); - for (var service in services) { - for (var charactersitic in service.characteristics) { - for (var descriptor in charactersitic.descriptors) { - descriptor.characteristic = charactersitic; - } - charactersitic.service = service; - } - service.peripheral = peripheral; - } - try { - // 部分外围设备连接后会触发 onMtuChanged 回调,若在此之前调用协商 MTU 的方法,会在协商完成前返回, - // 此时如果继续调用其他方法(如发现服务)会导致回调无法触发, - // 因此为避免此情况发生,需要延迟到发现服务完成后再协商 MTU。 - // TODO: 思考更好的解决方式,可以在连接后立即协商 MTU。 - const mtuArgs = 517; - await _api.requestMTU(peripheralHashCodeArgs, mtuArgs); - } catch (error, stackTrace) { - // 忽略协商 MTU 错误 - logger.warning('requst MTU failed.', error, stackTrace); - } - return services; - } - - @override - Future readCharacteristic( - GattCharacteristic characteristic, - ) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (characteristic is! MyGattCharacteristic2) { - throw TypeError(); - } - final service = characteristic.service; - final peripheral = service.peripheral; - final peripheralHashCodeArgs = peripheral.hashCode; - final characteristcHashCodeArgs = characteristic.hashCode; - final value = await _api.readCharacteristic( - peripheralHashCodeArgs, - characteristcHashCodeArgs, - ); - return value; - } - - @override - Future writeCharacteristic( - GattCharacteristic characteristic, { - required Uint8List value, - required GattCharacteristicWriteType type, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (characteristic is! MyGattCharacteristic2) { - throw TypeError(); - } - final service = characteristic.service; - final peripheral = service.peripheral; - final peripheralHashCodeArgs = peripheral.hashCode; - final characteristcHashCodeArgs = characteristic.hashCode; - final valueArgs = value; - final typeArgs = type.toArgs(); - final typeNumberArgs = typeArgs.index; - await _api.writeCharacteristic( - peripheralHashCodeArgs, - characteristcHashCodeArgs, - valueArgs, - typeNumberArgs, - ); - } - - @override - Future notifyCharacteristic( - GattCharacteristic characteristic, { - required bool state, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (characteristic is! MyGattCharacteristic2) { - throw TypeError(); - } - final service = characteristic.service; - final peripheral = service.peripheral; - final peripheralHashCodeArgs = peripheral.hashCode; - final characteristcHashCodeArgs = characteristic.hashCode; - final stateArgs = state; - await _api.notifyCharacteristic( - peripheralHashCodeArgs, - characteristcHashCodeArgs, - stateArgs, - ); - } - - @override - Future readDescriptor(GattDescriptor descriptor) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (descriptor is! MyGattDescriptor2) { - throw TypeError(); - } - final characteristic = descriptor.characteristic; - final service = characteristic.service; - final peripheral = service.peripheral; - final peripheralHashCodeArgs = peripheral.hashCode; - final descriptorHashCodeArgs = descriptor.hashCode; - final value = await _api.readDescriptor( - peripheralHashCodeArgs, - descriptorHashCodeArgs, - ); - return value; - } - - @override - Future writeDescriptor( - GattDescriptor descriptor, { - required Uint8List value, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (descriptor is! MyGattDescriptor2) { - throw TypeError(); - } - final characteristic = descriptor.characteristic; - final service = characteristic.service; - final peripheral = service.peripheral; - final peripheralHashCodeArgs = peripheral.hashCode; - final descriptorHashCodeArgs = descriptor.hashCode; - final valueArgs = value; - await _api.writeDescriptor( - peripheralHashCodeArgs, - descriptorHashCodeArgs, - valueArgs, - ); - } - - @override - void onStateChanged(int stateNumberArgs) { - final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; - state = stateArgs.toState(); - } - - @override - void onDiscovered( - MyPeripheralArgs peripheralArgs, - int rssiArgs, - MyAdvertisementArgs advertisementArgs, - ) { - final peripheral = peripheralArgs.toPeripheral(); - final rssi = rssiArgs; - final advertisement = advertisementArgs.toAdvertisement(); - final eventArgs = DiscoveredEventArgs( - peripheral, - rssi, - advertisement, - ); - _discoveredController.add(eventArgs); - } - - @override - void onPeripheralStateChanged( - MyPeripheralArgs peripheralArgs, - bool stateArgs, - ) { - final peripheral = peripheralArgs.toPeripheral(); - final state = stateArgs; - final eventArgs = PeripheralStateChangedEventArgs(peripheral, state); - _peripheralStateChangedController.add(eventArgs); - } - - @override - void onCharacteristicValueChanged( - MyGattCharacteristicArgs characteristicArgs, - Uint8List valueArgs, - ) { - final characteristic = characteristicArgs.toCharacteristic2(); - final value = valueArgs; - final eventArgs = GattCharacteristicValueChangedEventArgs( - characteristic, - value, - ); - _characteristicValueChangedController.add(eventArgs); - } -} diff --git a/bluetooth_low_energy_android/lib/src/my_gatt_characteristic2.dart b/bluetooth_low_energy_android/lib/src/my_gatt_characteristic2.dart index b8c8a15..5de360d 100644 --- a/bluetooth_low_energy_android/lib/src/my_gatt_characteristic2.dart +++ b/bluetooth_low_energy_android/lib/src/my_gatt_characteristic2.dart @@ -1,13 +1,16 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'my_gatt_descriptor2.dart'; -import 'my_gatt_service2.dart'; +import 'my_peripheral2.dart'; class MyGattCharacteristic2 extends MyGattCharacteristic { - late final MyGattService2 service; + final MyPeripheral2 peripheral; + @override + final int hashCode; MyGattCharacteristic2({ - super.hashCode, + required this.peripheral, + required this.hashCode, required super.uuid, required super.properties, required List descriptors, @@ -16,4 +19,11 @@ class MyGattCharacteristic2 extends MyGattCharacteristic { @override List get descriptors => super.descriptors.cast(); + + @override + bool operator ==(Object other) { + return other is MyGattCharacteristic2 && + other.peripheral == peripheral && + other.hashCode == hashCode; + } } diff --git a/bluetooth_low_energy_android/lib/src/my_gatt_descriptor2.dart b/bluetooth_low_energy_android/lib/src/my_gatt_descriptor2.dart index 5bc7dc4..30bc7d7 100644 --- a/bluetooth_low_energy_android/lib/src/my_gatt_descriptor2.dart +++ b/bluetooth_low_energy_android/lib/src/my_gatt_descriptor2.dart @@ -1,12 +1,22 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'my_gatt_characteristic2.dart'; +import 'my_peripheral2.dart'; class MyGattDescriptor2 extends MyGattDescriptor { - late final MyGattCharacteristic2 characteristic; + final MyPeripheral2 peripheral; + @override + final int hashCode; MyGattDescriptor2({ - super.hashCode, + required this.peripheral, + required this.hashCode, required super.uuid, }); + + @override + bool operator ==(Object other) { + return other is MyGattDescriptor2 && + other.peripheral == peripheral && + other.hashCode == hashCode; + } } diff --git a/bluetooth_low_energy_android/lib/src/my_gatt_service2.dart b/bluetooth_low_energy_android/lib/src/my_gatt_service2.dart index ac74a6d..e1d365f 100644 --- a/bluetooth_low_energy_android/lib/src/my_gatt_service2.dart +++ b/bluetooth_low_energy_android/lib/src/my_gatt_service2.dart @@ -1,12 +1,16 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'my_gatt_characteristic2.dart'; +import 'my_peripheral2.dart'; class MyGattService2 extends MyGattService { - late final MyPeripheral peripheral; + final MyPeripheral2 peripheral; + @override + final int hashCode; MyGattService2({ - super.hashCode, + required this.peripheral, + required this.hashCode, required super.uuid, required List characteristics, }) : super(characteristics: characteristics); @@ -14,4 +18,11 @@ class MyGattService2 extends MyGattService { @override List get characteristics => super.characteristics.cast(); + + @override + bool operator ==(Object other) { + return other is MyGattService2 && + other.peripheral == peripheral && + other.hashCode == hashCode; + } } diff --git a/bluetooth_low_energy_android/lib/src/my_peripheral2.dart b/bluetooth_low_energy_android/lib/src/my_peripheral2.dart new file mode 100644 index 0000000..60faa38 --- /dev/null +++ b/bluetooth_low_energy_android/lib/src/my_peripheral2.dart @@ -0,0 +1,11 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +class MyPeripheral2 extends MyPeripheral { + final String address; + + MyPeripheral2({ + required this.address, + }) : super( + uuid: UUID.fromAddress(address), + ); +} diff --git a/bluetooth_low_energy_android/lib/src/my_peripheral_manager.dart b/bluetooth_low_energy_android/lib/src/my_peripheral_manager.dart new file mode 100644 index 0000000..dac84d1 --- /dev/null +++ b/bluetooth_low_energy_android/lib/src/my_peripheral_manager.dart @@ -0,0 +1,413 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:flutter/foundation.dart'; + +import 'my_api.dart'; +import 'my_central2.dart'; + +class MyPeripheralManager extends PeripheralManager + implements MyPeripheralManagerFlutterApi { + final MyPeripheralManagerHostApi _api; + final StreamController + _stateChangedController; + final StreamController + _characteristicReadController; + final StreamController + _characteristicWrittenController; + final StreamController + _characteristicNotifyStateChangedController; + + final Map _centrals; + final Map> _characteristics; + final Map _mtus; + final Map> _confirms; + final Map _preparedCharacteristics; + final Map> _preparedValue; + + BluetoothLowEnergyState _state; + + MyPeripheralManager() + : _api = MyPeripheralManagerHostApi(), + _stateChangedController = StreamController.broadcast(), + _characteristicReadController = StreamController.broadcast(), + _characteristicWrittenController = StreamController.broadcast(), + _characteristicNotifyStateChangedController = + StreamController.broadcast(), + _centrals = {}, + _characteristics = {}, + _mtus = {}, + _confirms = {}, + _preparedCharacteristics = {}, + _preparedValue = {}, + _state = BluetoothLowEnergyState.unknown; + + @override + Stream get stateChanged => + _stateChangedController.stream; + @override + Stream get characteristicRead => + _characteristicReadController.stream; + @override + Stream get characteristicWritten => + _characteristicWrittenController.stream; + @override + Stream + get characteristicNotifyStateChanged => + _characteristicNotifyStateChangedController.stream; + + @override + Future setUp() async { + logger.info('setUp'); + await _api.setUp(); + MyPeripheralManagerFlutterApi.setup(this); + } + + @override + Future getState() { + logger.info('getState'); + return Future.value(_state); + } + + @override + Future addService(GattService service) async { + if (service is! MyGattService) { + throw TypeError(); + } + final serviceArgs = service.toArgs(); + final hashCodeArgs = serviceArgs.hashCodeArgs; + logger.info('addService: $hashCodeArgs'); + await _api.addService(serviceArgs); + _characteristics[hashCodeArgs] = { + for (var characteristics in service.characteristics) + characteristics.hashCode: characteristics + }; + } + + @override + Future removeService(GattService service) async { + final hashCodeArgs = service.hashCode; + logger.info('removeService: $hashCodeArgs'); + await _api.removeService(hashCodeArgs); + _characteristics.remove(hashCodeArgs); + } + + @override + Future clearServices() async { + logger.info('clearServices'); + await _api.clearServices(); + _characteristics.clear(); + } + + @override + Future startAdvertising(Advertisement advertisement) async { + final advertisementArgs = advertisement.toArgs(); + logger.info('startAdvertising: $advertisementArgs'); + await _api.startAdvertising(advertisementArgs); + } + + @override + Future stopAdvertising() async { + logger.info('stopAdvertising'); + await _api.stopAdvertising(); + } + + @override + Future readCharacteristic(GattCharacteristic characteristic) { + if (characteristic is! MyGattCharacteristic) { + throw TypeError(); + } + final hashCodeArgs = characteristic.hashCode; + logger.info('readCharacteristic: $hashCodeArgs'); + final value = characteristic.value; + return Future.value(value); + } + + @override + Future writeCharacteristic( + GattCharacteristic characteristic, { + required Uint8List value, + Central? central, + }) async { + if (characteristic is! MyGattCharacteristic) { + throw TypeError(); + } + characteristic.value = value; + if (central == null) { + return; + } + if (central is! MyCentral2) { + throw TypeError(); + } + final addressArgs = central.address; + final hashCodeArgs = characteristic.hashCode; + final confirm = _retrieveConfirm(addressArgs, hashCodeArgs); + if (confirm == null) { + logger.warning('The central is not listening.'); + return; + } + final trimmedValueArgs = characteristic.value; + // Fragments the value by MTU - 3 size. + // If mtu is null, use 23 as default MTU size. + final mtu = _mtus[addressArgs] ?? 23; + final fragmentSize = (mtu - 3).clamp(20, 512); + var start = 0; + while (start < trimmedValueArgs.length) { + final end = start + fragmentSize; + final fragmentedValueArgs = end < trimmedValueArgs.length + ? trimmedValueArgs.sublist(start, end) + : trimmedValueArgs.sublist(start); + logger.info( + 'notifyCharacteristicChanged: $hashCodeArgs - $fragmentedValueArgs, $confirm, $addressArgs'); + await _api.notifyCharacteristicChanged( + hashCodeArgs, + fragmentedValueArgs, + confirm, + addressArgs, + ); + start = end; + } + } + + @override + void onStateChanged(int stateNumberArgs) { + final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; + logger.info('onStateChanged: $stateArgs'); + final state = stateArgs.toState(); + if (_state == state) { + return; + } + _state = state; + final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); + _stateChangedController.add(eventArgs); + } + + @override + void onConnectionStateChanged(MyCentralArgs centralArgs, bool stateArgs) { + final addressArgs = centralArgs.addressArgs; + logger.info('onConnectionStateChanged: $addressArgs - $stateArgs'); + final central = centralArgs.toCentral(); + final state = stateArgs; + if (state) { + _centrals[addressArgs] = central; + } else { + _centrals.remove(addressArgs); + _mtus.remove(addressArgs); + _confirms.remove(addressArgs); + } + } + + @override + void onMtuChanged(String addressArgs, int mtuArgs) { + logger.info('onMtuChanged: $addressArgs - $mtuArgs'); + final mtu = mtuArgs; + _mtus[addressArgs] = mtu; + } + + @override + void onCharacteristicReadRequest( + String addressArgs, + int hashCodeArgs, + int idArgs, + int offsetArgs, + ) async { + logger.info( + 'onCharacteristicReadRequest: $addressArgs.$hashCodeArgs - $idArgs, $offsetArgs'); + final central = _centrals[addressArgs]; + if (central == null) { + return; + } + final characteristic = _retrieveCharacteristic(hashCodeArgs); + if (characteristic == null) { + return; + } + const statusArgs = MyGattStatusArgs.success; + final offset = offsetArgs; + final valueArgs = _onCharacteristicRead(central, characteristic, offset); + await _trySendResponse( + addressArgs, + idArgs, + statusArgs, + offsetArgs, + valueArgs, + ); + } + + @override + void onCharacteristicWriteRequest( + String addressArgs, + int hashCodeArgs, + int idArgs, + int offsetArgs, + Uint8List valueArgs, + bool preparedWrite, + bool responseNeeded, + ) async { + logger.info( + 'onCharacteristicWriteRequest: $addressArgs.$hashCodeArgs - $idArgs, $offsetArgs, $valueArgs, $preparedWrite, $responseNeeded'); + final central = _centrals[addressArgs]; + if (central == null) { + return; + } + final characteristic = _retrieveCharacteristic(hashCodeArgs); + if (characteristic == null) { + return; + } + final MyGattStatusArgs statusArgs; + if (preparedWrite) { + final preparedCharacteristic = _preparedCharacteristics[addressArgs]; + if (preparedCharacteristic != null && + preparedCharacteristic != characteristic) { + statusArgs = MyGattStatusArgs.connectionCongested; + } else { + final preparedValueArgs = _preparedValue[addressArgs]; + if (preparedValueArgs == null) { + _preparedCharacteristics[addressArgs] = characteristic; + // Change the immutable Uint8List to mutable. + _preparedValue[addressArgs] = [...valueArgs]; + } else { + preparedValueArgs.insertAll(offsetArgs, valueArgs); + } + statusArgs = MyGattStatusArgs.success; + } + } else { + final value = valueArgs; + _onCharacteristicWritten(central, characteristic, value); + statusArgs = MyGattStatusArgs.success; + } + if (responseNeeded) { + await _trySendResponse( + addressArgs, + idArgs, + statusArgs, + offsetArgs, + null, + ); + } + } + + @override + void onExecuteWrite(String addressArgs, int idArgs, bool executeArgs) async { + logger.info('onExecuteWrite: $addressArgs - $idArgs, $executeArgs'); + final central = _centrals[addressArgs]; + final characteristic = _preparedCharacteristics.remove(addressArgs); + final elements = _preparedValue.remove(addressArgs); + if (central == null || characteristic == null || elements == null) { + return; + } + final value = Uint8List.fromList(elements); + final execute = executeArgs; + if (execute) { + _onCharacteristicWritten(central, characteristic, value); + } + await _trySendResponse( + addressArgs, + idArgs, + MyGattStatusArgs.success, + 0, + null, + ); + } + + @override + void onCharacteristicNotifyStateChanged( + String addressArgs, + int hashCodeArgs, + int stateNumberArgs, + ) { + final stateArgs = + MyGattCharacteristicNotifyStateArgs.values[stateNumberArgs]; + logger.info( + 'onCharacteristicNotifyStateChanged: $addressArgs.$hashCodeArgs - $stateArgs'); + final central = _centrals[addressArgs]; + if (central == null) { + return; + } + final characteristic = _retrieveCharacteristic(hashCodeArgs); + if (characteristic == null) { + return; + } + final state = stateArgs != MyGattCharacteristicNotifyStateArgs.none; + final confirms = _confirms.putIfAbsent(addressArgs, () => {}); + if (state) { + confirms[hashCodeArgs] = + stateArgs == MyGattCharacteristicNotifyStateArgs.indicate; + } else { + confirms.remove(hashCodeArgs); + } + final eventArgs = GattCharacteristicNotifyStateChangedEventArgs( + central, + characteristic, + state, + ); + _characteristicNotifyStateChangedController.add(eventArgs); + } + + MyGattCharacteristic? _retrieveCharacteristic(int hashCodeArgs) { + final characteristics = _characteristics.values + .reduce((value, element) => value..addAll(element)); + return characteristics[hashCodeArgs]; + } + + bool? _retrieveConfirm(String addressArgs, int hashCodeArgs) { + final confirms = _confirms[addressArgs]; + if (confirms == null) { + return null; + } + return confirms[hashCodeArgs]; + } + + Future _trySendResponse( + String addressArgs, + int idArgs, + MyGattStatusArgs statusArgs, + int offsetArgs, + Uint8List? valueArgs, + ) async { + final statusNumberArgs = statusArgs.index; + try { + _api.sendResponse( + addressArgs, + idArgs, + statusNumberArgs, + offsetArgs, + valueArgs, + ); + } catch (e, stack) { + logger.shout('Send response failed.', e, stack); + } + } + + Uint8List _onCharacteristicRead( + MyCentral central, + MyGattCharacteristic characteristic, + int offset, + ) { + final value = characteristic.value; + final trimmedValue = value.sublist(offset); + if (offset == 0) { + final eventArgs = GattCharacteristicReadEventArgs( + central, + characteristic, + value, + ); + _characteristicReadController.add(eventArgs); + } + return trimmedValue; + } + + void _onCharacteristicWritten( + MyCentral central, + MyGattCharacteristic characteristic, + Uint8List value, + ) async { + final trimmedValue = value.trimGATT(); + final eventArgs = GattCharacteristicWrittenEventArgs( + central, + characteristic, + trimmedValue, + ); + _characteristicWrittenController.add(eventArgs); + } +} diff --git a/bluetooth_low_energy_android/lib/src/my_peripheral_manager2.dart b/bluetooth_low_energy_android/lib/src/my_peripheral_manager2.dart deleted file mode 100644 index c0b5473..0000000 --- a/bluetooth_low_energy_android/lib/src/my_peripheral_manager2.dart +++ /dev/null @@ -1,252 +0,0 @@ -import 'dart:async'; - -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:flutter/foundation.dart'; - -import 'my_api.dart'; - -class MyPeripheralManager2 extends MyPeripheralManager - implements MyPeripheralManagerFlutterApi { - final MyPeripheralManagerHostApi _api; - BluetoothLowEnergyState _state; - final StreamController - _stateChangedController; - final StreamController - _readCharacteristicCommandReceivedController; - final StreamController - _writeCharacteristicCommandReceivedController; - final StreamController - _notifyCharacteristicCommandReceivedController; - - MyPeripheralManager2() - : _api = MyPeripheralManagerHostApi(), - _state = BluetoothLowEnergyState.unknown, - _stateChangedController = StreamController.broadcast(), - _readCharacteristicCommandReceivedController = - StreamController.broadcast(), - _writeCharacteristicCommandReceivedController = - StreamController.broadcast(), - _notifyCharacteristicCommandReceivedController = - StreamController.broadcast(); - - @override - BluetoothLowEnergyState get state => _state; - @protected - set state(BluetoothLowEnergyState value) { - if (_state == value) { - return; - } - _state = value; - final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); - _stateChangedController.add(eventArgs); - } - - @override - Stream get stateChanged => - _stateChangedController.stream; - @override - Stream - get readCharacteristicCommandReceived => - _readCharacteristicCommandReceivedController.stream; - @override - Stream - get writeCharacteristicCommandReceived => - _writeCharacteristicCommandReceivedController.stream; - @override - Stream - get notifyCharacteristicCommandReceived => - _notifyCharacteristicCommandReceivedController.stream; - - Future _throwWithoutState(BluetoothLowEnergyState state) async { - if (this.state != state) { - throw StateError( - '$state is expected, but current state is ${this.state}.'); - } - } - - @override - Future setUp() async { - final args = await _api.setUp(); - final stateArgs = - MyBluetoothLowEnergyStateArgs.values[args.stateNumberArgs]; - state = stateArgs.toState(); - MyPeripheralManagerFlutterApi.setup(this); - } - - @override - Future addService(GattService service) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (service is! MyGattService) { - throw TypeError(); - } - final serviceArgs = service.toArgs(); - await _api.addService(serviceArgs); - } - - @override - Future removeService(GattService service) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final serviceHashCodeArgs = service.hashCode; - await _api.removeService(serviceHashCodeArgs); - } - - @override - Future clearServices() async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - await _api.clearServices(); - } - - @override - Future startAdvertising(Advertisement advertisement) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final advertisementArgs = advertisement.toArgs(); - await _api.startAdvertising(advertisementArgs); - } - - @override - Future stopAdvertising() async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - await _api.stopAdvertising(); - } - - @override - Future getMaximumWriteLength(Central central) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final centralHashCodeArgs = central.hashCode; - final maximumWriteLength = - await _api.getMaximumWriteLength(centralHashCodeArgs); - return maximumWriteLength; - } - - @override - Future sendReadCharacteristicReply( - Central central, { - required GattCharacteristic characteristic, - required int id, - required int offset, - required bool status, - required Uint8List value, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final centralHashCodeArgs = central.hashCode; - final characteristicHashCodeArgs = characteristic.hashCode; - final idArgs = id; - final offsetArgs = offset; - final statusArgs = status; - final valueArgs = value; - await _api.sendReadCharacteristicReply( - centralHashCodeArgs, - characteristicHashCodeArgs, - idArgs, - offsetArgs, - statusArgs, - valueArgs, - ); - } - - @override - Future sendWriteCharacteristicReply( - Central central, { - required GattCharacteristic characteristic, - required int id, - required int offset, - required bool status, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final centralHashCodeArgs = central.hashCode; - final characteristicHashCodeArgs = characteristic.hashCode; - final idArgs = id; - final offsetArgs = offset; - final statusArgs = status; - await _api.sendWriteCharacteristicReply( - centralHashCodeArgs, - characteristicHashCodeArgs, - idArgs, - offsetArgs, - statusArgs, - ); - } - - @override - Future notifyCharacteristicValueChanged( - Central central, { - required GattCharacteristic characteristic, - required Uint8List value, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final centralHashCodeArgs = central.hashCode; - final characteristicHashCodeArgs = characteristic.hashCode; - final valueArgs = value; - await _api.notifyCharacteristicValueChanged( - centralHashCodeArgs, - characteristicHashCodeArgs, - valueArgs, - ); - } - - @override - void onStateChanged(int stateNumberArgs) { - final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; - state = stateArgs.toState(); - } - - @override - void onReadCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - int idArgs, - int offsetArgs, - ) { - final central = centralArgs.toCentral(); - final characteristic = characteristicArgs.toCharacteristic2(); - final id = idArgs; - final offset = offsetArgs; - final eventArgs = ReadGattCharacteristicCommandEventArgs( - central, - characteristic, - id, - offset, - ); - _readCharacteristicCommandReceivedController.add(eventArgs); - } - - @override - void onWriteCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - int idArgs, - int offsetArgs, - Uint8List valueArgs, - ) { - final central = centralArgs.toCentral(); - final characteristic = characteristicArgs.toCharacteristic2(); - final id = idArgs; - final offset = offsetArgs; - final value = valueArgs; - final eventArgs = WriteGattCharacteristicCommandEventArgs( - central, - characteristic, - id, - offset, - value, - ); - _writeCharacteristicCommandReceivedController.add(eventArgs); - } - - @override - void onNotifyCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - bool stateArgs, - ) { - final central = centralArgs.toCentral(); - final characteristic = characteristicArgs.toCharacteristic2(); - final state = stateArgs; - final eventArgs = NotifyGattCharacteristicCommandEventArgs( - central, - characteristic, - state, - ); - _notifyCharacteristicCommandReceivedController.add(eventArgs); - } -} diff --git a/bluetooth_low_energy_android/my_api.dart b/bluetooth_low_energy_android/my_api.dart index 1ddb6ee..773b111 100644 --- a/bluetooth_low_energy_android/my_api.dart +++ b/bluetooth_low_energy_android/my_api.dart @@ -11,216 +11,6 @@ import 'package:pigeon/pigeon.dart'; ), ), ) -@HostApi() -abstract class MyCentralManagerHostApi { - @async - MyCentralManagerArgs setUp(); - @async - void startDiscovery(); - void stopDiscovery(); - @async - void connect(int peripheralHashCodeArgs); - @async - void disconnect(int peripheralHashCodeArgs); - int getMaximumWriteLength(int peripheralHashCodeArgs, int typeNumberArgs); - @async - int readRSSI(int peripheralHashCodeArgs); - @async - List discoverGATT(int peripheralHashCodeArgs); - @async - int requestMTU(int peripheralHashCodeArgs, int mtuArgs); - @async - Uint8List readCharacteristic( - int peripheralHashCodeArgs, - int characteristicHashCodeArgs, - ); - @async - void writeCharacteristic( - int peripheralHashCodeArgs, - int characteristicHashCodeArgs, - Uint8List valueArgs, - int typeNumberArgs, - ); - @async - void notifyCharacteristic( - int peripheralHashCodeArgs, - int characteristicHashCodeArgs, - bool stateArgs, - ); - @async - Uint8List readDescriptor( - int peripheralHashCodeArgs, - int descriptorHashCodeArgs, - ); - @async - void writeDescriptor( - int peripheralHashCodeArgs, - int descriptorHashCodeArgs, - Uint8List valueArgs, - ); -} - -@FlutterApi() -abstract class MyCentralManagerFlutterApi { - void onStateChanged(int stateNumberArgs); - void onDiscovered( - MyPeripheralArgs peripheralArgs, - int rssiArgs, - MyAdvertisementArgs advertisementArgs, - ); - void onPeripheralStateChanged( - MyPeripheralArgs peripheralArgs, - bool stateArgs, - ); - void onCharacteristicValueChanged( - MyGattCharacteristicArgs characteristicArgs, - Uint8List valueArgs, - ); -} - -@HostApi() -abstract class MyPeripheralManagerHostApi { - @async - MyPeripheralManagerArgs setUp(); - @async - void addService(MyGattServiceArgs serviceArgs); - void removeService(int serviceHashCodeArgs); - void clearServices(); - @async - void startAdvertising(MyAdvertisementArgs advertisementArgs); - void stopAdvertising(); - int getMaximumWriteLength(int centralHashCodeArgs); - void sendReadCharacteristicReply( - int centralHashCodeArgs, - int characteristicHashCodeArgs, - int idArgs, - int offsetArgs, - bool statusArgs, - Uint8List valueArgs, - ); - void sendWriteCharacteristicReply( - int centralHashCodeArgs, - int characteristicHashCodeArgs, - int idArgs, - int offsetArgs, - bool statusArgs, - ); - @async - void notifyCharacteristicValueChanged( - int centralHashCodeArgs, - int characteristicHashCodeArgs, - Uint8List valueArgs, - ); -} - -@FlutterApi() -abstract class MyPeripheralManagerFlutterApi { - void onStateChanged(int stateNumberArgs); - void onReadCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - int idArgs, - int offsetArgs, - ); - void onWriteCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - int idArgs, - int offsetArgs, - Uint8List valueArgs, - ); - void onNotifyCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - bool stateArgs, - ); -} - -class MyCentralManagerArgs { - final int stateNumberArgs; - - MyCentralManagerArgs(this.stateNumberArgs); -} - -class MyPeripheralManagerArgs { - final int stateNumberArgs; - - MyPeripheralManagerArgs(this.stateNumberArgs); -} - -class MyCentralArgs { - final int hashCodeArgs; - final String uuidArgs; - - MyCentralArgs(this.hashCodeArgs, this.uuidArgs); -} - -class MyPeripheralArgs { - final int hashCodeArgs; - final String uuidArgs; - - MyPeripheralArgs(this.hashCodeArgs, this.uuidArgs); -} - -class MyAdvertisementArgs { - final String? nameArgs; - final List serviceUUIDsArgs; - final Map serviceDataArgs; - final MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs; - - MyAdvertisementArgs( - this.nameArgs, - this.serviceUUIDsArgs, - this.serviceDataArgs, - this.manufacturerSpecificDataArgs, - ); -} - -class MyManufacturerSpecificDataArgs { - final int idArgs; - final Uint8List dataArgs; - - MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs); -} - -class MyGattServiceArgs { - final int hashCodeArgs; - final String uuidArgs; - final List characteristicsArgs; - - MyGattServiceArgs( - this.hashCodeArgs, - this.uuidArgs, - this.characteristicsArgs, - ); -} - -class MyGattCharacteristicArgs { - final int hashCodeArgs; - final String uuidArgs; - final List propertyNumbersArgs; - final List descriptorsArgs; - - MyGattCharacteristicArgs( - this.hashCodeArgs, - this.uuidArgs, - this.propertyNumbersArgs, - this.descriptorsArgs, - ); -} - -class MyGattDescriptorArgs { - final int hashCodeArgs; - final String uuidArgs; - final Uint8List? valueArgs; - - MyGattDescriptorArgs( - this.hashCodeArgs, - this.uuidArgs, - this.valueArgs, - ); -} - enum MyBluetoothLowEnergyStateArgs { unknown, unsupported, @@ -238,10 +28,212 @@ enum MyGattCharacteristicPropertyArgs { } enum MyGattCharacteristicWriteTypeArgs { - // Write with response withResponse, - // Write without response withoutResponse, - // Write with response and waiting for confirmation - // reliable, +} + +enum MyGattCharacteristicNotifyStateArgs { + none, + notify, + indicate, +} + +enum MyGattStatusArgs { + success, + readNotPermitted, + writeNotPermitted, + requestNotSupported, + invalidOffset, + insufficientAuthentication, + insufficientEncryption, + invalidAttributeLength, + connectionCongested, + failure, +} + +class MyManufacturerSpecificDataArgs { + final int idArgs; + final Uint8List dataArgs; + + MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs); +} + +class MyAdvertisementArgs { + final String? nameArgs; + final List serviceUUIDsArgs; + final Map serviceDataArgs; + final MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs; + + MyAdvertisementArgs( + this.nameArgs, + this.serviceUUIDsArgs, + this.serviceDataArgs, + this.manufacturerSpecificDataArgs, + ); +} + +class MyCentralArgs { + final String addressArgs; + + MyCentralArgs(this.addressArgs); +} + +class MyPeripheralArgs { + final String addressArgs; + + MyPeripheralArgs(this.addressArgs); +} + +class MyGattDescriptorArgs { + final int hashCodeArgs; + final String uuidArgs; + final Uint8List? valueArgs; + + MyGattDescriptorArgs( + this.hashCodeArgs, + this.uuidArgs, + this.valueArgs, + ); +} + +class MyGattCharacteristicArgs { + final int hashCodeArgs; + final String uuidArgs; + final List propertyNumbersArgs; + final List descriptorsArgs; + + MyGattCharacteristicArgs( + this.hashCodeArgs, + this.uuidArgs, + this.propertyNumbersArgs, + this.descriptorsArgs, + ); +} + +class MyGattServiceArgs { + final int hashCodeArgs; + final String uuidArgs; + final List characteristicsArgs; + + MyGattServiceArgs( + this.hashCodeArgs, + this.uuidArgs, + this.characteristicsArgs, + ); +} + +@HostApi() +abstract class MyCentralManagerHostApi { + void setUp(); + @async + void startDiscovery(); + void stopDiscovery(); + @async + void connect(String addressArgs); + @async + void disconnect(String addressArgs); + @async + int requestMTU(String addressArgs, int mtuArgs); + @async + int readRSSI(String addressArgs); + @async + List discoverServices(String addressArgs); + @async + Uint8List readCharacteristic(String addressArgs, int hashCodeArgs); + @async + void writeCharacteristic( + String addressArgs, + int hashCodeArgs, + Uint8List valueArgs, + int typeNumberArgs, + ); + @async + void setCharacteristicNotifyState( + String addressArgs, + int hashCodeArgs, + int stateNumberArgs, + ); + @async + Uint8List readDescriptor(String addressArgs, int hashCodeArgs); + @async + void writeDescriptor( + String addressArgs, + int hashCodeArgs, + Uint8List valueArgs, + ); +} + +@FlutterApi() +abstract class MyCentralManagerFlutterApi { + void onStateChanged(int stateNumberArgs); + void onDiscovered( + MyPeripheralArgs peripheralArgs, + int rssiArgs, + MyAdvertisementArgs advertisementArgs, + ); + void onConnectionStateChanged(String addressArgs, bool stateArgs); + void onMtuChanged(String addressArgs, int mtuArgs); + void onCharacteristicNotified( + String addressArgs, + int hashCodeArgs, + Uint8List valueArgs, + ); +} + +@HostApi() +abstract class MyPeripheralManagerHostApi { + void setUp(); + @async + void addService(MyGattServiceArgs serviceArgs); + void removeService(int hashCodeArgs); + void clearServices(); + @async + void startAdvertising(MyAdvertisementArgs advertisementArgs); + void stopAdvertising(); + void sendResponse( + String addressArgs, + int idArgs, + int statusNumberArgs, + int offsetArgs, + Uint8List? valueArgs, + ); + @async + void notifyCharacteristicChanged( + int hashCodeArgs, + Uint8List valueArgs, + bool confirmArgs, + String addressArgs, + ); +} + +@FlutterApi() +abstract class MyPeripheralManagerFlutterApi { + void onStateChanged(int stateNumberArgs); + void onConnectionStateChanged(MyCentralArgs centralArgs, bool stateArgs); + void onMtuChanged(String addressArgs, int mtuArgs); + void onCharacteristicReadRequest( + String addressArgs, + int hashCodeArgs, + int idArgs, + int offsetArgs, + ); + void onCharacteristicWriteRequest( + String addressArgs, + int hashCodeArgs, + int idArgs, + int offsetArgs, + Uint8List valueArgs, + bool preparedWriteArgs, + bool responseNeededArgs, + ); + void onExecuteWrite( + String addressArgs, + int idArgs, + bool executeArgs, + ); + void onCharacteristicNotifyStateChanged( + String addressArgs, + int hashCodeArgs, + int stateNumberArgs, + ); } diff --git a/bluetooth_low_energy_android/pubspec.yaml b/bluetooth_low_energy_android/pubspec.yaml index 2ac94c9..ddd178d 100644 --- a/bluetooth_low_energy_android/pubspec.yaml +++ b/bluetooth_low_energy_android/pubspec.yaml @@ -1,6 +1,6 @@ name: bluetooth_low_energy_android description: Android implementation of the bluetooth_low_energy plugin. -version: 4.0.0 +version: 5.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy environment: @@ -10,13 +10,13 @@ environment: dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^4.0.0 + bluetooth_low_energy_platform_interface: ^5.0.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^3.0.0 - pigeon: ^12.0.1 + pigeon: ^15.0.2 flutter: plugin: diff --git a/bluetooth_low_energy_darwin/CHANGELOG.md b/bluetooth_low_energy_darwin/CHANGELOG.md index 36d8ecd..a8ab5fc 100644 --- a/bluetooth_low_energy_darwin/CHANGELOG.md +++ b/bluetooth_low_energy_darwin/CHANGELOG.md @@ -1,3 +1,29 @@ +## 5.0.0 + +* Now `CentralManager#writeCharacteristic` and `PeripheralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes. +* Add `UUID#fromAddress` constructor. +* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`. +* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`. +* Add `PeripheralManager#readCharacteristic`. +* Remove `CentralManager#getMaximumWriteLength` method. +* Remove `PeripheralManager#getMaximumWriteLength` method. +* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`. +* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`. +* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`. +* Move `CentralManager#state` to `CentralManager#getState()`. +* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`. +* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`. +* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`. +* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`. +* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`. +* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#writeCharacteristic`. +* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`. +* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`. + +## 5.0.0-dev.1 + +* Implements new Api. + ## 4.0.0 * Remove `BluetoothLowEnergy` class. diff --git a/bluetooth_low_energy_darwin/darwin/Classes/BluetoothLowEnergyDarwin.swift b/bluetooth_low_energy_darwin/darwin/Classes/BluetoothLowEnergyDarwin.swift index e335bee..23a4891 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/BluetoothLowEnergyDarwin.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/BluetoothLowEnergyDarwin.swift @@ -11,15 +11,15 @@ import FlutterMacOS public class BluetoothLowEnergyDarwin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { #if os(iOS) - let binaryMessenger = registrar.messenger() + let messenger = registrar.messenger() #elseif os(macOS) - let binaryMessenger = registrar.messenger + let messenger = registrar.messenger #else #error("Unsupported platform.") #endif - let centralManager = MyCentralManager(binaryMessenger) - let peripheralManager = MyPeripheralManager(binaryMessenger) - MyCentralManagerHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: centralManager) - MyPeripheralManagerHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: peripheralManager) + let centralManager = MyCentralManager(messenger: messenger) + let peripheralManager = MyPeripheralManager(messenger: messenger) + MyCentralManagerHostApiSetup.setUp(binaryMessenger: messenger, api: centralManager) + MyPeripheralManagerHostApiSetup.setUp(binaryMessenger: messenger, api: peripheralManager) } } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyApi.g.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyApi.g.swift index 08489b3..e3f119c 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyApi.g.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyApi.g.swift @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v12.0.1), do not edit directly. +// Autogenerated from Pigeon (v15.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -10,10 +10,6 @@ import FlutterMacOS #error("Unsupported platform.") #endif -private func isNullish(_ value: Any?) -> Bool { - return value is NSNull || value == nil -} - private func wrapResult(_ result: Any?) -> [Any?] { return [result] } @@ -33,6 +29,14 @@ 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 isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + private func nilOrValue(_ value: Any?) -> T? { if value is NSNull { return nil } return value as! T? @@ -40,10 +44,11 @@ private func nilOrValue(_ value: Any?) -> T? { enum MyBluetoothLowEnergyStateArgs: Int { case unknown = 0 - case unsupported = 1 - case unauthorized = 2 - case poweredOff = 3 - case poweredOn = 4 + case resetting = 1 + case unsupported = 2 + case unauthorized = 3 + case poweredOff = 4 + case poweredOn = 5 } enum MyGattCharacteristicPropertyArgs: Int { @@ -59,82 +64,45 @@ enum MyGattCharacteristicWriteTypeArgs: Int { case withoutResponse = 1 } -/// Generated class from Pigeon that represents data sent in messages. -struct MyCentralManagerArgs { - var stateNumberArgs: Int64 - - static func fromList(_ list: [Any?]) -> MyCentralManagerArgs? { - let stateNumberArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) - - return MyCentralManagerArgs( - stateNumberArgs: stateNumberArgs - ) - } - func toList() -> [Any?] { - return [ - stateNumberArgs, - ] - } +enum MyGattErrorArgs: Int { + case success = 0 + case invalidHandle = 1 + case readNotPermitted = 2 + case writeNotPermitted = 3 + case invalidPDU = 4 + case insufficientAuthentication = 5 + case requestNotSupported = 6 + case invalidOffset = 7 + case insufficientAuthorization = 8 + case prepareQueueFull = 9 + case attributeNotFound = 10 + case attributeNotLong = 11 + case insufficientEncryptionKeySize = 12 + case invalidAttributeValueLength = 13 + case unlikelyError = 14 + case insufficientEncryption = 15 + case unsupportedGroupType = 16 + case insufficientResources = 17 } /// Generated class from Pigeon that represents data sent in messages. -struct MyPeripheralManagerArgs { - var stateNumberArgs: Int64 +struct MyManufacturerSpecificDataArgs { + var idArgs: Int64 + var dataArgs: FlutterStandardTypedData - static func fromList(_ list: [Any?]) -> MyPeripheralManagerArgs? { - let stateNumberArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) + static func fromList(_ list: [Any?]) -> MyManufacturerSpecificDataArgs? { + let idArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) + let dataArgs = list[1] as! FlutterStandardTypedData - return MyPeripheralManagerArgs( - stateNumberArgs: stateNumberArgs + return MyManufacturerSpecificDataArgs( + idArgs: idArgs, + dataArgs: dataArgs ) } func toList() -> [Any?] { return [ - stateNumberArgs, - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct MyCentralArgs { - var hashCodeArgs: Int64 - var uuidArgs: String - - static func fromList(_ list: [Any?]) -> MyCentralArgs? { - let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) - let uuidArgs = list[1] as! String - - return MyCentralArgs( - hashCodeArgs: hashCodeArgs, - uuidArgs: uuidArgs - ) - } - func toList() -> [Any?] { - return [ - hashCodeArgs, - uuidArgs, - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct MyPeripheralArgs { - var hashCodeArgs: Int64 - var uuidArgs: String - - static func fromList(_ list: [Any?]) -> MyPeripheralArgs? { - let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) - let uuidArgs = list[1] as! String - - return MyPeripheralArgs( - hashCodeArgs: hashCodeArgs, - uuidArgs: uuidArgs - ) - } - func toList() -> [Any?] { - return [ - hashCodeArgs, - uuidArgs, + idArgs, + dataArgs, ] } } @@ -173,49 +141,63 @@ struct MyAdvertisementArgs { } /// Generated class from Pigeon that represents data sent in messages. -struct MyManufacturerSpecificDataArgs { - var idArgs: Int64 - var dataArgs: FlutterStandardTypedData +struct MyCentralArgs { + var uuidArgs: String - static func fromList(_ list: [Any?]) -> MyManufacturerSpecificDataArgs? { - let idArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) - let dataArgs = list[1] as! FlutterStandardTypedData + static func fromList(_ list: [Any?]) -> MyCentralArgs? { + let uuidArgs = list[0] as! String - return MyManufacturerSpecificDataArgs( - idArgs: idArgs, - dataArgs: dataArgs + return MyCentralArgs( + uuidArgs: uuidArgs ) } func toList() -> [Any?] { return [ - idArgs, - dataArgs, + uuidArgs, ] } } /// Generated class from Pigeon that represents data sent in messages. -struct MyGattServiceArgs { +struct MyPeripheralArgs { + var uuidArgs: String + + static func fromList(_ list: [Any?]) -> MyPeripheralArgs? { + let uuidArgs = list[0] as! String + + return MyPeripheralArgs( + uuidArgs: uuidArgs + ) + } + func toList() -> [Any?] { + return [ + uuidArgs, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct MyGattDescriptorArgs { var hashCodeArgs: Int64 var uuidArgs: String - var characteristicsArgs: [MyGattCharacteristicArgs?] + var valueArgs: FlutterStandardTypedData? = nil - static func fromList(_ list: [Any?]) -> MyGattServiceArgs? { + static func fromList(_ list: [Any?]) -> MyGattDescriptorArgs? { let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) let uuidArgs = list[1] as! String - let characteristicsArgs = list[2] as! [MyGattCharacteristicArgs?] + let valueArgs: FlutterStandardTypedData? = nilOrValue(list[2]) - return MyGattServiceArgs( + return MyGattDescriptorArgs( hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, - characteristicsArgs: characteristicsArgs + valueArgs: valueArgs ) } func toList() -> [Any?] { return [ hashCodeArgs, uuidArgs, - characteristicsArgs, + valueArgs, ] } } @@ -251,27 +233,27 @@ struct MyGattCharacteristicArgs { } /// Generated class from Pigeon that represents data sent in messages. -struct MyGattDescriptorArgs { +struct MyGattServiceArgs { var hashCodeArgs: Int64 var uuidArgs: String - var valueArgs: FlutterStandardTypedData? = nil + var characteristicsArgs: [MyGattCharacteristicArgs?] - static func fromList(_ list: [Any?]) -> MyGattDescriptorArgs? { + static func fromList(_ list: [Any?]) -> MyGattServiceArgs? { let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) let uuidArgs = list[1] as! String - let valueArgs: FlutterStandardTypedData? = nilOrValue(list[2]) + let characteristicsArgs = list[2] as! [MyGattCharacteristicArgs?] - return MyGattDescriptorArgs( + return MyGattServiceArgs( hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, - valueArgs: valueArgs + characteristicsArgs: characteristicsArgs ) } func toList() -> [Any?] { return [ hashCodeArgs, uuidArgs, - valueArgs, + characteristicsArgs, ] } } @@ -280,12 +262,10 @@ private class MyCentralManagerHostApiCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { case 128: - return MyCentralManagerArgs.fromList(self.readValue() as! [Any?]) - case 129: return MyGattCharacteristicArgs.fromList(self.readValue() as! [Any?]) - case 130: + case 129: return MyGattDescriptorArgs.fromList(self.readValue() as! [Any?]) - case 131: + case 130: return MyGattServiceArgs.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -295,17 +275,14 @@ private class MyCentralManagerHostApiCodecReader: FlutterStandardReader { private class MyCentralManagerHostApiCodecWriter: FlutterStandardWriter { override func writeValue(_ value: Any) { - if let value = value as? MyCentralManagerArgs { + if let value = value as? MyGattCharacteristicArgs { super.writeByte(128) super.writeValue(value.toList()) - } else if let value = value as? MyGattCharacteristicArgs { + } else if let value = value as? MyGattDescriptorArgs { super.writeByte(129) super.writeValue(value.toList()) - } else if let value = value as? MyGattDescriptorArgs { - super.writeByte(130) - super.writeValue(value.toList()) } else if let value = value as? MyGattServiceArgs { - super.writeByte(131) + super.writeByte(130) super.writeValue(value.toList()) } else { super.writeValue(value) @@ -329,19 +306,21 @@ class MyCentralManagerHostApiCodec: FlutterStandardMessageCodec { /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol MyCentralManagerHostApi { - func setUp(completion: @escaping (Result) -> Void) + func setUp() throws func startDiscovery() throws func stopDiscovery() throws - func connect(peripheralHashCodeArgs: Int64, completion: @escaping (Result) -> Void) - func disconnect(peripheralHashCodeArgs: Int64, completion: @escaping (Result) -> Void) - func getMaximumWriteLength(peripheralHashCodeArgs: Int64, typeNumberArgs: Int64) throws -> Int64 - func readRSSI(peripheralHashCodeArgs: Int64, completion: @escaping (Result) -> Void) - func discoverGATT(peripheralHashCodeArgs: Int64, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void) - func readCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, completion: @escaping (Result) -> Void) - func writeCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result) -> Void) - func notifyCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, stateArgs: Bool, completion: @escaping (Result) -> Void) - func readDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, completion: @escaping (Result) -> Void) - func writeDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result) -> Void) + func connect(uuidArgs: String, completion: @escaping (Result) -> Void) + func disconnect(uuidArgs: String, completion: @escaping (Result) -> Void) + func getMaximumWriteValueLength(uuidArgs: String, typeNumberArgs: Int64) throws -> Int64 + func readRSSI(uuidArgs: String, completion: @escaping (Result) -> Void) + func discoverServices(uuidArgs: String, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void) + func discoverCharacteristics(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattCharacteristicArgs], Error>) -> Void) + func discoverDescriptors(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattDescriptorArgs], Error>) -> Void) + func readCharacteristic(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result) -> Void) + func writeCharacteristic(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result) -> Void) + func setCharacteristicNotifyState(uuidArgs: String, hashCodeArgs: Int64, stateArgs: Bool, completion: @escaping (Result) -> Void) + func readDescriptor(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result) -> Void) + func writeDescriptor(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result) -> Void) } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -353,13 +332,11 @@ class MyCentralManagerHostApiSetup { let setUpChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setUp", binaryMessenger: binaryMessenger, codec: codec) if let api = api { setUpChannel.setMessageHandler { _, reply in - api.setUp() { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } + do { + try api.setUp() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) } } } else { @@ -395,8 +372,8 @@ class MyCentralManagerHostApiSetup { if let api = api { connectChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - api.connect(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in + let uuidArgsArg = args[0] as! String + api.connect(uuidArgs: uuidArgsArg) { result in switch result { case .success: reply(wrapResult(nil)) @@ -412,8 +389,8 @@ class MyCentralManagerHostApiSetup { if let api = api { disconnectChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - api.disconnect(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in + let uuidArgsArg = args[0] as! String + api.disconnect(uuidArgs: uuidArgsArg) { result in switch result { case .success: reply(wrapResult(nil)) @@ -425,28 +402,28 @@ class MyCentralManagerHostApiSetup { } else { disconnectChannel.setMessageHandler(nil) } - let getMaximumWriteLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.getMaximumWriteLength", binaryMessenger: binaryMessenger, codec: codec) + let getMaximumWriteValueLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.getMaximumWriteValueLength", binaryMessenger: binaryMessenger, codec: codec) if let api = api { - getMaximumWriteLengthChannel.setMessageHandler { message, reply in + getMaximumWriteValueLengthChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let uuidArgsArg = args[0] as! String let typeNumberArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) do { - let result = try api.getMaximumWriteLength(peripheralHashCodeArgs: peripheralHashCodeArgsArg, typeNumberArgs: typeNumberArgsArg) + let result = try api.getMaximumWriteValueLength(uuidArgs: uuidArgsArg, typeNumberArgs: typeNumberArgsArg) reply(wrapResult(result)) } catch { reply(wrapError(error)) } } } else { - getMaximumWriteLengthChannel.setMessageHandler(nil) + getMaximumWriteValueLengthChannel.setMessageHandler(nil) } let readRSSIChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readRSSI", binaryMessenger: binaryMessenger, codec: codec) if let api = api { readRSSIChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - api.readRSSI(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in + let uuidArgsArg = args[0] as! String + api.readRSSI(uuidArgs: uuidArgsArg) { result in switch result { case .success(let res): reply(wrapResult(res)) @@ -458,12 +435,12 @@ class MyCentralManagerHostApiSetup { } else { readRSSIChannel.setMessageHandler(nil) } - let discoverGATTChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverGATT", binaryMessenger: binaryMessenger, codec: codec) + let discoverServicesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverServices", binaryMessenger: binaryMessenger, codec: codec) if let api = api { - discoverGATTChannel.setMessageHandler { message, reply in + discoverServicesChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - api.discoverGATT(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in + let uuidArgsArg = args[0] as! String + api.discoverServices(uuidArgs: uuidArgsArg) { result in switch result { case .success(let res): reply(wrapResult(res)) @@ -473,15 +450,51 @@ class MyCentralManagerHostApiSetup { } } } else { - discoverGATTChannel.setMessageHandler(nil) + discoverServicesChannel.setMessageHandler(nil) + } + let discoverCharacteristicsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverCharacteristics", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + discoverCharacteristicsChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let uuidArgsArg = args[0] as! String + let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + api.discoverCharacteristics(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + discoverCharacteristicsChannel.setMessageHandler(nil) + } + let discoverDescriptorsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverDescriptors", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + discoverDescriptorsChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let uuidArgsArg = args[0] as! String + let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + api.discoverDescriptors(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + discoverDescriptorsChannel.setMessageHandler(nil) } let readCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readCharacteristic", binaryMessenger: binaryMessenger, codec: codec) if let api = api { readCharacteristicChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) - api.readCharacteristic(peripheralHashCodeArgs: peripheralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg) { result in + let uuidArgsArg = args[0] as! String + let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + api.readCharacteristic(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in switch result { case .success(let res): reply(wrapResult(res)) @@ -497,11 +510,11 @@ class MyCentralManagerHostApiSetup { if let api = api { writeCharacteristicChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + let uuidArgsArg = args[0] as! String + let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) let valueArgsArg = args[2] as! FlutterStandardTypedData let typeNumberArgsArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32) - api.writeCharacteristic(peripheralHashCodeArgs: peripheralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, valueArgs: valueArgsArg, typeNumberArgs: typeNumberArgsArg) { result in + api.writeCharacteristic(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg, typeNumberArgs: typeNumberArgsArg) { result in switch result { case .success: reply(wrapResult(nil)) @@ -513,14 +526,14 @@ class MyCentralManagerHostApiSetup { } else { writeCharacteristicChannel.setMessageHandler(nil) } - let notifyCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.notifyCharacteristic", binaryMessenger: binaryMessenger, codec: codec) + let setCharacteristicNotifyStateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setCharacteristicNotifyState", binaryMessenger: binaryMessenger, codec: codec) if let api = api { - notifyCharacteristicChannel.setMessageHandler { message, reply in + setCharacteristicNotifyStateChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + let uuidArgsArg = args[0] as! String + let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) let stateArgsArg = args[2] as! Bool - api.notifyCharacteristic(peripheralHashCodeArgs: peripheralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, stateArgs: stateArgsArg) { result in + api.setCharacteristicNotifyState(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, stateArgs: stateArgsArg) { result in switch result { case .success: reply(wrapResult(nil)) @@ -530,15 +543,15 @@ class MyCentralManagerHostApiSetup { } } } else { - notifyCharacteristicChannel.setMessageHandler(nil) + setCharacteristicNotifyStateChannel.setMessageHandler(nil) } let readDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readDescriptor", binaryMessenger: binaryMessenger, codec: codec) if let api = api { readDescriptorChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let descriptorHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) - api.readDescriptor(peripheralHashCodeArgs: peripheralHashCodeArgsArg, descriptorHashCodeArgs: descriptorHashCodeArgsArg) { result in + let uuidArgsArg = args[0] as! String + let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + api.readDescriptor(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in switch result { case .success(let res): reply(wrapResult(res)) @@ -554,10 +567,10 @@ class MyCentralManagerHostApiSetup { if let api = api { writeDescriptorChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let descriptorHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + let uuidArgsArg = args[0] as! String + let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) let valueArgsArg = args[2] as! FlutterStandardTypedData - api.writeDescriptor(peripheralHashCodeArgs: peripheralHashCodeArgsArg, descriptorHashCodeArgs: descriptorHashCodeArgsArg, valueArgs: valueArgsArg) { result in + api.writeDescriptor(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg) { result in switch result { case .success: reply(wrapResult(nil)) @@ -577,12 +590,8 @@ private class MyCentralManagerFlutterApiCodecReader: FlutterStandardReader { case 128: return MyAdvertisementArgs.fromList(self.readValue() as! [Any?]) case 129: - return MyGattCharacteristicArgs.fromList(self.readValue() as! [Any?]) - case 130: - return MyGattDescriptorArgs.fromList(self.readValue() as! [Any?]) - case 131: return MyManufacturerSpecificDataArgs.fromList(self.readValue() as! [Any?]) - case 132: + case 130: return MyPeripheralArgs.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) @@ -595,17 +604,11 @@ private class MyCentralManagerFlutterApiCodecWriter: FlutterStandardWriter { if let value = value as? MyAdvertisementArgs { super.writeByte(128) super.writeValue(value.toList()) - } else if let value = value as? MyGattCharacteristicArgs { + } else if let value = value as? MyManufacturerSpecificDataArgs { super.writeByte(129) super.writeValue(value.toList()) - } else if let value = value as? MyGattDescriptorArgs { - super.writeByte(130) - super.writeValue(value.toList()) - } else if let value = value as? MyManufacturerSpecificDataArgs { - super.writeByte(131) - super.writeValue(value.toList()) } else if let value = value as? MyPeripheralArgs { - super.writeByte(132) + super.writeByte(130) super.writeValue(value.toList()) } else { super.writeValue(value) @@ -629,10 +632,10 @@ class MyCentralManagerFlutterApiCodec: FlutterStandardMessageCodec { /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. protocol MyCentralManagerFlutterApiProtocol { - func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result) -> Void) - func onDiscovered(peripheralArgs peripheralArgsArg: MyPeripheralArgs, rssiArgs rssiArgsArg: Int64, advertisementArgs advertisementArgsArg: MyAdvertisementArgs, completion: @escaping (Result) -> Void) - func onPeripheralStateChanged(peripheralArgs peripheralArgsArg: MyPeripheralArgs, stateArgs stateArgsArg: Bool, completion: @escaping (Result) -> Void) - func onCharacteristicValueChanged(characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) + func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result) -> Void) + func onDiscovered(peripheralArgs peripheralArgsArg: MyPeripheralArgs, rssiArgs rssiArgsArg: Int64, advertisementArgs advertisementArgsArg: MyAdvertisementArgs, completion: @escaping (Result) -> Void) + func onConnectionStateChanged(uuidArgs uuidArgsArg: String, stateArgs stateArgsArg: Bool, completion: @escaping (Result) -> Void) + func onCharacteristicNotified(uuidArgs uuidArgsArg: String, hashCodeArgs hashCodeArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) } class MyCentralManagerFlutterApi: MyCentralManagerFlutterApiProtocol { private let binaryMessenger: FlutterBinaryMessenger @@ -642,28 +645,76 @@ class MyCentralManagerFlutterApi: MyCentralManagerFlutterApiProtocol { var codec: FlutterStandardMessageCodec { return MyCentralManagerFlutterApiCodec.shared } - func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result) -> Void) { - let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onStateChanged", binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([stateNumberArgsArg] as [Any?]) { _ in - completion(.success(Void())) + func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onStateChanged" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([stateNumberArgsArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName:channelName))) + return + } + if (listResponse.count > 1) { + 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))); + } else { + completion(.success(Void())) + } } } - func onDiscovered(peripheralArgs peripheralArgsArg: MyPeripheralArgs, rssiArgs rssiArgsArg: Int64, advertisementArgs advertisementArgsArg: MyAdvertisementArgs, completion: @escaping (Result) -> Void) { - let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onDiscovered", binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([peripheralArgsArg, rssiArgsArg, advertisementArgsArg] as [Any?]) { _ in - completion(.success(Void())) + func onDiscovered(peripheralArgs peripheralArgsArg: MyPeripheralArgs, rssiArgs rssiArgsArg: Int64, advertisementArgs advertisementArgsArg: MyAdvertisementArgs, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onDiscovered" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([peripheralArgsArg, rssiArgsArg, advertisementArgsArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName:channelName))) + return + } + if (listResponse.count > 1) { + 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))); + } else { + completion(.success(Void())) + } } } - func onPeripheralStateChanged(peripheralArgs peripheralArgsArg: MyPeripheralArgs, stateArgs stateArgsArg: Bool, completion: @escaping (Result) -> Void) { - let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onPeripheralStateChanged", binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([peripheralArgsArg, stateArgsArg] as [Any?]) { _ in - completion(.success(Void())) + func onConnectionStateChanged(uuidArgs uuidArgsArg: String, stateArgs stateArgsArg: Bool, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onConnectionStateChanged" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([uuidArgsArg, stateArgsArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName:channelName))) + return + } + if (listResponse.count > 1) { + 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))); + } else { + completion(.success(Void())) + } } } - func onCharacteristicValueChanged(characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) { - let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicValueChanged", binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([characteristicArgsArg, valueArgsArg] as [Any?]) { _ in - completion(.success(Void())) + func onCharacteristicNotified(uuidArgs uuidArgsArg: String, hashCodeArgs hashCodeArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicNotified" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([uuidArgsArg, hashCodeArgsArg, valueArgsArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName:channelName))) + return + } + if (listResponse.count > 1) { + 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))); + } else { + completion(.success(Void())) + } } } } @@ -680,8 +731,6 @@ private class MyPeripheralManagerHostApiCodecReader: FlutterStandardReader { return MyGattServiceArgs.fromList(self.readValue() as! [Any?]) case 132: return MyManufacturerSpecificDataArgs.fromList(self.readValue() as! [Any?]) - case 133: - return MyPeripheralManagerArgs.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -705,9 +754,6 @@ private class MyPeripheralManagerHostApiCodecWriter: FlutterStandardWriter { } else if let value = value as? MyManufacturerSpecificDataArgs { super.writeByte(132) super.writeValue(value.toList()) - } else if let value = value as? MyPeripheralManagerArgs { - super.writeByte(133) - super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -730,16 +776,15 @@ class MyPeripheralManagerHostApiCodec: FlutterStandardMessageCodec { /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol MyPeripheralManagerHostApi { - func setUp(completion: @escaping (Result) -> Void) + func setUp() throws func addService(serviceArgs: MyGattServiceArgs, completion: @escaping (Result) -> Void) - func removeService(serviceHashCodeArgs: Int64) throws + func removeService(hashCodeArgs: Int64) throws func clearServices() throws func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result) -> Void) func stopAdvertising() throws - func getMaximumWriteLength(centralHashCodeArgs: Int64) throws -> Int64 - func sendReadCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool, valueArgs: FlutterStandardTypedData) throws - func sendWriteCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool) throws - func notifyCharacteristicValueChanged(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result) -> Void) + func getMaximumUpdateValueLength(uuidArgs: String) throws -> Int64 + func respond(idArgs: Int64, errorNumberArgs: Int64, valueArgs: FlutterStandardTypedData?) throws + func updateCharacteristic(hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, uuidsArgs: [String]?, completion: @escaping (Result) -> Void) } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -751,13 +796,11 @@ class MyPeripheralManagerHostApiSetup { let setUpChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.setUp", binaryMessenger: binaryMessenger, codec: codec) if let api = api { setUpChannel.setMessageHandler { _, reply in - api.setUp() { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } + do { + try api.setUp() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) } } } else { @@ -784,9 +827,9 @@ class MyPeripheralManagerHostApiSetup { if let api = api { removeServiceChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let serviceHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let hashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) do { - try api.removeService(serviceHashCodeArgs: serviceHashCodeArgsArg) + try api.removeService(hashCodeArgs: hashCodeArgsArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) @@ -838,68 +881,46 @@ class MyPeripheralManagerHostApiSetup { } else { stopAdvertisingChannel.setMessageHandler(nil) } - let getMaximumWriteLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.getMaximumWriteLength", binaryMessenger: binaryMessenger, codec: codec) + let getMaximumUpdateValueLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.getMaximumUpdateValueLength", binaryMessenger: binaryMessenger, codec: codec) if let api = api { - getMaximumWriteLengthChannel.setMessageHandler { message, reply in + getMaximumUpdateValueLengthChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let uuidArgsArg = args[0] as! String do { - let result = try api.getMaximumWriteLength(centralHashCodeArgs: centralHashCodeArgsArg) + let result = try api.getMaximumUpdateValueLength(uuidArgs: uuidArgsArg) reply(wrapResult(result)) } catch { reply(wrapError(error)) } } } else { - getMaximumWriteLengthChannel.setMessageHandler(nil) + getMaximumUpdateValueLengthChannel.setMessageHandler(nil) } - let sendReadCharacteristicReplyChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.sendReadCharacteristicReply", binaryMessenger: binaryMessenger, codec: codec) + let respondChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.respond", binaryMessenger: binaryMessenger, codec: codec) if let api = api { - sendReadCharacteristicReplyChannel.setMessageHandler { message, reply in + respondChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) - let idArgsArg = args[2] is Int64 ? args[2] as! Int64 : Int64(args[2] as! Int32) - let offsetArgsArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32) - let statusArgsArg = args[4] as! Bool - let valueArgsArg = args[5] as! FlutterStandardTypedData + let idArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let errorNumberArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) + let valueArgsArg: FlutterStandardTypedData? = nilOrValue(args[2]) do { - try api.sendReadCharacteristicReply(centralHashCodeArgs: centralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, idArgs: idArgsArg, offsetArgs: offsetArgsArg, statusArgs: statusArgsArg, valueArgs: valueArgsArg) + try api.respond(idArgs: idArgsArg, errorNumberArgs: errorNumberArgsArg, valueArgs: valueArgsArg) reply(wrapResult(nil)) } catch { reply(wrapError(error)) } } } else { - sendReadCharacteristicReplyChannel.setMessageHandler(nil) + respondChannel.setMessageHandler(nil) } - let sendWriteCharacteristicReplyChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.sendWriteCharacteristicReply", binaryMessenger: binaryMessenger, codec: codec) + let updateCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.updateCharacteristic", binaryMessenger: binaryMessenger, codec: codec) if let api = api { - sendWriteCharacteristicReplyChannel.setMessageHandler { message, reply in + updateCharacteristicChannel.setMessageHandler { message, reply in let args = message as! [Any?] - let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) - let idArgsArg = args[2] is Int64 ? args[2] as! Int64 : Int64(args[2] as! Int32) - let offsetArgsArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32) - let statusArgsArg = args[4] as! Bool - do { - try api.sendWriteCharacteristicReply(centralHashCodeArgs: centralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, idArgs: idArgsArg, offsetArgs: offsetArgsArg, statusArgs: statusArgsArg) - reply(wrapResult(nil)) - } catch { - reply(wrapError(error)) - } - } - } else { - sendWriteCharacteristicReplyChannel.setMessageHandler(nil) - } - let notifyCharacteristicValueChangedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.notifyCharacteristicValueChanged", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - notifyCharacteristicValueChangedChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) - let valueArgsArg = args[2] as! FlutterStandardTypedData - api.notifyCharacteristicValueChanged(centralHashCodeArgs: centralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, valueArgs: valueArgsArg) { result in + let hashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let valueArgsArg = args[1] as! FlutterStandardTypedData + let uuidsArgsArg: [String]? = nilOrValue(args[2]) + api.updateCharacteristic(hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg, uuidsArgs: uuidsArgsArg) { result in switch result { case .success: reply(wrapResult(nil)) @@ -909,7 +930,7 @@ class MyPeripheralManagerHostApiSetup { } } } else { - notifyCharacteristicValueChangedChannel.setMessageHandler(nil) + updateCharacteristicChannel.setMessageHandler(nil) } } } @@ -918,10 +939,6 @@ private class MyPeripheralManagerFlutterApiCodecReader: FlutterStandardReader { switch type { case 128: return MyCentralArgs.fromList(self.readValue() as! [Any?]) - case 129: - return MyGattCharacteristicArgs.fromList(self.readValue() as! [Any?]) - case 130: - return MyGattDescriptorArgs.fromList(self.readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -933,12 +950,6 @@ private class MyPeripheralManagerFlutterApiCodecWriter: FlutterStandardWriter { if let value = value as? MyCentralArgs { super.writeByte(128) super.writeValue(value.toList()) - } else if let value = value as? MyGattCharacteristicArgs { - super.writeByte(129) - super.writeValue(value.toList()) - } else if let value = value as? MyGattDescriptorArgs { - super.writeByte(130) - super.writeValue(value.toList()) } else { super.writeValue(value) } @@ -961,10 +972,10 @@ class MyPeripheralManagerFlutterApiCodec: FlutterStandardMessageCodec { /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. protocol MyPeripheralManagerFlutterApiProtocol { - func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result) -> Void) - func onReadCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, completion: @escaping (Result) -> Void) - func onWriteCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) - func onNotifyCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, stateArgs stateArgsArg: Bool, completion: @escaping (Result) -> Void) + func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result) -> Void) + func onCharacteristicReadRequest(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, completion: @escaping (Result) -> Void) + func onCharacteristicWriteRequest(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) + func onCharacteristicNotifyStateChanged(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, stateArgs stateArgsArg: Bool, completion: @escaping (Result) -> Void) } class MyPeripheralManagerFlutterApi: MyPeripheralManagerFlutterApiProtocol { private let binaryMessenger: FlutterBinaryMessenger @@ -974,28 +985,76 @@ class MyPeripheralManagerFlutterApi: MyPeripheralManagerFlutterApiProtocol { var codec: FlutterStandardMessageCodec { return MyPeripheralManagerFlutterApiCodec.shared } - func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result) -> Void) { - let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onStateChanged", binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([stateNumberArgsArg] as [Any?]) { _ in - completion(.success(Void())) + func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onStateChanged" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([stateNumberArgsArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName:channelName))) + return + } + if (listResponse.count > 1) { + 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))); + } else { + completion(.success(Void())) + } } } - func onReadCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, completion: @escaping (Result) -> Void) { - let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived", binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([centralArgsArg, characteristicArgsArg, idArgsArg, offsetArgsArg] as [Any?]) { _ in - completion(.success(Void())) + func onCharacteristicReadRequest(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([centralArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName:channelName))) + return + } + if (listResponse.count > 1) { + 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))); + } else { + completion(.success(Void())) + } } } - func onWriteCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) { - let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived", binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([centralArgsArg, characteristicArgsArg, idArgsArg, offsetArgsArg, valueArgsArg] as [Any?]) { _ in - completion(.success(Void())) + func onCharacteristicWriteRequest(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([centralArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg, valueArgsArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName:channelName))) + return + } + if (listResponse.count > 1) { + 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))); + } else { + completion(.success(Void())) + } } } - func onNotifyCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, stateArgs stateArgsArg: Bool, completion: @escaping (Result) -> Void) { - let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived", binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([centralArgsArg, characteristicArgsArg, stateArgsArg] as [Any?]) { _ in - completion(.success(Void())) + func onCharacteristicNotifyStateChanged(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, stateArgs stateArgsArg: Bool, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([centralArgsArg, hashCodeArgsArg, stateArgsArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName:channelName))) + return + } + if (listResponse.count > 1) { + 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))); + } else { + completion(.success(Void())) + } } } } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyApi.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyApi.swift index 682e546..5044078 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyApi.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyApi.swift @@ -16,85 +16,40 @@ import FlutterMacOS #error("Unsupported platform.") #endif -// This extension of Error is required to do use FlutterError in any Swift code. -extension FlutterError: Error {} - -extension CBManagerState { - func toArgs() -> MyBluetoothLowEnergyStateArgs { - switch self { - case .unauthorized: - return .unauthorized - case .poweredOff: - return .poweredOff - case .poweredOn: - return .poweredOn - default: - return .unsupported +// ToObj +extension [MyGattCharacteristicPropertyArgs] { + func toProperties() -> CBCharacteristicProperties { + var properties: CBCharacteristicProperties = [] + for args in self { + switch args { + case .read: + properties.insert(.read) + case .write: + properties.insert(.write) + case .writeWithoutResponse: + properties.insert(.writeWithoutResponse) + case .notify: + properties.insert(.notify) + case .indicate: + properties.insert(.indicate) + } } - } -} - -extension CBPeer { - var uuidArgs: String { identifier.uuidString } -} - -extension CBCentral { - func toArgs() -> MyCentralArgs { - let hashCodeArgs = Int64(hash) - return MyCentralArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs) - } -} - -extension CBPeripheral { - func toArgs() -> MyPeripheralArgs { - let hashCodeArgs = Int64(hash) - return MyPeripheralArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs) - } -} - -extension CBAttribute { - var uuidArgs: String { uuid.uuidString } -} - -extension CBService { - func toArgs(_ characteristicsArgs: [MyGattCharacteristicArgs]) -> MyGattServiceArgs { - let hashCodeArgs = Int64(hash) - return MyGattServiceArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, characteristicsArgs: characteristicsArgs) - } -} - -extension CBCharacteristic { - func toArgs(_ descriptorsArgs: [MyGattDescriptorArgs]) -> MyGattCharacteristicArgs { - let hashCodeArgs = Int64(hash) - return MyGattCharacteristicArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, propertyNumbersArgs: propertyNumbersArgs, descriptorsArgs: descriptorsArgs) + return properties } - var propertyNumbersArgs: [Int64] { - var propertiesArgs = [MyGattCharacteristicPropertyArgs]() - let properties = self.properties - if properties.contains(.read) { - propertiesArgs.append(.read) + func toPermissions() -> CBAttributePermissions { + var permissions: CBAttributePermissions = [] + for args in self { + switch args { + case .read: + permissions.insert(.readable) + case .write, .writeWithoutResponse: + permissions.insert(.writeable) + default: + continue + } } - if properties.contains(.write) { - propertiesArgs.append(.write) - } - if properties.contains(.writeWithoutResponse) { - propertiesArgs.append(.writeWithoutResponse) - } - if properties.contains(.notify) { - propertiesArgs.append(.notify) - } - if properties.contains(.indicate) { - propertiesArgs.append(.indicate) - } - return propertiesArgs.map { args in Int64(args.rawValue) } - } -} - -extension CBDescriptor { - func toArgs() -> MyGattDescriptorArgs { - let hashCodeArgs = Int64(hash) - return MyGattDescriptorArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs) + return permissions } } @@ -109,21 +64,151 @@ extension MyGattCharacteristicWriteTypeArgs { } } -extension String { - var data: Data { data(using: String.Encoding.utf8)! } -} - -extension NSNumber { - var data: Data { - var source = self - return Data(bytes: &source, count: MemoryLayout.size) +extension MyGattErrorArgs { + func toError() -> CBATTError.Code { + switch self { + case .success: + return .success + case .invalidHandle: + return .invalidHandle + case .readNotPermitted: + return .readNotPermitted + case .writeNotPermitted: + return .writeNotPermitted + case .invalidPDU: + return .invalidPdu + case .insufficientAuthentication: + return .insufficientAuthentication + case .requestNotSupported: + return .requestNotSupported + case .invalidOffset: + return .invalidOffset + case .insufficientAuthorization: + return .insufficientAuthorization + case .prepareQueueFull: + return .prepareQueueFull + case .attributeNotFound: + return .attributeNotFound + case .attributeNotLong: + return .attributeNotLong + case .insufficientEncryptionKeySize: + return .insufficientEncryptionKeySize + case .invalidAttributeValueLength: + return .invalidAttributeValueLength + case .unlikelyError: + return .unlikelyError + case .insufficientEncryption: + return .insufficientEncryption + case .unsupportedGroupType: + return .unsupportedGroupType + case .insufficientResources: + return .insufficientResources + } } } -extension UInt16 { - var data: Data { - var source = self - return Data(bytes: &source, count: MemoryLayout.size) +extension MyAdvertisementArgs { + func toAdvertisement() -> [String : Any] { + // CoreBluetooth only support `CBAdvertisementDataLocalNameKey` and `CBAdvertisementDataServiceUUIDsKey` + // see https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising + var advertisement = [String: Any]() + if nameArgs != nil { + let name = nameArgs! + advertisement[CBAdvertisementDataLocalNameKey] = name + } + if serviceUUIDsArgs.count > 0 { + var serviceUUIDs = [CBUUID]() + for args in serviceUUIDsArgs { + guard let uuidArgs = args else { + continue + } + let uuid = uuidArgs.toCBUUID() + serviceUUIDs.append(uuid) + } + advertisement[CBAdvertisementDataServiceUUIDsKey] = serviceUUIDs + } + return advertisement + } +} + +extension MyGattDescriptorArgs { + func toDescriptor() -> CBMutableDescriptor { + let type = uuidArgs.toCBUUID() + let value = valueArgs?.data + return CBMutableDescriptor(type: type, value: value) + } +} + +extension MyGattCharacteristicArgs { + func toCharacteristic() -> CBMutableCharacteristic { + let type = uuidArgs.toCBUUID() + var propertiesArgs = [MyGattCharacteristicPropertyArgs]() + for args in propertyNumbersArgs { + guard let propertyNumberArgs = args else { + continue + } + let propertyNumber = propertyNumberArgs.toInt() + guard let propertyArgs = MyGattCharacteristicPropertyArgs(rawValue: propertyNumber) else { + continue + } + propertiesArgs.append(propertyArgs) + } + let properties = propertiesArgs.toProperties() + let permissions = propertiesArgs.toPermissions() + return CBMutableCharacteristic(type: type, properties: properties, value: nil, permissions: permissions) + } +} + +extension MyGattServiceArgs { + func toService() -> CBMutableService { + let type = uuidArgs.toCBUUID() + let primary = true + return CBMutableService(type: type, primary: primary) + } +} + +extension Int64 { + func toInt() -> Int { + return Int(self) + } +} + +extension String { + func toCBUUID() -> CBUUID { + return CBUUID(string: self) + } +} + +// ToArgs +extension CBManagerState { + func toArgs() -> MyBluetoothLowEnergyStateArgs { + switch self { + case .resetting: + return .resetting + case .unsupported: + return .unsupported + case .unauthorized: + return .unauthorized + case .poweredOff: + return .poweredOff + case .poweredOn: + return .poweredOn + default: + return .unknown + } + } +} + +extension Data { + func toManufacturerSpecificDataArgs() -> MyManufacturerSpecificDataArgs? { + if count > 2 { + let idArgs = Int64(self[0]) | (Int64(self[1]) << 8) + let data = self[2...count - 1] + let dataArgs = FlutterStandardTypedData(bytes: data) + return MyManufacturerSpecificDataArgs(idArgs: idArgs, dataArgs: dataArgs) + } else { + return nil + } } } @@ -145,160 +230,105 @@ extension [String: Any] { } } -extension Data { - func toManufacturerSpecificDataArgs() -> MyManufacturerSpecificDataArgs? { - if count > 2 { - let idArgs = Int64(self[0]) | (Int64(self[1]) << 8) - let data = self[2...count - 1] - let dataArgs = FlutterStandardTypedData(bytes: data) - return MyManufacturerSpecificDataArgs(idArgs: idArgs, dataArgs: dataArgs) - } else { - return nil - } +extension CBCentral { + func toArgs() -> MyCentralArgs { + let uuidArgs = identifier.toArgs() + return MyCentralArgs(uuidArgs: uuidArgs) } } -extension MyAdvertisementArgs { - func toAdvertisement() throws -> [String : Any] { - // CoreBluetooth only support `CBAdvertisementDataLocalNameKey` and `CBAdvertisementDataServiceUUIDsKey`, see https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising - var advertisement = [String: Any]() - if nameArgs != nil { - let name = nameArgs! - advertisement[CBAdvertisementDataLocalNameKey] = name - } - if serviceUUIDsArgs.count > 0 { - var serviceUUIDs = [CBUUID]() - for args in serviceUUIDsArgs { - guard let uuidArgs = args else { - throw MyError.illegalArgument - } - let uuid = CBUUID(string: uuidArgs) - serviceUUIDs.append(uuid) - } - advertisement[CBAdvertisementDataServiceUUIDsKey] = serviceUUIDs - } -// if serviceDataArgs.count > 0 { -// var serviceData = [CBUUID: Data]() -// for args in serviceDataArgs { -// guard let uuidArgs = args.key else { -// throw MyError.illegalArgument -// } -// guard let dataArgs = args.value else { -// throw MyError.illegalArgument -// } -// let uuid = CBUUID(string: uuidArgs) -// let data = dataArgs.data -// serviceData[uuid] = data -// } -// advertisement[CBAdvertisementDataServiceDataKey] = serviceData -// } -// if manufacturerSpecificDataArgs != nil { -// let manufacturerSpecificData = manufacturerSpecificDataArgs!.toManufacturerSpecificData() -// advertisement[CBAdvertisementDataManufacturerDataKey] = manufacturerSpecificData -// } - return advertisement +extension CBPeripheral { + func toArgs() -> MyPeripheralArgs { + let uuidArgs = identifier.toArgs() + return MyPeripheralArgs(uuidArgs: uuidArgs) } } -//extension MyManufacturerSpecificDataArgs { -// func toManufacturerSpecificData() -> Data { -// let id = UInt16(idArgs).data -// let data = dataArgs.data -// return id + data -// } -//} - -extension MyGattServiceArgs { - func toService() -> CBMutableService { - let type = CBUUID(string: uuidArgs) - let primary = true - return CBMutableService(type: type, primary: primary) +extension CBDescriptor { + func toArgs() -> MyGattDescriptorArgs { + let hashCodeArgs = hash.toInt64() + let uuidArgs = uuid.toArgs() + return MyGattDescriptorArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs) } } -extension MyGattCharacteristicArgs { - func toCharacteristic() -> CBMutableCharacteristic { - let type = CBUUID(string: uuidArgs) - return CBMutableCharacteristic(type: type, properties: properties, value: nil, permissions: permissions) - } - - var properties: CBCharacteristicProperties { - var properties: CBCharacteristicProperties = [] - for args in propertyNumbersArgs { - guard let rawArgs = args else { - continue - } - let rawValue = Int(rawArgs) - let property = MyGattCharacteristicPropertyArgs(rawValue: rawValue) - switch property { - case .read: - properties.insert(.read) - case .write: - properties.insert(.write) - case .writeWithoutResponse: - properties.insert(.writeWithoutResponse) - case .notify: - properties.insert(.notify) - case .indicate: - properties.insert(.indicate) - default: - continue - } - } - return properties - } - - var permissions: CBAttributePermissions { - var permissions: CBAttributePermissions = [] - for args in propertyNumbersArgs { - guard let rawArgs = args else { - continue - } - let rawValue = Int(rawArgs) - let property = MyGattCharacteristicPropertyArgs(rawValue: rawValue) - switch property { - case .read: - permissions.insert(.readable) - case .write, .writeWithoutResponse: - permissions.insert(.writeable) - default: - continue - } - } - return permissions +extension CBCharacteristic { + func toArgs() -> MyGattCharacteristicArgs { + let hashCodeArgs = hash.toInt64() + let uuidArgs = uuid.toArgs() + let propertyNumbersArgs = properties.toArgs().map { args in args.rawValue.toInt64() } + let descriptorsArgs = descriptors?.map { descriptor in descriptor.toArgs() } ?? [] + return MyGattCharacteristicArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, propertyNumbersArgs: propertyNumbersArgs, descriptorsArgs: descriptorsArgs) } } -extension MyGattDescriptorArgs { - func toDescriptor() -> CBMutableDescriptor { - let type = CBUUID(string: uuidArgs) - let value = valueArgs?.data - return CBMutableDescriptor(type: type, value: value) +extension CBCharacteristicProperties { + func toArgs() -> [MyGattCharacteristicPropertyArgs] { + var args = [MyGattCharacteristicPropertyArgs]() + if contains(.read) { + args.append(.read) + } + if contains(.write) { + args.append(.write) + } + if contains(.writeWithoutResponse) { + args.append(.writeWithoutResponse) + } + if contains(.notify) { + args.append(.notify) + } + if contains(.indicate) { + args.append(.indicate) + } + return args + } +} + +extension CBService { + func toArgs() -> MyGattServiceArgs { + let hashCodeArgs = hash.toInt64() + let uuidArgs = uuid.toArgs() + let characteristicsArgs = characteristics?.map { characteristic in characteristic.toArgs() } ?? [] + return MyGattServiceArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, characteristicsArgs: characteristicsArgs) } } extension Int { - func coerceIn(_ minimum: Int, _ maximum: Int) throws -> Int { - if minimum > maximum { - throw MyError.illegalArgument - } - if self < minimum { - return minimum - } - if self > maximum { - return maximum - } - return self + func toInt64() -> Int64 { + return Int64(self) } } -extension Dictionary { - mutating func getOrPut(_ key: Key, _ defaultValue: () -> Value) -> Value { - guard let value = self[key] else { - let value1 = defaultValue() - self[key] = value1 - return value1 - } - return value +extension UUID { + func toArgs() -> String { + return uuidString.lowercased() + } +} + +extension CBUUID { + func toArgs() -> String { + return uuidString.lowercased() + } +} + +// This extension of Error is required to do use FlutterError in any Swift code. +extension FlutterError: Error {} + +extension String { + var data: Data { data(using: String.Encoding.utf8)! } +} + +extension NSNumber { + var data: Data { + var source = self + // TODO: resolve warning: Forming 'UnsafeRawPointer' to a variable of type 'NSNumber'; this is likely incorrect because 'NSNumber' may contain an object reference. + return Data(bytes: &source, count: MemoryLayout.size) + } +} + +extension UInt16 { + var data: Data { + var source = self + return Data(bytes: &source, count: MemoryLayout.size) } } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManager.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManager.swift index 6a8ab9c..bfa6883 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManager.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManager.swift @@ -17,220 +17,192 @@ import FlutterMacOS #endif class MyCentralManager: MyCentralManagerHostApi { - init(_ binaryMessenger: FlutterBinaryMessenger) { - self.binaryMessenger = binaryMessenger + private let _api: MyCentralManagerFlutterApi + private let _centralManager: CBCentralManager + + private lazy var _centralManagerDelegate = MyCentralManagerDelegate(centralManager: self) + private lazy var _peripheralDelegate = MyPeripheralDelegate(centralManager: self) + + private var _peripherals: [String: CBPeripheral] + private var _services: [String: [Int64: CBService]] + private var _characteristics: [String: [Int64: CBCharacteristic]] + private var _descriptors: [String: [Int64: CBDescriptor]] + + private var _connectCompletions: [String: (Result) -> Void] + private var _disconnectCompletions: [String: (Result) -> Void] + private var _readRssiCompletions: [String: (Result) -> Void] + private var _discoverServicesCompletions: [String: (Result<[MyGattServiceArgs], Error>) -> Void] + private var _discoverCharacteristicsCompletions: [String: [Int64: (Result<[MyGattCharacteristicArgs], Error>) -> Void]] + private var _discoverDescriptorsCompletions: [String: [Int64: (Result<[MyGattDescriptorArgs], Error>) -> Void]] + private var _readCharacteristicCompletions: [String: [Int64: (Result) -> Void]] + private var _writeCharacteristicCompletions: [String: [Int64: (Result) -> Void]] + private var _setCharacteristicNotifyStateCompletions: [String: [Int64: (Result) -> Void]] + private var _readDescriptorCompletions: [String: [Int64: (Result) -> Void]] + private var _writeDescriptorCompletions: [String: [Int64: (Result) -> Void]] + + init(messenger: FlutterBinaryMessenger) { + _api = MyCentralManagerFlutterApi(binaryMessenger: messenger) + _centralManager = CBCentralManager() + + _peripherals = [:] + _services = [:] + _characteristics = [:] + _descriptors = [:] + + _connectCompletions = [:] + _disconnectCompletions = [:] + _readRssiCompletions = [:] + _discoverServicesCompletions = [:] + _discoverCharacteristicsCompletions = [:] + _discoverDescriptorsCompletions = [:] + _readCharacteristicCompletions = [:] + _writeCharacteristicCompletions = [:] + _setCharacteristicNotifyStateCompletions = [:] + _readDescriptorCompletions = [:] + _writeDescriptorCompletions = [:] } - private let binaryMessenger: FlutterBinaryMessenger - private let centralManager = CBCentralManager() - - private lazy var api = MyCentralManagerFlutterApi(binaryMessenger: binaryMessenger) - private lazy var centralManagerDelegate = MyCentralManagerDelegate(self) - private lazy var peripheralDelegate = MyPeripheralDelegate(self) - - private var peripherals = [Int64: CBPeripheral]() - private var services = [Int64: CBService]() - private var characteristics = [Int64: CBCharacteristic]() - private var descriptors = [Int64: CBDescriptor]() - - private var peripheralsArgs = [Int: MyPeripheralArgs]() - private var servicesArgsOfPeripheralsArgs = [Int64: [MyGattServiceArgs]]() - private var servicesArgs = [Int: MyGattServiceArgs]() - private var characteristicsArgs = [Int: MyGattCharacteristicArgs]() - private var descriptorsArgs = [Int: MyGattDescriptorArgs]() - - private var setUpCompletion: ((Result) -> Void)? - private var connectCompletions = [Int64: (Result) -> Void]() - private var disconnectCompletions = [Int64: (Result) -> Void]() - private var readRssiCompletions = [Int64: (Result) -> Void]() - private var discoverGattCompletions = [Int64: (Result<[MyGattServiceArgs], Error>) -> Void]() - private var unfinishedServices = [Int64: [CBService]]() - private var unfinishedCharacteristics = [Int64: [CBCharacteristic]]() - private var readCharacteristicCompletions = [Int64: (Result) -> Void]() - private var writeCharacteristicCompletions = [Int64: (Result) -> Void]() - private var notifyCharacteristicCompletions = [Int64: (Result) -> Void]() - private var readDescriptorCompletions = [Int64: (Result) -> Void]() - private var writeDescriptorCompletions = [Int64: (Result) -> Void]() - - func setUp(completion: @escaping (Result) -> Void) { - do { - if setUpCompletion != nil { - throw MyError.illegalState - } - try tearDown() - centralManager.delegate = centralManagerDelegate - if centralManager.state == .unknown { - setUpCompletion = completion - } else { - let stateArgs = centralManager.state.toArgs() - let stateNumberArgs = Int64(stateArgs.rawValue) - let args = MyCentralManagerArgs(stateNumberArgs: stateNumberArgs) - completion(.success(args)) - } - } catch { - completion(.failure(error)) + func setUp() throws { + _clearState() + if _centralManager.delegate == nil { + _centralManager.delegate = _centralManagerDelegate } - } - - func tearDown() throws { - if(centralManager.isScanning) { - centralManager.stopScan() - } - for peripheral in peripherals.values { - if peripheral.state != .disconnected { - centralManager.cancelPeripheralConnection(peripheral) - } - } - peripherals.removeAll() - services.removeAll() - characteristics.removeAll() - descriptors.removeAll() - peripheralsArgs.removeAll() - servicesArgsOfPeripheralsArgs.removeAll() - servicesArgs.removeAll() - characteristicsArgs.removeAll() - descriptorsArgs.removeAll() - setUpCompletion = nil - connectCompletions.removeAll() - disconnectCompletions.removeAll() - readRssiCompletions.removeAll() - discoverGattCompletions.removeAll() - unfinishedServices.removeAll() - unfinishedCharacteristics.removeAll() - readCharacteristicCompletions.removeAll() - writeCharacteristicCompletions.removeAll() - notifyCharacteristicCompletions.removeAll() - readDescriptorCompletions.removeAll() - writeDescriptorCompletions.removeAll() + _onStateChanged() } func startDiscovery() throws { let options = [CBCentralManagerScanOptionAllowDuplicatesKey: true] - centralManager.scanForPeripherals(withServices: nil, options: options) + _centralManager.scanForPeripherals(withServices: nil, options: options) } func stopDiscovery() throws { - centralManager.stopScan() + _centralManager.stopScan() } - func connect(peripheralHashCodeArgs: Int64, completion: @escaping (Result) -> Void) { + func connect(uuidArgs: String, completion: @escaping (Result) -> Void) { do { - let unfinishedCompletion = connectCompletions[peripheralHashCodeArgs] - if unfinishedCompletion != nil { - throw MyError.illegalState - } - guard let peripheral = peripherals[peripheralHashCodeArgs] else { + guard let peripheral = _peripherals[uuidArgs] else { throw MyError.illegalArgument } - centralManager.connect(peripheral) - connectCompletions[peripheralHashCodeArgs] = completion + _centralManager.connect(peripheral) + _connectCompletions[uuidArgs] = completion } catch { completion(.failure(error)) } } - func disconnect(peripheralHashCodeArgs: Int64, completion: @escaping (Result) -> Void) { + func disconnect(uuidArgs: String, completion: @escaping (Result) -> Void) { do { - let unfinishedCompletion = disconnectCompletions[peripheralHashCodeArgs] - if unfinishedCompletion != nil { - throw MyError.illegalState - } - guard let peripheral = peripherals[peripheralHashCodeArgs] else { + guard let peripheral = _peripherals[uuidArgs] else { throw MyError.illegalArgument } - centralManager.cancelPeripheralConnection(peripheral) - disconnectCompletions[peripheralHashCodeArgs] = completion + _centralManager.cancelPeripheralConnection(peripheral) + _disconnectCompletions[uuidArgs] = completion } catch { completion(.failure(error)) } } - func getMaximumWriteLength(peripheralHashCodeArgs: Int64, typeNumberArgs: Int64) throws -> Int64 { - guard let peripheral = peripherals[peripheralHashCodeArgs] else { + func getMaximumWriteValueLength(uuidArgs: String, typeNumberArgs: Int64) throws -> Int64 { + guard let peripheral = _peripherals[uuidArgs] else { throw MyError.illegalArgument } - let typeRawValue = Int(typeNumberArgs) - guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeRawValue) else { + let typeNumber = typeNumberArgs.toInt() + guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeNumber) else { throw MyError.illegalArgument } let type = typeArgs.toWriteType() - let maximumWriteLength = try peripheral.maximumWriteValueLength(for: type).coerceIn(20, 512) - let maximumWriteLengthArgs = Int64(maximumWriteLength) - return maximumWriteLengthArgs + let maximumWriteValueLength = peripheral.maximumWriteValueLength(for: type) + let maximumWriteValueLengthArgs = maximumWriteValueLength.toInt64() + return maximumWriteValueLengthArgs } - func readRSSI(peripheralHashCodeArgs: Int64, completion: @escaping (Result) -> Void) { + func readRSSI(uuidArgs: String, completion: @escaping (Result) -> Void) { do { - let unfinishedCompletion = readRssiCompletions[peripheralHashCodeArgs] - if unfinishedCompletion != nil { - throw MyError.illegalState - } - guard let peripheral = peripherals[peripheralHashCodeArgs] else { + guard let peripheral = _peripherals[uuidArgs] else { throw MyError.illegalArgument } peripheral.readRSSI() - readRssiCompletions[peripheralHashCodeArgs] = completion + _readRssiCompletions[uuidArgs] = completion } catch { completion(.failure(error)) } } - func discoverGATT(peripheralHashCodeArgs: Int64, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void) { + func discoverServices(uuidArgs: String, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void) { do { - let unfinishedCompletion = discoverGattCompletions[peripheralHashCodeArgs] - if unfinishedCompletion != nil { - throw MyError.illegalState - } - guard let peripheral = peripherals[peripheralHashCodeArgs] else { + guard let peripheral = _peripherals[uuidArgs] else { throw MyError.illegalArgument } peripheral.discoverServices(nil) - discoverGattCompletions[peripheralHashCodeArgs] = completion + _discoverServicesCompletions[uuidArgs] = completion } catch { completion(.failure(error)) } } - func readCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, completion: @escaping (Result) -> Void) { + func discoverCharacteristics(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattCharacteristicArgs], Error>) -> Void) { do { - let unfinishedCompletion = readCharacteristicCompletions[characteristicHashCodeArgs] - if unfinishedCompletion != nil { - throw MyError.illegalState - } - guard let peripheral = peripherals[peripheralHashCodeArgs] else { + guard let peripheral = _peripherals[uuidArgs] else { throw MyError.illegalArgument } - guard let characteristic = characteristics[characteristicHashCodeArgs] else { + guard let service = _retrieveService(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else { + throw MyError.illegalArgument + } + peripheral.discoverCharacteristics(nil, for: service) + _discoverCharacteristicsCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion + } catch { + completion(.failure(error)) + } + } + + func discoverDescriptors(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattDescriptorArgs], Error>) -> Void){ + do { + guard let peripheral = _peripherals[uuidArgs] else { + throw MyError.illegalArgument + } + guard let characteristic = _retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else { + throw MyError.illegalArgument + } + peripheral.discoverDescriptors(for: characteristic) + _discoverDescriptorsCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion + } catch { + completion(.failure(error)) + } + } + + func readCharacteristic(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result) -> Void) { + do { + guard let peripheral = _peripherals[uuidArgs] else { + throw MyError.illegalArgument + } + guard let characteristic = _retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else { throw MyError.illegalArgument } peripheral.readValue(for: characteristic) - readCharacteristicCompletions[characteristicHashCodeArgs] = completion + _readCharacteristicCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } catch { completion(.failure(error)) } } - func writeCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result) -> Void) { + func writeCharacteristic(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result) -> Void) { do { - let unfinishedCompletion = writeCharacteristicCompletions[characteristicHashCodeArgs] - if unfinishedCompletion != nil { - throw MyError.illegalState - } - guard let peripheral = peripherals[peripheralHashCodeArgs] else { + guard let peripheral = _peripherals[uuidArgs] else { throw MyError.illegalArgument } - guard let characteristic = characteristics[characteristicHashCodeArgs] else { + guard let characteristic = _retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else { throw MyError.illegalArgument } let data = valueArgs.data - let typeRawValue = Int(typeNumberArgs) - guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeRawValue) else { + let typeNumber = typeNumberArgs.toInt() + guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeNumber) else { throw MyError.illegalArgument } let type = typeArgs.toWriteType() peripheral.writeValue(data, for: characteristic, type: type) if type == .withResponse { - writeCharacteristicCompletions[characteristicHashCodeArgs] = completion + _writeCharacteristicCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } else { completion(.success(())) } @@ -239,159 +211,149 @@ class MyCentralManager: MyCentralManagerHostApi { } } - func notifyCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, stateArgs: Bool, completion: @escaping (Result) -> Void) { + func setCharacteristicNotifyState(uuidArgs: String, hashCodeArgs: Int64, stateArgs: Bool, completion: @escaping (Result) -> Void) { do { - let unfinishedCompletion = notifyCharacteristicCompletions[characteristicHashCodeArgs] - if unfinishedCompletion != nil { - throw MyError.illegalState - } - guard let peripheral = peripherals[peripheralHashCodeArgs] else { + guard let peripheral = _peripherals[uuidArgs] else { throw MyError.illegalArgument } - guard let characteristic = characteristics[characteristicHashCodeArgs] else { + guard let characteristic = _retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else { throw MyError.illegalArgument } let enabled = stateArgs peripheral.setNotifyValue(enabled, for: characteristic) - notifyCharacteristicCompletions[characteristicHashCodeArgs] = completion + _setCharacteristicNotifyStateCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } catch { completion(.failure(error)) } } - func readDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, completion: @escaping (Result) -> Void) { + func readDescriptor(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result) -> Void) { do { - let unfinishedCompletion = readDescriptorCompletions[descriptorHashCodeArgs] - if unfinishedCompletion != nil { - throw MyError.illegalState - } - guard let peripheral = peripherals[peripheralHashCodeArgs] else { + guard let peripheral = _peripherals[uuidArgs] else { throw MyError.illegalArgument } - guard let descriptor = descriptors[descriptorHashCodeArgs] else { + guard let descriptor = _retrieveDescriptor(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else { throw MyError.illegalArgument } peripheral.readValue(for: descriptor) - readDescriptorCompletions[descriptorHashCodeArgs] = completion + _readDescriptorCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } catch { completion(.failure(error)) } } - func writeDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result) -> Void) { + func writeDescriptor(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result) -> Void) { do { - let unfinishedCompletion = writeDescriptorCompletions[descriptorHashCodeArgs] - if unfinishedCompletion != nil { - throw MyError.illegalState - } - guard let peripheral = peripherals[peripheralHashCodeArgs] else { + guard let peripheral = _peripherals[uuidArgs] else { throw MyError.illegalArgument } - guard let descriptor = descriptors[descriptorHashCodeArgs] else { + guard let descriptor = _retrieveDescriptor(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else { throw MyError.illegalArgument } let data = valueArgs.data peripheral.writeValue(data, for: descriptor) - writeDescriptorCompletions[descriptorHashCodeArgs] = completion + _writeDescriptorCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } catch { completion(.failure(error)) } } - func didUpdateState() { - let state = centralManager.state - let stateArgs = state.toArgs() - let stateNumberArgs = Int64(stateArgs.rawValue) - if state != .unknown && setUpCompletion != nil { - let args = MyCentralManagerArgs(stateNumberArgs: stateNumberArgs) - setUpCompletion!(.success(args)) - setUpCompletion = nil - } - api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in } + func didUpdateState(central: CBCentralManager) { + _onStateChanged() } - func didDiscover(_ peripheral: CBPeripheral, _ advertisementData: [String : Any], _ rssi: NSNumber) { + func didDiscover(central: CBCentralManager, peripheral: CBPeripheral, advertisementData: [String : Any], rssi: NSNumber) { let peripheralArgs = peripheral.toArgs() - let peripheralHashCode = peripheral.hash - let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs - peripheral.delegate = peripheralDelegate - peripherals[peripheralHashCodeArgs] = peripheral - peripheralsArgs[peripheralHashCode] = peripheralArgs + let uuidArgs = peripheralArgs.uuidArgs let rssiArgs = rssi.int64Value let advertisementArgs = advertisementData.toAdvertisementArgs() - api.onDiscovered(peripheralArgs: peripheralArgs, rssiArgs: rssiArgs, advertisementArgs: advertisementArgs) {_ in } + if peripheral.delegate == nil { + peripheral.delegate = _peripheralDelegate + } + _peripherals[uuidArgs] = peripheral + _api.onDiscovered(peripheralArgs: peripheralArgs, rssiArgs: rssiArgs, advertisementArgs: advertisementArgs) {_ in } } - func didConnect(_ peripheral: CBPeripheral) { - let peripheralHashCode = peripheral.hash - guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { - return - } - let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs + func didConnect(central: CBCentralManager, peripheral: CBPeripheral) { + let uuidArgs = peripheral.identifier.toArgs() let stateArgs = true - api.onPeripheralStateChanged(peripheralArgs: peripheralArgs, stateArgs: stateArgs) {_ in } - guard let completion = connectCompletions.removeValue(forKey: peripheralHashCodeArgs) else { + _api.onConnectionStateChanged(uuidArgs: uuidArgs, stateArgs: stateArgs) {_ in } + guard let completion = _connectCompletions.removeValue(forKey: uuidArgs) else { return } completion(.success(())) } - func didFailToConnect(_ peripheral: CBPeripheral, _ error: Error?) { - let peripheralHashCode = peripheral.hash - guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { + func didFailToConnect(central: CBCentralManager, peripheral: CBPeripheral, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + guard let completion = _connectCompletions.removeValue(forKey: uuidArgs) else { return } - let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs - let completion = connectCompletions.removeValue(forKey: peripheralHashCodeArgs) - completion?(.failure(error ?? MyError.unknown)) + completion(.failure(error ?? MyError.unknown)) } - func didDisconnectPeripheral(_ peripheral: CBPeripheral, _ error: Error?) { - let peripheralHashCode = peripheral.hash - guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { - return + func didDisconnectPeripheral(central: CBCentralManager, peripheral: CBPeripheral, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + _services.removeValue(forKey: uuidArgs) + _characteristics.removeValue(forKey: uuidArgs) + _descriptors.removeValue(forKey: uuidArgs) + let errorNotNil = error ?? MyError.unknown + let readRssiCompletion = _readRssiCompletions.removeValue(forKey: uuidArgs) + readRssiCompletion?(.failure(errorNotNil)) + let discoverServicesCompletion = _discoverServicesCompletions.removeValue(forKey: uuidArgs) + discoverServicesCompletion?(.failure(errorNotNil)) + let discoverCharacteristicsCompletions = _discoverCharacteristicsCompletions.removeValue(forKey: uuidArgs) + if discoverCharacteristicsCompletions != nil { + let completions = discoverCharacteristicsCompletions!.values + for completion in completions { + completion(.failure(errorNotNil)) + } } - let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs - let readRssiCompletion = readRssiCompletions.removeValue(forKey: peripheralHashCodeArgs) - readRssiCompletion?(.failure(error ?? MyError.unknown)) - let discoverGattCompletion = discoverGattCompletions.removeValue(forKey: peripheralHashCodeArgs) - discoverGattCompletion?(.failure(error ?? MyError.unknown)) - unfinishedServices.removeValue(forKey: peripheralHashCodeArgs) - unfinishedCharacteristics.removeValue(forKey: peripheralHashCodeArgs) - let servicesArgs = servicesArgsOfPeripheralsArgs.removeValue(forKey: peripheralHashCodeArgs) ?? [] - for serviceArgs in servicesArgs { - let serviceHashCodeArgs = serviceArgs.hashCodeArgs - let service = services.removeValue(forKey: serviceHashCodeArgs)! - let serviceHashCode = service.hash - self.servicesArgs.removeValue(forKey: serviceHashCode) - let characteristicsArgs = serviceArgs.characteristicsArgs.map { args in args! } - for characteristicArgs in characteristicsArgs { - let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - let characteristic = characteristics.removeValue(forKey: characteristicHashCodeArgs)! - let characteristicHashCode = characteristic.hash - self.characteristicsArgs.removeValue(forKey: characteristicHashCode) - let readCharacteristicCompletion = readCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) - let writeCharacteristicCompletion = writeCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) - let notifyCharacteristicCompletion = notifyCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) - readCharacteristicCompletion?(.failure(error ?? MyError.unknown)) - writeCharacteristicCompletion?(.failure(error ?? MyError.unknown)) - notifyCharacteristicCompletion?(.failure(error ?? MyError.unknown)) - let descriptorsArgs = characteristicArgs.descriptorsArgs.map { args in args! } - for descriptorArgs in descriptorsArgs { - let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - let descriptor = descriptors.removeValue(forKey: descriptorHashCodeArgs)! - let descriptorHashCode = descriptor.hash - self.descriptorsArgs.removeValue(forKey: descriptorHashCode) - let readDescriptorCompletion = readDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs) - let writeDescriptorCompletion = writeDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs) - readDescriptorCompletion?(.failure(error ?? MyError.unknown)) - writeDescriptorCompletion?(.failure(error ?? MyError.unknown)) - } + let discoverDescriptorsCompletions = _discoverDescriptorsCompletions.removeValue(forKey: uuidArgs) + if discoverDescriptorsCompletions != nil { + let completions = discoverDescriptorsCompletions!.values + for completion in completions { + completion(.failure(errorNotNil)) + } + } + let readCharacteristicCompletions = _readCharacteristicCompletions.removeValue(forKey: uuidArgs) + if readCharacteristicCompletions != nil { + let completions = readCharacteristicCompletions!.values + for completion in completions { + completion(.failure(errorNotNil)) + } + } + let writeCharacteristicCompletions = _writeCharacteristicCompletions.removeValue(forKey: uuidArgs) + if writeCharacteristicCompletions != nil { + let completions = writeCharacteristicCompletions!.values + for completion in completions { + completion(.failure(errorNotNil)) + } + } + let notifyCharacteristicCompletions = _setCharacteristicNotifyStateCompletions.removeValue(forKey: uuidArgs) + if notifyCharacteristicCompletions != nil { + let completions = notifyCharacteristicCompletions!.values + for completioin in completions { + completioin(.failure(errorNotNil)) + } + } + let readDescriptorCompletions = _readDescriptorCompletions.removeValue(forKey: uuidArgs) + if readDescriptorCompletions != nil { + let completions = readDescriptorCompletions!.values + for completioin in completions { + completioin(.failure(errorNotNil)) + } + } + let writeDescriptorCompletions = _writeDescriptorCompletions.removeValue(forKey: uuidArgs) + if writeDescriptorCompletions != nil { + let completions = writeDescriptorCompletions!.values + for completion in completions { + completion(.failure(errorNotNil)) } } let stateArgs = false - api.onPeripheralStateChanged(peripheralArgs: peripheralArgs, stateArgs: stateArgs) {_ in } - guard let completion = disconnectCompletions.removeValue(forKey: peripheralHashCodeArgs) else { + _api.onConnectionStateChanged(uuidArgs: uuidArgs, stateArgs: stateArgs) {_ in } + guard let completion = _disconnectCompletions.removeValue(forKey: uuidArgs) else { return } if error == nil { @@ -401,13 +363,9 @@ class MyCentralManager: MyCentralManagerHostApi { } } - func didReadRSSI(_ peripheral: CBPeripheral, _ rssi: NSNumber, _ error: Error?) { - let peripheralHashCode = peripheral.hash - guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { - return - } - let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs - guard let completion = readRssiCompletions.removeValue(forKey: peripheralHashCodeArgs) else { + func didReadRSSI(peripheral: CBPeripheral, rssi: NSNumber, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + guard let completion = _readRssiCompletions.removeValue(forKey: uuidArgs) else { return } if error == nil { @@ -418,157 +376,110 @@ class MyCentralManager: MyCentralManagerHostApi { } } - func didDiscoverServices(_ peripheral: CBPeripheral, _ error: Error?) { - let peripheralHashCode = peripheral.hash - guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { - return - } - let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs - if error == nil { - var services = peripheral.services ?? [] - if services.isEmpty { - didDiscoverGATT(peripheral, error) - } else { - let service = services.removeFirst() - unfinishedServices[peripheralHashCodeArgs] = services - peripheral.discoverCharacteristics(nil, for: service) - } - } else { - didDiscoverGATT(peripheral, error) - } - } - - func didDiscoverCharacteristics(_ peripheral: CBPeripheral, _ service: CBService, _ error: Error?) { - let peripheralHashCode = peripheral.hash - guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { - return - } - let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs - if error == nil { - var characteristics = service.characteristics ?? [] - if characteristics.isEmpty { - var services = unfinishedServices.removeValue(forKey: peripheralHashCodeArgs) ?? [] - if services.isEmpty { - didDiscoverGATT(peripheral, error) - } else { - let service = services.removeFirst() - unfinishedServices[peripheralHashCodeArgs] = services - peripheral.discoverCharacteristics(nil, for: service) - } - } else { - let characteristic = characteristics.removeFirst() - unfinishedCharacteristics[peripheralHashCodeArgs] = characteristics - peripheral.discoverDescriptors(for: characteristic) - } - } else { - didDiscoverGATT(peripheral, error) - } - } - - func didDiscoverDescriptors(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic, _ error: Error?) { - let peripheralHashCode = peripheral.hash - guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { - return - } - let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs - if error == nil { - var characteristics = unfinishedCharacteristics.removeValue(forKey: peripheralHashCodeArgs) ?? [] - if (characteristics.isEmpty) { - var services = unfinishedServices.removeValue(forKey: peripheralHashCodeArgs) ?? [] - if services.isEmpty { - didDiscoverGATT(peripheral, error) - } else { - let service = services.removeFirst() - unfinishedServices[peripheralHashCodeArgs] = services - peripheral.discoverCharacteristics(nil, for: service) - } - } else { - let characteristic = characteristics.removeFirst() - unfinishedCharacteristics[peripheralHashCodeArgs] = characteristics - peripheral.discoverDescriptors(for: characteristic) - } - } else { - didDiscoverGATT(peripheral, error) - } - } - - private func didDiscoverGATT(_ peripheral: CBPeripheral, _ error: Error?) { - let peripheralHashCode = peripheral.hash - guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { - return - } - let peripheralhashCodeArgs = peripheralArgs.hashCodeArgs - guard let completion = discoverGattCompletions.removeValue(forKey: peripheralhashCodeArgs) else { + func didDiscoverServices(peripheral: CBPeripheral, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + guard let completion = _discoverServicesCompletions.removeValue(forKey: uuidArgs) else { return } if error == nil { let services = peripheral.services ?? [] - var servicesArgs = [MyGattServiceArgs]() - for service in services { - let characteristics = service.characteristics ?? [] - var characteristicsArgs = [MyGattCharacteristicArgs]() - for characteristic in characteristics { - let descriptors = characteristic.descriptors ?? [] - var descriptorsArgs = [MyGattDescriptorArgs]() - for descriptor in descriptors { - let descriptorArgs = descriptor.toArgs() - let descriptorHashCode = descriptor.hash - let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - self.descriptors[descriptorHashCodeArgs] = descriptor - self.descriptorsArgs[descriptorHashCode] = descriptorArgs - descriptorsArgs.append(descriptorArgs) - } - let characteristicArgs = characteristic.toArgs(descriptorsArgs) - let characteristicHashCode = characteristic.hash - let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - self.characteristics[characteristicHashCodeArgs] = characteristic - self.characteristicsArgs[characteristicHashCode] = characteristicArgs - characteristicsArgs.append(characteristicArgs) - } - let serviceArgs = service.toArgs(characteristicsArgs) - let serviceHashCode = service.hash - let servcieHashCodeArgs = serviceArgs.hashCodeArgs - self.services[servcieHashCodeArgs] = service - self.servicesArgs[serviceHashCode] = serviceArgs - servicesArgs.append(serviceArgs) + let servicesArgs = services.map { service in service.toArgs() } + let elements = services.flatMap { service in + let hashCodeArgs = service.hash.toInt64() + return [hashCodeArgs: service] } - servicesArgsOfPeripheralsArgs[peripheralhashCodeArgs] = servicesArgs - completion(.success((servicesArgs))) + var items = _services[uuidArgs] + if items == nil { + _services[uuidArgs] = Dictionary(uniqueKeysWithValues: elements) + } else { + items!.merge(elements) { service1, service2 in service2 } + } + completion(.success(servicesArgs)) } else { completion(.failure(error!)) - unfinishedServices.removeValue(forKey: peripheralhashCodeArgs) - unfinishedCharacteristics.removeValue(forKey: peripheralhashCodeArgs) } } - func didUpdateCharacteristicValue(_ characteristic: CBCharacteristic, _ error: Error?) { - let characteristicHashCode = characteristic.hash - guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else { + func didDiscoverCharacteristics(peripheral: CBPeripheral, service: CBService, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + let hashCodeArgs = service.hash.toInt64() + guard var completions = _discoverCharacteristicsCompletions[uuidArgs] else { return } - let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - guard let completion = readCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) else { - let value = characteristic.value ?? Data() - let valueArgs = FlutterStandardTypedData(bytes: value) - api.onCharacteristicValueChanged(characteristicArgs: characteristicArgs, valueArgs: valueArgs) {_ in } + guard let completion = completions.removeValue(forKey: hashCodeArgs) else { + return + } + if error == nil { + let characteristics = service.characteristics ?? [] + let characteristicsArgs = characteristics.map { characteristic in characteristic.toArgs() } + let elements = characteristics.flatMap { characteristic in + let hashCodeArgs = characteristic.hash.toInt64() + return [hashCodeArgs: characteristic] + } + var items = _characteristics[uuidArgs] + if items == nil { + _characteristics[uuidArgs] = Dictionary(uniqueKeysWithValues: elements) + } else { + items!.merge(elements) { characteristic1, characteristic2 in characteristic2 } + } + completion(.success(characteristicsArgs)) + } else { + completion(.failure(error!)) + } + } + + func didDiscoverDescriptors(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + let hashCodeArgs = characteristic.hash.toInt64() + guard var completions = _discoverDescriptorsCompletions[uuidArgs] else { + return + } + guard let completion = completions.removeValue(forKey: hashCodeArgs) else { + return + } + if error == nil { + let descriptors = characteristic.descriptors ?? [] + let descriptorsArgs = descriptors.map { descriptor in descriptor.toArgs() } + let elements = descriptors.flatMap { descriptor in + let hashCodeArgs = descriptor.hash.toInt64() + return [hashCodeArgs: descriptor] + } + var items = _descriptors[uuidArgs] + if items == nil { + _descriptors[uuidArgs] = Dictionary(uniqueKeysWithValues: elements) + } else { + items!.merge(elements) { descriptor1, descriptor2 in descriptor2 } + } + completion(.success(descriptorsArgs)) + } else { + completion(.failure(error!)) + } + } + + func didUpdateCharacteristicValue(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + let hashCodeArgs = characteristic.hash.toInt64() + let value = characteristic.value ?? Data() + let valueArgs = FlutterStandardTypedData(bytes: value) + var completions = _readCharacteristicCompletions[uuidArgs] + guard let completion = completions?.removeValue(forKey: hashCodeArgs) else { + _api.onCharacteristicNotified(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs, valueArgs: valueArgs) {_ in } return } if error == nil { - let value = characteristic.value ?? Data() - let valueArgs = FlutterStandardTypedData(bytes: value) completion(.success(valueArgs)) } else { completion(.failure(error!)) } } - func didWriteCharacteristicValue(_ characteristic: CBCharacteristic, _ error: Error?) { - let characteristicHashCode = characteristic.hash - guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else { + func didWriteCharacteristicValue(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + let hashCodeArgs = characteristic.hash.toInt64() + guard var completions = _writeCharacteristicCompletions[uuidArgs] else { return } - let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - guard let completion = writeCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) else { + guard let completion = completions.removeValue(forKey: hashCodeArgs) else { return } if error == nil { @@ -578,13 +489,13 @@ class MyCentralManager: MyCentralManagerHostApi { } } - func didUpdateNotificationState(_ characteristic: CBCharacteristic, _ error: Error?) { - let characteristicHashCode = characteristic.hash - guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else { + func didUpdateCharacteristicNotificationState(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + let hashCodeArgs = characteristic.hash.toInt64() + guard var completions = _setCharacteristicNotifyStateCompletions[uuidArgs] else { return } - let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - guard let completion = notifyCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) else { + guard let completion = completions.removeValue(forKey: hashCodeArgs) else { return } if error == nil { @@ -594,17 +505,17 @@ class MyCentralManager: MyCentralManagerHostApi { } } - func didUpdateDescriptorValue(_ descriptor: CBDescriptor, _ error: Error?) { - let descriptorHashCode = descriptor.hash - guard let descriptorArgs = descriptorsArgs[descriptorHashCode] else { + func didUpdateDescriptorValue(peripheral: CBPeripheral, descriptor: CBDescriptor, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + let hashCodeArgs = descriptor.hash.toInt64() + guard var completions = _readDescriptorCompletions[uuidArgs] else { return } - let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - guard let completion = readDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs) else { + guard let completion = completions.removeValue(forKey: hashCodeArgs) else { return } if error == nil { - // TODO: Need to confirm wheather the corresponding descriptor type and value is correct. + // TODO: confirm the corresponding descriptor types and values are correct. let valueArgs: FlutterStandardTypedData let value = descriptor.value do { @@ -647,13 +558,13 @@ class MyCentralManager: MyCentralManagerHostApi { } } - func didWriteDescriptorValue(_ descriptor: CBDescriptor, _ error: Error?) { - let descriptorHashCode = descriptor.hash - guard let descriptorArgs = descriptorsArgs[descriptorHashCode] else { + func didWriteDescriptorValue(peripheral: CBPeripheral, descriptor: CBDescriptor, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + let hashCodeArgs = descriptor.hash.toInt64() + guard var completions = _writeDescriptorCompletions[uuidArgs] else { return } - let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - guard let completion = writeDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs) else { + guard let completion = completions.removeValue(forKey: hashCodeArgs) else { return } if error == nil { @@ -662,4 +573,60 @@ class MyCentralManager: MyCentralManagerHostApi { completion(.failure(error!)) } } + + private func _clearState() { + if(_centralManager.isScanning) { + _centralManager.stopScan() + } + for peripheral in _peripherals.values { + if peripheral.state != .disconnected { + _centralManager.cancelPeripheralConnection(peripheral) + } + } + + _peripherals.removeAll() + _services.removeAll() + _characteristics.removeAll() + _descriptors.removeAll() + + _connectCompletions.removeAll() + _disconnectCompletions.removeAll() + _readRssiCompletions.removeAll() + _discoverServicesCompletions.removeAll() + _discoverCharacteristicsCompletions.removeAll() + _discoverDescriptorsCompletions.removeAll() + _readCharacteristicCompletions.removeAll() + _writeCharacteristicCompletions.removeAll() + _setCharacteristicNotifyStateCompletions.removeAll() + _readDescriptorCompletions.removeAll() + _writeDescriptorCompletions.removeAll() + } + + private func _retrieveService(uuidArgs: String, hashCodeArgs: Int64) -> CBService? { + guard let services = _services[uuidArgs] else { + return nil + } + return services[hashCodeArgs] + } + + private func _retrieveCharacteristic(uuidArgs: String, hashCodeArgs: Int64) -> CBCharacteristic? { + guard let characteristics = _characteristics[uuidArgs] else { + return nil + } + return characteristics[hashCodeArgs] + } + + private func _retrieveDescriptor(uuidArgs: String, hashCodeArgs: Int64) -> CBDescriptor? { + guard let descriptors = _descriptors[uuidArgs] else { + return nil + } + return descriptors[hashCodeArgs] + } + + private func _onStateChanged() { + let state = _centralManager.state + let stateArgs = state.toArgs() + let stateNumberArgs = stateArgs.rawValue.toInt64() + _api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in } + } } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManagerDelegate.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManagerDelegate.swift index 9fdc05d..127a74e 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManagerDelegate.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManagerDelegate.swift @@ -9,29 +9,29 @@ import Foundation import CoreBluetooth class MyCentralManagerDelegate: NSObject, CBCentralManagerDelegate { - init(_ centralManager: MyCentralManager) { - self.centralManager = centralManager + private let _centralManager: MyCentralManager + + init(centralManager: MyCentralManager) { + _centralManager = centralManager } - private let centralManager: MyCentralManager - func centralManagerDidUpdateState(_ central: CBCentralManager) { - centralManager.didUpdateState() + _centralManager.didUpdateState(central: central) } func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { - centralManager.didDiscover(peripheral, advertisementData, RSSI) + _centralManager.didDiscover(central: central, peripheral: peripheral, advertisementData: advertisementData, rssi: RSSI) } func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { - centralManager.didConnect(peripheral) + _centralManager.didConnect(central: central, peripheral: peripheral) } func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { - centralManager.didFailToConnect(peripheral, error) + _centralManager.didFailToConnect(central: central, peripheral: peripheral, error: error) } func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { - centralManager.didDisconnectPeripheral(peripheral, error) + _centralManager.didDisconnectPeripheral(central: central, peripheral: peripheral, error: error) } } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyError.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyError.swift index d07649e..4d46c65 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyError.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyError.swift @@ -10,6 +10,5 @@ import Foundation // TODO: 优化错误内容 enum MyError: Error { case illegalArgument - case illegalState case unknown } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralDelegate.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralDelegate.swift index d0617fb..4c9d497 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralDelegate.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralDelegate.swift @@ -9,45 +9,45 @@ import Foundation import CoreBluetooth class MyPeripheralDelegate: NSObject, CBPeripheralDelegate { - private let centralManager: MyCentralManager + private let _centralManager: MyCentralManager - init(_ centralManager: MyCentralManager) { - self.centralManager = centralManager + init(centralManager: MyCentralManager) { + _centralManager = centralManager } func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) { - centralManager.didReadRSSI(peripheral, RSSI, error) + _centralManager.didReadRSSI(peripheral: peripheral, rssi: RSSI, error: error) } func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { - centralManager.didDiscoverServices(peripheral, error) + _centralManager.didDiscoverServices(peripheral: peripheral, error: error) } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { - centralManager.didDiscoverCharacteristics(peripheral, service, error) + _centralManager.didDiscoverCharacteristics(peripheral: peripheral, service: service, error: error) } func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) { - centralManager.didDiscoverDescriptors(peripheral, characteristic, error) + _centralManager.didDiscoverDescriptors(peripheral: peripheral, characteristic: characteristic, error: error) } func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { - centralManager.didUpdateCharacteristicValue(characteristic, error) + _centralManager.didUpdateCharacteristicValue(peripheral: peripheral, characteristic: characteristic, error: error) } func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { - centralManager.didWriteCharacteristicValue(characteristic, error) + _centralManager.didWriteCharacteristicValue(peripheral: peripheral, characteristic: characteristic, error: error) } func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) { - centralManager.didUpdateNotificationState(characteristic, error) + _centralManager.didUpdateCharacteristicNotificationState(peripheral: peripheral, characteristic: characteristic, error: error) } func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?) { - centralManager.didUpdateDescriptorValue(descriptor, error) + _centralManager.didUpdateDescriptorValue(peripheral: peripheral, descriptor: descriptor, error: error) } func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) { - centralManager.didWriteDescriptorValue(descriptor, error) + _centralManager.didWriteDescriptorValue(peripheral: peripheral, descriptor: descriptor, error: error) } } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManager.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManager.swift index 25d3420..e5d212b 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManager.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManager.swift @@ -17,138 +17,308 @@ import FlutterMacOS #endif class MyPeripheralManager: MyPeripheralManagerHostApi { - init(_ binaryMessenger: FlutterBinaryMessenger) { - self.binaryMessenger = binaryMessenger + private let _api: MyPeripheralManagerFlutterApi + private let _peripheralManager: CBPeripheralManager + + private lazy var _peripheralManagerDelegate = MyPeripheralManagerDelegate(peripheralManager: self) + + private var _servicesArgs: [Int: MyGattServiceArgs] + private var _characteristicsArgs: [Int: MyGattCharacteristicArgs] + private var _descriptorsArgs: [Int: MyGattDescriptorArgs] + + private var _centrals: [String: CBCentral] + private var _services: [Int64: CBMutableService] + private var _characteristics: [Int64: CBMutableCharacteristic] + private var _descriptors: [Int64: CBMutableDescriptor] + private var _requests: [Int64: CBATTRequest] + + private var _addServiceCompletion: ((Result) -> Void)? + private var _startAdvertisingCompletion: ((Result) -> Void)? + private var _isReadyToUpdateSubscribersCallbacks: [() -> Void] + + init(messenger: FlutterBinaryMessenger) { + _api = MyPeripheralManagerFlutterApi(binaryMessenger: messenger) + _peripheralManager = CBPeripheralManager() + + _servicesArgs = [:] + _characteristicsArgs = [:] + _descriptorsArgs = [:] + + _centrals = [:] + _services = [:] + _characteristics = [:] + _descriptors = [:] + _requests = [:] + + _addServiceCompletion = nil + _startAdvertisingCompletion = nil + _isReadyToUpdateSubscribersCallbacks = [] } - private let binaryMessenger: FlutterBinaryMessenger - private let peripheralManager = CBPeripheralManager() - - private lazy var api = MyPeripheralManagerFlutterApi(binaryMessenger: binaryMessenger) - private lazy var peripheralManagerDelegate = MyPeripheralManagerDelegate(self) - - private var centrals = [Int64: CBCentral]() - private var services = [Int64: CBMutableService]() - private var characteristics = [Int64: CBMutableCharacteristic]() - private var descriptors = [Int64: CBMutableDescriptor]() - private var requests = [Int64: CBATTRequest]() - - private var centralsArgs = [Int: MyCentralArgs]() - private var servicesArgs = [Int: MyGattServiceArgs]() - private var characteristicsArgs = [Int: MyGattCharacteristicArgs]() - private var descriptorsArgs = [Int: MyGattDescriptorArgs]() - - private var setUpCompletion: ((Result) -> Void)? - private var addServiceCompletion: ((Result) -> Void)? - private var startAdvertisingCompletion: ((Result) -> Void)? - private var notifyCharacteristicValueChangedCallbacks = [() -> Void]() - - func setUp(completion: @escaping (Result) -> Void) { - do { - if setUpCompletion != nil { - throw MyError.illegalState - } - try tearDown() - peripheralManager.delegate = peripheralManagerDelegate - if peripheralManager.state == .unknown { - setUpCompletion = completion - } else { - let stateArgs = peripheralManager.state.toArgs() - let stateNumberArgs = Int64(stateArgs.rawValue) - let args = MyPeripheralManagerArgs(stateNumberArgs: stateNumberArgs) - completion(.success(args)) - } - } catch { - completion(.failure(error)) + func setUp() throws { + _clearState() + if _peripheralManager.delegate == nil { + _peripheralManager.delegate = _peripheralManagerDelegate } - } - - func tearDown() throws { - if(peripheralManager.isAdvertising) { - peripheralManager.stopAdvertising() - } - centrals.removeAll() - services.removeAll() - characteristics.removeAll() - descriptors.removeAll() - requests.removeAll() - centralsArgs.removeAll() - servicesArgs.removeAll() - characteristicsArgs.removeAll() - descriptorsArgs.removeAll() - setUpCompletion = nil - addServiceCompletion = nil - startAdvertisingCompletion = nil - notifyCharacteristicValueChangedCallbacks.removeAll() + _onStateChanged() } func addService(serviceArgs: MyGattServiceArgs, completion: @escaping (Result) -> Void) { - do { - if addServiceCompletion != nil { - throw MyError.illegalState + let service = serviceArgs.toService() + var characteristics = [CBMutableCharacteristic]() + let characteristicsArgs = serviceArgs.characteristicsArgs + for args in characteristicsArgs { + guard let characteristicArgs = args else { + continue } - let service = serviceArgs.toService() - var characteristics = [CBMutableCharacteristic]() - let characteristicsArgs = serviceArgs.characteristicsArgs - for args in characteristicsArgs { - guard let characteristicArgs = args else { + let characteristic = characteristicArgs.toCharacteristic() + characteristics.append(characteristic) + var descriptors = [CBMutableDescriptor]() + let descriptorsArgs = characteristicArgs.descriptorsArgs + for args in descriptorsArgs { + guard let descriptorArgs = args else { continue } - let characteristic = characteristicArgs.toCharacteristic() - var descriptors = [CBMutableDescriptor]() - let descriptorsArgs = characteristicArgs.descriptorsArgs - for args in descriptorsArgs { - guard let descriptorArgs = args else { - continue - } - let descriptor = descriptorArgs.toDescriptor() - descriptors.append(descriptor) - let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - let descriptorHashCode = descriptor.hash - self.descriptorsArgs[descriptorHashCode] = descriptorArgs - self.descriptors[descriptorHashCodeArgs] = descriptor - } - characteristic.descriptors = descriptors - characteristics.append(characteristic) - let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - let characteristicHashCode = characteristic.hash - self.characteristicsArgs[characteristicHashCode] = characteristicArgs - self.characteristics[characteristicHashCodeArgs] = characteristic + let descriptor = descriptorArgs.toDescriptor() + descriptors.append(descriptor) + let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs + let descriptorHashCode = descriptor.hash + _descriptorsArgs[descriptorHashCode] = descriptorArgs + _descriptors[descriptorHashCodeArgs] = descriptor + } + characteristic.descriptors = descriptors + let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs + let characteristicHashCode = characteristic.hash + _characteristicsArgs[characteristicHashCode] = characteristicArgs + _characteristics[characteristicHashCodeArgs] = characteristic + } + service.characteristics = characteristics + let serviceHashCodeArgs = serviceArgs.hashCodeArgs + let serviceHashCode = service.hash + _servicesArgs[serviceHashCode] = serviceArgs + _services[serviceHashCodeArgs] = service + _peripheralManager.add(service) + _addServiceCompletion = completion + } + + func removeService(hashCodeArgs: Int64) throws { + guard let service = _services[hashCodeArgs] else { + throw MyError.illegalArgument + } + let hashCode = service.hash + guard let serviceArgs = _servicesArgs[hashCode] else { + throw MyError.illegalArgument + } + _peripheralManager.remove(service) + _clearService(serviceArgs: serviceArgs) + } + + func clearServices() throws { + _peripheralManager.removeAllServices() + let servicesArgs = _servicesArgs.values + for serviceArgs in servicesArgs { + _clearService(serviceArgs: serviceArgs) + } + } + + func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result) -> Void) { + let advertisement = advertisementArgs.toAdvertisement() + _peripheralManager.startAdvertising(advertisement) + _startAdvertisingCompletion = completion + } + + func stopAdvertising() throws { + _peripheralManager.stopAdvertising() + } + + func getMaximumUpdateValueLength(uuidArgs: String) throws -> Int64 { + guard let central = _centrals[uuidArgs] else { + throw MyError.illegalArgument + } + let maximumUpdateValueLength = central.maximumUpdateValueLength + let maximumUpdateValueLengthArgs = maximumUpdateValueLength.toInt64() + return maximumUpdateValueLengthArgs + } + + func respond(idArgs: Int64, errorNumberArgs: Int64, valueArgs: FlutterStandardTypedData?) throws { + guard let request = _requests.removeValue(forKey: idArgs) else { + throw MyError.illegalArgument + } + if valueArgs != nil { + request.value = valueArgs?.data + } + let errorArgsRawValue = errorNumberArgs.toInt() + guard let errorArgs = MyGattErrorArgs(rawValue: errorArgsRawValue) else { + throw MyError.illegalArgument + } + let result = errorArgs.toError() + _peripheralManager.respond(to: request, withResult: result) + } + + func updateCharacteristic(hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, uuidsArgs: [String]?, completion: @escaping (Result) -> Void) { + do { + let centrals = try uuidsArgs?.map { uuidArgs in + guard let central = _centrals[uuidArgs] else { + throw MyError.illegalArgument + } + return central + } + guard let characteristic = _characteristics[hashCodeArgs] else { + throw MyError.illegalArgument + } + let value = valueArgs.data + let updated = _peripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals) + if updated { + completion(.success(())) + } else { + _isReadyToUpdateSubscribersCallbacks.append { + self.updateCharacteristic(hashCodeArgs: hashCodeArgs, valueArgs: valueArgs, uuidsArgs: uuidsArgs, completion: completion) + } } - service.characteristics = characteristics - let serviceHashCodeArgs = serviceArgs.hashCodeArgs - let serviceHashCode = service.hash - self.servicesArgs[serviceHashCode] = serviceArgs - self.services[serviceHashCodeArgs] = service - peripheralManager.add(service) - addServiceCompletion = completion } catch { - freeService(serviceArgs) completion(.failure(error)) } } - func removeService(serviceHashCodeArgs: Int64) throws { - guard let service = services[serviceHashCodeArgs] else { - throw MyError.illegalArgument - } - let serviceHashCode = service.hash - guard let serviceArgs = servicesArgs[serviceHashCode] else { - throw MyError.illegalArgument - } - peripheralManager.remove(service) - freeService(serviceArgs) + func didUpdateState(peripheral: CBPeripheralManager) { + _onStateChanged() } - func clearServices() throws { - peripheralManager.removeAllServices() - let servicesArgs = self.servicesArgs.values - for serviceArgs in servicesArgs { - freeService(serviceArgs) + func didAdd(peripheral: CBPeripheralManager, service: CBService, error: Error?) { + guard let completion = _addServiceCompletion else { + return + } + _addServiceCompletion = nil + if error == nil { + completion(.success(())) + } else { + completion(.failure(error!)) + let hashCode = service.hash + guard let serviceArgs = _servicesArgs[hashCode] else { + return + } + _clearService(serviceArgs: serviceArgs) } } - private func freeService(_ serviceArgs: MyGattServiceArgs) { + func didStartAdvertising(peripheral: CBPeripheralManager, error: Error?) { + guard let completion = _startAdvertisingCompletion else { + return + } + _startAdvertisingCompletion = nil + if error == nil { + completion(.success(())) + } else { + completion(.failure(error!)) + } + } + + func didReceiveRead(peripheral: CBPeripheralManager, request: CBATTRequest) { + let central = request.central + let centralArgs = central.toArgs() + _centrals[centralArgs.uuidArgs] = central + let characteristic = request.characteristic + let hashCode = characteristic.hash + guard let characteristicArgs = _characteristicsArgs[hashCode] else { + _peripheralManager.respond(to: request, withResult: .attributeNotFound) + return + } + let hashCodeArgs = characteristicArgs.hashCodeArgs + let idArgs = request.hash.toInt64() + let offsetArgs = request.offset.toInt64() + _requests[idArgs] = request + _api.onCharacteristicReadRequest(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, idArgs: idArgs, offsetArgs: offsetArgs) {_ in } + } + + func didReceiveWrite(peripheral: CBPeripheralManager, requests: [CBATTRequest]) { + // When you respond to a write request, note that the first parameter of the respond(to:with + // Result:) method expects a single CBATTRequest object, even though you received an array of + // them from the peripheralManager(_:didReceiveWrite:) method. To respond properly, + // pass in the first request of the requests array. + // see: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393315-peripheralmanager#discussion + guard let request = requests.first else { + return + } + if requests.count > 1 { + // TODO: 支持多写入请求,暂时不清楚此处应如何处理 + let result = CBATTError.requestNotSupported + _peripheralManager.respond(to: request, withResult: result) + return + } + let central = request.central + let centralArgs = central.toArgs() + _centrals[centralArgs.uuidArgs] = central + let characteristic = request.characteristic + let hashCode = characteristic.hash + guard let characteristicArgs = _characteristicsArgs[hashCode] else { + _peripheralManager.respond(to: request, withResult: .attributeNotFound) + return + } + let hashCodeArgs = characteristicArgs.hashCodeArgs + let idArgs = request.hash.toInt64() + let offsetArgs = request.offset.toInt64() + guard let value = request.value else { + _peripheralManager.respond(to: request, withResult: .requestNotSupported) + return + } + let valueArgs = FlutterStandardTypedData(bytes: value) + _requests[idArgs] = request + _api.onCharacteristicWriteRequest(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, idArgs: idArgs, offsetArgs: offsetArgs, valueArgs: valueArgs) {_ in } + } + + func didSubscribeTo(peripheral: CBPeripheralManager, central: CBCentral, characteristic: CBCharacteristic) { + let centralArgs = central.toArgs() + _centrals[centralArgs.uuidArgs] = central + let hashCode = characteristic.hash + guard let characteristicArgs = _characteristicsArgs[hashCode] else { + return + } + let hashCodeArgs = characteristicArgs.hashCodeArgs + let stateArgs = true + _api.onCharacteristicNotifyStateChanged(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, stateArgs: stateArgs) {_ in } + } + + func didUnsubscribeFrom(peripheral: CBPeripheralManager, central: CBCentral, characteristic: CBCharacteristic) { + let centralArgs = central.toArgs() + _centrals[centralArgs.uuidArgs] = central + let hashCode = characteristic.hash + guard let characteristicArgs = _characteristicsArgs[hashCode] else { + return + } + let hashCodeArgs = characteristicArgs.hashCodeArgs + let stateArgs = false + _api.onCharacteristicNotifyStateChanged(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, stateArgs: stateArgs) {_ in } + } + + func isReadyToUpdateSubscribers(peripheral: CBPeripheralManager) { + let callbacks = _isReadyToUpdateSubscribersCallbacks + _isReadyToUpdateSubscribersCallbacks.removeAll() + for callback in callbacks { + callback() + } + } + + private func _clearState() { + if(_peripheralManager.isAdvertising) { + _peripheralManager.stopAdvertising() + } + + _servicesArgs.removeAll() + _characteristicsArgs.removeAll() + _descriptorsArgs.removeAll() + + _centrals.removeAll() + _services.removeAll() + _characteristics.removeAll() + _descriptors.removeAll() + _requests.removeAll() + + _addServiceCompletion = nil + _startAdvertisingCompletion = nil + _isReadyToUpdateSubscribersCallbacks.removeAll() + } + + private func _clearService(serviceArgs: MyGattServiceArgs) { let characteristicsArgs = serviceArgs.characteristicsArgs for args in characteristicsArgs { guard let characteristicArgs = args else { @@ -160,223 +330,31 @@ class MyPeripheralManager: MyPeripheralManagerHostApi { continue } let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - guard let descriptor = self.descriptors.removeValue(forKey: descriptorHashCodeArgs) else { + guard let descriptor = _descriptors.removeValue(forKey: descriptorHashCodeArgs) else { continue } let descriptorHashCode = descriptor.hash - self.descriptorsArgs.removeValue(forKey: descriptorHashCode) + _descriptorsArgs.removeValue(forKey: descriptorHashCode) } let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - guard let characteristic = self.characteristics.removeValue(forKey: characteristicHashCodeArgs) else { + guard let characteristic = _characteristics.removeValue(forKey: characteristicHashCodeArgs) else { continue } let characteristicHashCode = characteristic.hash - self.characteristicsArgs.removeValue(forKey: characteristicHashCode) + _characteristicsArgs.removeValue(forKey: characteristicHashCode) } let serviceHashCodeArgs = serviceArgs.hashCodeArgs - guard let service = self.services.removeValue(forKey: serviceHashCodeArgs) else { + guard let service = _services.removeValue(forKey: serviceHashCodeArgs) else { return } let serviceHashCode = service.hash - self.servicesArgs.removeValue(forKey: serviceHashCode) + _servicesArgs.removeValue(forKey: serviceHashCode) } - func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result) -> Void) { - do { - if startAdvertisingCompletion != nil { - throw MyError.illegalState - } - let advertisement = try advertisementArgs.toAdvertisement() - peripheralManager.startAdvertising(advertisement) - startAdvertisingCompletion = completion - } catch { - completion(.failure(error)) - } - } - - func stopAdvertising() throws { - peripheralManager.stopAdvertising() - } - - func getMaximumWriteLength(centralHashCodeArgs: Int64) throws -> Int64 { - guard let central = centrals[centralHashCodeArgs] else { - throw MyError.illegalArgument - } - let maximumWriteLength = try central.maximumUpdateValueLength.coerceIn(20, 512) - let maximumWriteLengthArgs = Int64(maximumWriteLength) - return maximumWriteLengthArgs - } - - func sendReadCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool, valueArgs: FlutterStandardTypedData) throws { - guard let request = requests.removeValue(forKey: idArgs) else { - throw MyError.illegalArgument - } - request.value = valueArgs.data - let result = statusArgs ? CBATTError.Code.success : CBATTError.Code.requestNotSupported - peripheralManager.respond(to: request, withResult: result) - } - - func sendWriteCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool) throws { - guard let request = requests.removeValue(forKey: idArgs) else { - throw MyError.illegalArgument - } - let result = statusArgs ? CBATTError.Code.success : CBATTError.Code.requestNotSupported - peripheralManager.respond(to: request, withResult: result) - } - - func notifyCharacteristicValueChanged(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result) -> Void) { - do { - if notifyCharacteristicValueChangedCallbacks.count > 0 { - throw MyError.illegalState - } - let value = valueArgs.data - guard let characteristic = characteristics[characteristicHashCodeArgs] else { - throw MyError.illegalArgument - } - guard let central = centrals[centralHashCodeArgs] else { - throw MyError.illegalArgument - } - let centrals = [central] - let updated = peripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals) - if updated { - completion(.success(())) - } else { - notifyCharacteristicValueChangedCallbacks.append { - let updated = self.peripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals) - if updated { - completion(.success(())) - } else { - completion(.failure(MyError.unknown)) - } - } - } - } catch { - completion(.failure(error)) - } - } - - func didUpdateState() { - let state = peripheralManager.state + private func _onStateChanged() { + let state = _peripheralManager.state let stateArgs = state.toArgs() - let stateNumberArgs = Int64(stateArgs.rawValue) - if state != .unknown && setUpCompletion != nil { - let args = MyPeripheralManagerArgs(stateNumberArgs: stateNumberArgs) - setUpCompletion!(.success(args)) - setUpCompletion = nil - } - api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in } - } - - func didAdd(_ service: CBService, _ error: Error?) { - guard let completion = addServiceCompletion else { - return - } - addServiceCompletion = nil - if error == nil { - completion(.success(())) - } else { - completion(.failure(error!)) - let serviceHashCode = service.hash - guard let serviceArgs = servicesArgs[serviceHashCode] else { - return - } - freeService(serviceArgs) - } - } - - func didStartAdvertising(_ error: Error?) { - guard let completion = startAdvertisingCompletion else { - return - } - startAdvertisingCompletion = nil - if error == nil { - completion(.success(())) - } else { - completion(.failure(error!)) - } - } - - func didReceiveRead(_ request: CBATTRequest) { - let central = request.central - let centralHashCode = central.hash - let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() } - let centralHashCodeArgs = centralArgs.hashCodeArgs - centrals[centralHashCodeArgs] = central - let characteristic = request.characteristic - let characteristicHashCode = characteristic.hash - guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else { - peripheralManager.respond(to: request, withResult: .attributeNotFound) - return - } - let idArgs = Int64(request.hash) - requests[idArgs] = request - let offsetArgs = Int64(request.offset) - api.onReadCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, idArgs: idArgs, offsetArgs: offsetArgs) {_ in } - } - - func didReceiveWrite(_ requests: [CBATTRequest]) { - // 根据官方文档,仅响应第一个写入请求 - guard let request = requests.first else { - return - } - if requests.count > 1 { - // TODO: 支持多写入请求,暂时不清楚此处应如何处理 - let result = CBATTError.requestNotSupported - peripheralManager.respond(to: request, withResult: result) - } - let central = request.central - let centralHashCode = central.hash - let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() } - let centralHashCodeArgs = centralArgs.hashCodeArgs - centrals[centralHashCodeArgs] = central - let characteristic = request.characteristic - let characteristicHashCode = characteristic.hash - guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else { - peripheralManager.respond(to: request, withResult: .attributeNotFound) - return - } - let idArgs = Int64(request.hash) - self.requests[idArgs] = request - let offsetArgs = Int64(request.offset) - guard let value = request.value else { - peripheralManager.respond(to: request, withResult: .requestNotSupported) - return - } - let valueArgs = FlutterStandardTypedData(bytes: value) - api.onWriteCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, idArgs: idArgs, offsetArgs: offsetArgs, valueArgs: valueArgs) {_ in } - } - - func didSubscribeTo(_ central: CBCentral, _ characteristic: CBCharacteristic) { - let centralHashCode = central.hash - let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() } - let centralHashCodeArgs = centralArgs.hashCodeArgs - centrals[centralHashCodeArgs] = central - let characteristicHashCode = characteristic.hash - guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else { - return - } - let stateArgs = true - api.onNotifyCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, stateArgs: stateArgs) {_ in } - } - - func didUnsubscribeFrom(_ central: CBCentral, _ characteristic: CBCharacteristic) { - let centralHashCode = central.hash - let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() } - let centralHashCodeArgs = centralArgs.hashCodeArgs - centrals[centralHashCodeArgs] = central - let characteristicHashCode = characteristic.hash - guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else { - return - } - let stateArgs = false - api.onNotifyCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, stateArgs: stateArgs) {_ in } - } - - func isReadyToUpdateSubscribers() { - let callbacks = notifyCharacteristicValueChangedCallbacks - notifyCharacteristicValueChangedCallbacks.removeAll() - for callback in callbacks { - callback() - } + let stateNumberArgs = stateArgs.rawValue.toInt64() + _api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in } } } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManagerDelegate.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManagerDelegate.swift index e8c784d..87d66df 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManagerDelegate.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManagerDelegate.swift @@ -9,41 +9,41 @@ import Foundation import CoreBluetooth class MyPeripheralManagerDelegate: NSObject, CBPeripheralManagerDelegate { - init(_ peripheralManager: MyPeripheralManager) { - self.peripheralManager = peripheralManager + private let _peripheralManager: MyPeripheralManager + + init(peripheralManager: MyPeripheralManager) { + _peripheralManager = peripheralManager } - private let peripheralManager: MyPeripheralManager - func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) { - peripheralManager.didUpdateState() + _peripheralManager.didUpdateState(peripheral: peripheral) } func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) { - peripheralManager.didAdd(service, error) + _peripheralManager.didAdd(peripheral: peripheral, service: service, error: error) } func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) { - peripheralManager.didStartAdvertising(error) + _peripheralManager.didStartAdvertising(peripheral: peripheral, error: error) } func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) { - peripheralManager.didReceiveRead(request) + _peripheralManager.didReceiveRead(peripheral: peripheral, request: request) } func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) { - peripheralManager.didReceiveWrite(requests) + _peripheralManager.didReceiveWrite(peripheral: peripheral, requests: requests) } func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) { - peripheralManager.didSubscribeTo(central, characteristic) + _peripheralManager.didSubscribeTo(peripheral: peripheral, central: central, characteristic: characteristic) } func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) { - peripheralManager.didUnsubscribeFrom(central, characteristic) + _peripheralManager.didUnsubscribeFrom(peripheral: peripheral, central: central, characteristic: characteristic) } func peripheralManagerIsReady(toUpdateSubscribers peripheral: CBPeripheralManager) { - peripheralManager.isReadyToUpdateSubscribers() + _peripheralManager.isReadyToUpdateSubscribers(peripheral: peripheral) } } diff --git a/bluetooth_low_energy_darwin/example/ios/Podfile.lock b/bluetooth_low_energy_darwin/example/ios/Podfile.lock index 01f97ed..90d7b93 100644 --- a/bluetooth_low_energy_darwin/example/ios/Podfile.lock +++ b/bluetooth_low_energy_darwin/example/ios/Podfile.lock @@ -26,4 +26,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 -COCOAPODS: 1.12.1 +COCOAPODS: 1.14.3 diff --git a/bluetooth_low_energy_darwin/example/lib/main.dart b/bluetooth_low_energy_darwin/example/lib/main.dart index 813aa77..f6e2074 100644 --- a/bluetooth_low_energy_darwin/example/lib/main.dart +++ b/bluetooth_low_energy_darwin/example/lib/main.dart @@ -8,18 +8,17 @@ import 'package:convert/convert.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; -CentralManager get centralManager => CentralManager.instance; -PeripheralManager get peripheralManager => PeripheralManager.instance; - void main() { runZonedGuarded(onStartUp, onCrashed); } void onStartUp() async { Logger.root.onRecord.listen(onLogRecord); + // hierarchicalLoggingEnabled = true; + // CentralManager.instance.logLevel = Level.WARNING; WidgetsFlutterBinding.ensureInitialized(); - await centralManager.setUp(); - await peripheralManager.setUp(); + await CentralManager.instance.setUp(); + await PeripheralManager.instance.setUp(); runApp(const MyApp()); } @@ -58,6 +57,8 @@ class _MyAppState extends State { return MaterialApp( theme: ThemeData.light( useMaterial3: true, + ).copyWith( + materialTapTargetSize: MaterialTapTargetSize.padded, ), home: const HomeView(), routes: { @@ -168,15 +169,15 @@ class _ScannerViewState extends State { @override void initState() { super.initState(); - state = ValueNotifier(centralManager.state); + state = ValueNotifier(BluetoothLowEnergyState.unknown); discovering = ValueNotifier(false); discoveredEventArgs = ValueNotifier([]); - stateChangedSubscription = centralManager.stateChanged.listen( + stateChangedSubscription = CentralManager.instance.stateChanged.listen( (eventArgs) { state.value = eventArgs.state; }, ); - discoveredSubscription = centralManager.discovered.listen( + discoveredSubscription = CentralManager.instance.discovered.listen( (eventArgs) { final items = discoveredEventArgs.value; final i = items.indexWhere( @@ -190,6 +191,11 @@ class _ScannerViewState extends State { } }, ); + _initialize(); + } + + void _initialize() async { + state.value = await CentralManager.instance.getState(); } @override @@ -233,12 +239,13 @@ class _ScannerViewState extends State { } Future startDiscovery() async { - await centralManager.startDiscovery(); + discoveredEventArgs.value = []; + await CentralManager.instance.startDiscovery(); discovering.value = true; } Future stopDiscovery() async { - await centralManager.stopDiscovery(); + await CentralManager.instance.stopDiscovery(); discovering.value = false; } @@ -340,7 +347,13 @@ class _ScannerViewState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, ), - trailing: RssiWidget(rssi), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RssiWidget(rssi), + Text('$rssi'), + ], + ), ); }, separatorBuilder: (context, i) { @@ -378,44 +391,39 @@ class PeripheralView extends StatefulWidget { } class _PeripheralViewState extends State { - late final ValueNotifier state; + late final ValueNotifier connectionState; late final DiscoveredEventArgs eventArgs; late final ValueNotifier> services; late final ValueNotifier> characteristics; late final ValueNotifier service; late final ValueNotifier characteristic; late final ValueNotifier writeType; - late final ValueNotifier maximumWriteLength; - late final ValueNotifier rssi; late final ValueNotifier> logs; late final TextEditingController writeController; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription valueChangedSubscription; - late final StreamSubscription rssiChangedSubscription; - late final Timer rssiTimer; + late final StreamSubscription connectionStateChangedSubscription; + late final StreamSubscription characteristicNotifiedSubscription; @override void initState() { super.initState(); eventArgs = widget.eventArgs; - state = ValueNotifier(false); + connectionState = ValueNotifier(false); services = ValueNotifier([]); characteristics = ValueNotifier([]); service = ValueNotifier(null); characteristic = ValueNotifier(null); writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); - maximumWriteLength = ValueNotifier(0); - rssi = ValueNotifier(-100); logs = ValueNotifier([]); writeController = TextEditingController(); - stateChangedSubscription = centralManager.peripheralStateChanged.listen( + connectionStateChangedSubscription = + CentralManager.instance.connectionStateChanged.listen( (eventArgs) { if (eventArgs.peripheral != this.eventArgs.peripheral) { return; } - final state = eventArgs.state; - this.state.value = state; - if (!state) { + final connectionState = eventArgs.connectionState; + this.connectionState.value = connectionState; + if (!connectionState) { services.value = []; characteristics.value = []; service.value = null; @@ -424,12 +432,13 @@ class _PeripheralViewState extends State { } }, ); - valueChangedSubscription = centralManager.characteristicValueChanged.listen( + characteristicNotifiedSubscription = + CentralManager.instance.characteristicNotified.listen( (eventArgs) { - final characteristic = this.characteristic.value; - if (eventArgs.characteristic != characteristic) { - return; - } + // final characteristic = this.characteristic.value; + // if (eventArgs.characteristic != characteristic) { + // return; + // } const type = LogType.notify; final log = Log(type, eventArgs.value); logs.value = [ @@ -438,28 +447,16 @@ class _PeripheralViewState extends State { ]; }, ); - rssiTimer = Timer.periodic( - const Duration(seconds: 5), - (timer) async { - final state = this.state.value; - if (state) { - rssi.value = await centralManager.readRSSI(eventArgs.peripheral); - } else { - rssi.value = -100; - } - }, - ); } @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () async { - if (state.value) { + return PopScope( + onPopInvoked: (didPop) async { + if (connectionState.value) { final peripheral = eventArgs.peripheral; - await centralManager.disconnect(peripheral); + await CentralManager.instance.disconnect(peripheral); } - return true; }, child: Scaffold( appBar: buildAppBar(context), @@ -474,25 +471,17 @@ class _PeripheralViewState extends State { title: Text(title), actions: [ ValueListenableBuilder( - valueListenable: state, + valueListenable: connectionState, builder: (context, state, child) { return TextButton( onPressed: () async { final peripheral = eventArgs.peripheral; if (state) { - await centralManager.disconnect(peripheral); - maximumWriteLength.value = 0; - rssi.value = 0; + await CentralManager.instance.disconnect(peripheral); } else { - await centralManager.connect(peripheral); + await CentralManager.instance.connect(peripheral); services.value = - await centralManager.discoverGATT(peripheral); - maximumWriteLength.value = - await centralManager.getMaximumWriteLength( - peripheral, - type: writeType.value, - ); - rssi.value = await centralManager.readRSSI(peripheral); + await CentralManager.instance.discoverGATT(peripheral); } }, child: Text(state ? 'DISCONNECT' : 'CONNECT'), @@ -566,7 +555,32 @@ class _PeripheralViewState extends State { hint: const Text('CHOOSE A CHARACTERISTIC'), value: characteristic, onChanged: (characteristic) { + if (characteristic == null) { + return; + } this.characteristic.value = characteristic; + final writeType = this.writeType.value; + final canWrite = characteristic.properties.contains( + GattCharacteristicProperty.write, + ); + final canWriteWithoutResponse = + characteristic.properties.contains( + GattCharacteristicProperty.writeWithoutResponse, + ); + if (writeType == + GattCharacteristicWriteType.withResponse && + !canWrite && + canWriteWithoutResponse) { + this.writeType.value = + GattCharacteristicWriteType.withoutResponse; + } + if (writeType == + GattCharacteristicWriteType.withoutResponse && + !canWriteWithoutResponse && + canWrite) { + this.writeType.value = + GattCharacteristicWriteType.withResponse; + } }, ); }, @@ -624,129 +638,45 @@ class _PeripheralViewState extends State { }, ), ), - Row( - children: [ - ValueListenableBuilder( - valueListenable: writeType, - builder: (context, writeType, child) { - return ToggleButtons( - onPressed: (i) async { - final type = GattCharacteristicWriteType.values[i]; - this.writeType.value = type; - maximumWriteLength.value = - await centralManager.getMaximumWriteLength( - eventArgs.peripheral, - type: type, - ); - }, - constraints: const BoxConstraints( - minWidth: 0.0, - minHeight: 0.0, - ), - borderRadius: BorderRadius.circular(4.0), - isSelected: GattCharacteristicWriteType.values - .map((type) => type == writeType) - .toList(), - children: GattCharacteristicWriteType.values.map((type) { - return Container( - margin: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 4.0, - ), - child: Text(type.name), - ); - }).toList(), - ); - // final segments = - // GattCharacteristicWriteType.values.map((type) { - // return ButtonSegment( - // value: type, - // label: Text(type.name), - // ); - // }).toList(); - // return SegmentedButton( - // segments: segments, - // selected: {writeType}, - // showSelectedIcon: false, - // style: OutlinedButton.styleFrom( - // tapTargetSize: MaterialTapTargetSize.shrinkWrap, - // padding: EdgeInsets.zero, - // visualDensity: VisualDensity.compact, - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(8.0), - // ), - // ), - // ); - }, - ), - const SizedBox(width: 8.0), - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: maximumWriteLength, - builder: (context, maximumWriteLength, child) { - return Text('$maximumWriteLength'); - }, - ); - }, - ), - const Spacer(), - ValueListenableBuilder( - valueListenable: rssi, - builder: (context, rssi, child) { - return RssiWidget(rssi); - }, - ), - ], - ), - Container( - margin: const EdgeInsets.only(bottom: 16.0), - height: 160.0, - child: ValueListenableBuilder( - valueListenable: characteristic, - builder: (context, characteristic, child) { - final bool canNotify, canRead, canWrite; - if (characteristic == null) { - canNotify = canRead = canWrite = false; - } else { - final properties = characteristic.properties; - canNotify = properties.contains( - GattCharacteristicProperty.notify, - ); - canRead = properties.contains( - GattCharacteristicProperty.read, - ); - canWrite = properties.contains( - GattCharacteristicProperty.write, - ); - } - return Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded( - child: TextField( - controller: writeController, - enabled: canWrite, - expands: true, - maxLines: null, - textAlignVertical: TextAlignVertical.top, - decoration: const InputDecoration( - border: OutlineInputBorder(), - contentPadding: EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 8.0, - ), - ), - ), - ), - Column( - mainAxisSize: MainAxisSize.min, + ValueListenableBuilder( + valueListenable: characteristic, + builder: (context, characteristic, chld) { + final bool canNotify, canRead, canWrite, canWriteWithoutResponse; + if (characteristic == null) { + canNotify = + canRead = canWrite = canWriteWithoutResponse = false; + } else { + final properties = characteristic.properties; + canNotify = properties.contains( + GattCharacteristicProperty.notify, + ) || + properties.contains( + GattCharacteristicProperty.indicate, + ); + canRead = properties.contains( + GattCharacteristicProperty.read, + ); + canWrite = properties.contains( + GattCharacteristicProperty.write, + ); + canWriteWithoutResponse = properties.contains( + GattCharacteristicProperty.writeWithoutResponse, + ); + } + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + margin: const EdgeInsets.symmetric(vertical: 4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ - TextButton( + ElevatedButton( onPressed: characteristic != null && canNotify ? () async { - await centralManager.notifyCharacteristic( + await CentralManager.instance + .setCharacteristicNotifyState( characteristic, state: true, ); @@ -754,10 +684,11 @@ class _PeripheralViewState extends State { : null, child: const Text('NOTIFY'), ), - TextButton( + const SizedBox(width: 8.0), + ElevatedButton( onPressed: characteristic != null && canRead ? () async { - final value = await centralManager + final value = await CentralManager.instance .readCharacteristic(characteristic); const type = LogType.read; final log = Log(type, value); @@ -765,31 +696,124 @@ class _PeripheralViewState extends State { } : null, child: const Text('READ'), - ), - TextButton( - onPressed: characteristic != null && canWrite - ? () async { - final text = writeController.text; - final elements = utf8.encode(text); - final value = Uint8List.fromList(elements); - final type = writeType.value; - await centralManager.writeCharacteristic( - characteristic, - value: value, - type: type, - ); - final log = Log(LogType.write, value); - logs.value = [...logs.value, log]; - } - : null, - child: const Text('WRITE'), - ), + ) ], ), - ], - ); - }, - ), + ), + SizedBox( + height: 160.0, + child: TextField( + controller: writeController, + enabled: canWrite || canWriteWithoutResponse, + expands: true, + maxLines: null, + textAlignVertical: TextAlignVertical.top, + decoration: const InputDecoration( + border: OutlineInputBorder(), + contentPadding: EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 8.0, + ), + ), + ), + ), + Row( + children: [ + ValueListenableBuilder( + valueListenable: writeType, + builder: (context, writeType, child) { + return ToggleButtons( + onPressed: canWrite || canWriteWithoutResponse + ? (i) { + if (!canWrite || !canWriteWithoutResponse) { + return; + } + final type = + GattCharacteristicWriteType.values[i]; + this.writeType.value = type; + } + : null, + constraints: const BoxConstraints( + minWidth: 0.0, + minHeight: 0.0, + ), + borderRadius: BorderRadius.circular(4.0), + isSelected: GattCharacteristicWriteType.values + .map((type) => type == writeType) + .toList(), + children: GattCharacteristicWriteType.values.map( + (type) { + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, + ), + child: Text(type.name), + ); + }, + ).toList(), + ); + // final segments = + // GattCharacteristicWriteType.values.map((type) { + // return ButtonSegment( + // value: type, + // label: Text(type.name), + // ); + // }).toList(); + // return SegmentedButton( + // segments: segments, + // selected: {writeType}, + // showSelectedIcon: false, + // style: OutlinedButton.styleFrom( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, + // padding: EdgeInsets.zero, + // visualDensity: VisualDensity.compact, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8.0), + // ), + // ), + // ); + }, + ), + const Spacer(), + ElevatedButton( + onPressed: characteristic != null && canWrite + ? () async { + final text = writeController.text; + final elements = utf8.encode(text); + final value = Uint8List.fromList(elements); + final type = writeType.value; + // Fragments the value by 512 bytes. + const fragmentSize = 512; + var start = 0; + while (start < value.length) { + final end = start + fragmentSize; + final fragmentedValue = end < value.length + ? value.sublist(start, end) + : value.sublist(start); + await CentralManager.instance + .writeCharacteristic( + characteristic, + value: fragmentedValue, + type: type, + ); + final log = Log( + LogType.write, + fragmentedValue, + ); + logs.value = [...logs.value, log]; + start = end; + } + } + : null, + child: const Text('WRITE'), + ), + ], + ), + const SizedBox(height: 16.0), + ], + ); + }, ), ], ), @@ -799,17 +823,14 @@ class _PeripheralViewState extends State { @override void dispose() { super.dispose(); - rssiTimer.cancel(); - stateChangedSubscription.cancel(); - valueChangedSubscription.cancel(); - state.dispose(); + connectionStateChangedSubscription.cancel(); + characteristicNotifiedSubscription.cancel(); + connectionState.dispose(); services.dispose(); characteristics.dispose(); service.dispose(); characteristic.dispose(); writeType.dispose(); - maximumWriteLength.dispose(); - rssi.dispose(); logs.dispose(); writeController.dispose(); } @@ -828,87 +849,63 @@ class _AdvertiserViewState extends State late final ValueNotifier advertising; late final ValueNotifier> logs; late final StreamSubscription stateChangedSubscription; - late final StreamSubscription readCharacteristicCommandReceivedSubscription; - late final StreamSubscription writeCharacteristicCommandReceivedSubscription; - late final StreamSubscription notifyCharacteristicCommandReceivedSubscription; + late final StreamSubscription characteristicReadSubscription; + late final StreamSubscription characteristicWrittenSubscription; + late final StreamSubscription characteristicNotifyStateChangedSubscription; @override void initState() { super.initState(); - state = ValueNotifier(peripheralManager.state); + state = ValueNotifier(BluetoothLowEnergyState.unknown); advertising = ValueNotifier(false); logs = ValueNotifier([]); - stateChangedSubscription = peripheralManager.stateChanged.listen( + stateChangedSubscription = PeripheralManager.instance.stateChanged.listen( (eventArgs) { state.value = eventArgs.state; }, ); - readCharacteristicCommandReceivedSubscription = - peripheralManager.readCharacteristicCommandReceived.listen( + characteristicReadSubscription = + PeripheralManager.instance.characteristicRead.listen( (eventArgs) async { final central = eventArgs.central; final characteristic = eventArgs.characteristic; - final id = eventArgs.id; - final offset = eventArgs.offset; + final value = eventArgs.value; final log = Log( LogType.read, - Uint8List.fromList([]), - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', + value, + 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', ); logs.value = [ ...logs.value, log, ]; - // final maximumWriteLength = peripheralManager.getMaximumWriteLength( - // central, - // ); - const status = true; - final value = Uint8List.fromList([0x01, 0x02, 0x03]); - await peripheralManager.sendReadCharacteristicReply( - central, - characteristic: characteristic, - id: id, - offset: offset, - status: status, - value: value, - ); }, ); - writeCharacteristicCommandReceivedSubscription = - peripheralManager.writeCharacteristicCommandReceived.listen( + characteristicWrittenSubscription = + PeripheralManager.instance.characteristicWritten.listen( (eventArgs) async { final central = eventArgs.central; final characteristic = eventArgs.characteristic; - final id = eventArgs.id; - final offset = eventArgs.offset; final value = eventArgs.value; final log = Log( LogType.write, value, - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', + 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', ); logs.value = [ ...logs.value, log, ]; - const status = true; - await peripheralManager.sendWriteCharacteristicReply( - central, - characteristic: characteristic, - id: id, - offset: offset, - status: status, - ); }, ); - notifyCharacteristicCommandReceivedSubscription = - peripheralManager.notifyCharacteristicCommandReceived.listen( + characteristicNotifyStateChangedSubscription = + PeripheralManager.instance.characteristicNotifyStateChanged.listen( (eventArgs) async { final central = eventArgs.central; final characteristic = eventArgs.characteristic; final state = eventArgs.state; final log = Log( - LogType.write, + LogType.notify, Uint8List.fromList([]), 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state', ); @@ -918,15 +915,21 @@ class _AdvertiserViewState extends State ]; // Write someting to the central when notify started. if (state) { - final value = Uint8List.fromList([0x03, 0x02, 0x01]); - await peripheralManager.notifyCharacteristicValueChanged( - central, - characteristic: characteristic, + final elements = List.generate(2000, (i) => i % 256); + final value = Uint8List.fromList(elements); + await PeripheralManager.instance.writeCharacteristic( + characteristic, value: value, + central: central, ); } }, ); + _initialize(); + } + + void _initialize() async { + state.value = await PeripheralManager.instance.getState(); } @override @@ -970,7 +973,9 @@ class _AdvertiserViewState extends State } Future startAdvertising() async { - await peripheralManager.clearServices(); + await PeripheralManager.instance.clearServices(); + final elements = List.generate(1000, (i) => i % 256); + final value = Uint8List.fromList(elements); final service = GattService( uuid: UUID.short(100), characteristics: [ @@ -979,15 +984,16 @@ class _AdvertiserViewState extends State properties: [ GattCharacteristicProperty.read, ], + value: value, descriptors: [], ), GattCharacteristic( uuid: UUID.short(201), properties: [ - GattCharacteristicProperty.read, GattCharacteristicProperty.write, GattCharacteristicProperty.writeWithoutResponse, ], + value: Uint8List.fromList([]), descriptors: [], ), GattCharacteristic( @@ -996,24 +1002,41 @@ class _AdvertiserViewState extends State GattCharacteristicProperty.notify, GattCharacteristicProperty.indicate, ], + value: Uint8List.fromList([]), + descriptors: [], + ), + GattCharacteristic( + uuid: UUID.short(203), + properties: [ + GattCharacteristicProperty.notify, + ], + value: Uint8List.fromList([]), + descriptors: [], + ), + GattCharacteristic( + uuid: UUID.short(204), + properties: [ + GattCharacteristicProperty.indicate, + ], + value: Uint8List.fromList([]), descriptors: [], ), ], ); - await peripheralManager.addService(service); + await PeripheralManager.instance.addService(service); final advertisement = Advertisement( - name: 'flutter', + name: 'le12138', manufacturerSpecificData: ManufacturerSpecificData( id: 0x2e19, data: Uint8List.fromList([0x01, 0x02, 0x03]), ), ); - await peripheralManager.startAdvertising(advertisement); + await PeripheralManager.instance.startAdvertising(advertisement); advertising.value = true; } Future stopAdvertising() async { - await peripheralManager.stopAdvertising(); + await PeripheralManager.instance.stopAdvertising(); advertising.value = false; } @@ -1074,9 +1097,9 @@ class _AdvertiserViewState extends State void dispose() { super.dispose(); stateChangedSubscription.cancel(); - readCharacteristicCommandReceivedSubscription.cancel(); - writeCharacteristicCommandReceivedSubscription.cancel(); - notifyCharacteristicCommandReceivedSubscription.cancel(); + characteristicReadSubscription.cancel(); + characteristicWrittenSubscription.cancel(); + characteristicNotifyStateChangedSubscription.cancel(); state.dispose(); advertising.dispose(); logs.dispose(); diff --git a/bluetooth_low_energy_darwin/example/macos/Podfile.lock b/bluetooth_low_energy_darwin/example/macos/Podfile.lock index 6e81df1..c3133f1 100644 --- a/bluetooth_low_energy_darwin/example/macos/Podfile.lock +++ b/bluetooth_low_energy_darwin/example/macos/Podfile.lock @@ -20,4 +20,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.12.1 +COCOAPODS: 1.14.3 diff --git a/bluetooth_low_energy_darwin/example/pubspec.lock b/bluetooth_low_energy_darwin/example/pubspec.lock index aa96aca..7896fe6 100644 --- a/bluetooth_low_energy_darwin/example/pubspec.lock +++ b/bluetooth_low_energy_darwin/example/pubspec.lock @@ -15,15 +15,15 @@ packages: path: ".." relative: true source: path - version: "4.0.0" + version: "5.0.0" bluetooth_low_energy_platform_interface: dependency: "direct main" description: name: bluetooth_low_energy_platform_interface - sha256: a01819f4ef89d033edaa979465ec8c3af13b2618dc718d90fe681be91b6c4356 + sha256: "54f92ab2d7746fb6f2b4a40a48cd7eb8e3bf772f3ee89e1979d4d7b741fb2a05" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" boolean_selector: dependency: transitive description: @@ -52,10 +52,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: "direct main" description: @@ -102,10 +102,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -125,10 +125,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" lints: dependency: transitive description: @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -189,18 +189,18 @@ packages: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" process: dependency: transitive description: @@ -226,18 +226,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -266,10 +266,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" typed_data: dependency: transitive description: @@ -290,18 +290,18 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 url: "https://pub.dev" source: hosted - version: "11.7.1" + version: "11.10.0" web: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" webdriver: dependency: transitive description: @@ -311,5 +311,5 @@ packages: source: hosted version: "3.0.2" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.3.0" diff --git a/bluetooth_low_energy_darwin/example/pubspec.yaml b/bluetooth_low_energy_darwin/example/pubspec.yaml index f7f26bc..d904d36 100644 --- a/bluetooth_low_energy_darwin/example/pubspec.yaml +++ b/bluetooth_low_energy_darwin/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^4.0.0 + bluetooth_low_energy_platform_interface: ^5.0.0 bluetooth_low_energy_darwin: # When depending on this package from a real application you should use: # bluetooth_low_energy: ^x.y.z @@ -30,7 +30,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 convert: ^3.1.1 - intl: ^0.18.1 + intl: ^0.19.0 logging: ^1.2.0 dev_dependencies: diff --git a/bluetooth_low_energy_darwin/lib/bluetooth_low_energy_darwin.dart b/bluetooth_low_energy_darwin/lib/bluetooth_low_energy_darwin.dart index d38f2ba..1666f3d 100644 --- a/bluetooth_low_energy_darwin/lib/bluetooth_low_energy_darwin.dart +++ b/bluetooth_low_energy_darwin/lib/bluetooth_low_energy_darwin.dart @@ -1,11 +1,11 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'src/my_central_manager_2.dart'; -import 'src/my_peripheral_manager_2.dart'; +import 'src/my_central_manager.dart'; +import 'src/my_peripheral_manager.dart'; abstract class BluetoothLowEnergyDarwin { static void registerWith() { - MyCentralManager.instance = MyCentralManager2(); - MyPeripheralManager.instance = MyPeripheralManager2(); + CentralManager.instance = MyCentralManager(); + PeripheralManager.instance = MyPeripheralManager(); } } diff --git a/bluetooth_low_energy_darwin/lib/src/my_api.dart b/bluetooth_low_energy_darwin/lib/src/my_api.dart index 940f859..2f6f5ed 100644 --- a/bluetooth_low_energy_darwin/lib/src/my_api.dart +++ b/bluetooth_low_energy_darwin/lib/src/my_api.dart @@ -9,22 +9,51 @@ import 'my_gatt_service2.dart'; export 'my_api.g.dart'; +// ToObject extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { BluetoothLowEnergyState toState() { - return BluetoothLowEnergyState.values[index]; + switch (this) { + case MyBluetoothLowEnergyStateArgs.unknown: + case MyBluetoothLowEnergyStateArgs.resetting: + return BluetoothLowEnergyState.unknown; + case MyBluetoothLowEnergyStateArgs.unsupported: + return BluetoothLowEnergyState.unsupported; + case MyBluetoothLowEnergyStateArgs.unauthorized: + return BluetoothLowEnergyState.unauthorized; + case MyBluetoothLowEnergyStateArgs.poweredOff: + return BluetoothLowEnergyState.poweredOff; + case MyBluetoothLowEnergyStateArgs.poweredOn: + return BluetoothLowEnergyState.poweredOn; + } + } +} + +extension MyGattCharacteristicPropertyArgsX + on MyGattCharacteristicPropertyArgs { + GattCharacteristicProperty toProperty() { + return GattCharacteristicProperty.values[index]; + } +} + +extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs { + ManufacturerSpecificData toManufacturerSpecificData() { + final id = idArgs; + final data = dataArgs; + return ManufacturerSpecificData( + id: id, + data: data, + ); } } extension MyAdvertisementArgsX on MyAdvertisementArgs { Advertisement toAdvertisement() { final name = nameArgs; - final serviceUUIDs = serviceUUIDsArgs - .cast() - .map((args) => UUID.fromString(args)) - .toList(); + final serviceUUIDs = + serviceUUIDsArgs.cast().map((args) => args.toUUID()).toList(); final serviceData = serviceDataArgs.cast().map( (uuidArgs, dataArgs) { - final uuid = UUID.fromString(uuidArgs); + final uuid = uuidArgs.toUUID(); final data = dataArgs; return MapEntry(uuid, data); }, @@ -40,61 +69,40 @@ extension MyAdvertisementArgsX on MyAdvertisementArgs { } } -extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs { - ManufacturerSpecificData toManufacturerSpecificData() { - final id = idArgs; - final data = dataArgs; - return ManufacturerSpecificData( - id: id, - data: data, +extension MyCentralArgsX on MyCentralArgs { + MyCentral toCentral() { + final uuid = uuidArgs.toUUID(); + return MyCentral( + uuid: uuid, ); } } -extension MyGattCharacteristicPropertyArgsX - on MyGattCharacteristicPropertyArgs { - GattCharacteristicProperty toProperty() { - return GattCharacteristicProperty.values[index]; - } -} - -extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType { - MyGattCharacteristicWriteTypeArgs toArgs() { - return MyGattCharacteristicWriteTypeArgs.values[index]; - } -} - extension MyPeripheralArgsX on MyPeripheralArgs { - Peripheral toPeripheral() { - final hashCode = hashCodeArgs; - final uuid = UUID.fromString(uuidArgs); + MyPeripheral toPeripheral() { + final uuid = uuidArgs.toUUID(); return MyPeripheral( - hashCode: hashCode, uuid: uuid, ); } } -extension MyGattServiceArgsX on MyGattServiceArgs { - MyGattService2 toService2() { +extension MyGattDescriptorArgsX on MyGattDescriptorArgs { + MyGattDescriptor2 toDescriptor2(MyPeripheral peripheral) { final hashCode = hashCodeArgs; - final uuid = UUID.fromString(uuidArgs); - final characteristics = characteristicsArgs - .cast() - .map((args) => args.toCharacteristic2()) - .toList(); - return MyGattService2( + final uuid = uuidArgs.toUUID(); + return MyGattDescriptor2( + peripheral: peripheral, hashCode: hashCode, uuid: uuid, - characteristics: characteristics, ); } } extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs { - MyGattCharacteristic2 toCharacteristic2() { + MyGattCharacteristic2 toCharacteristic2(MyPeripheral peripheral) { final hashCode = hashCodeArgs; - final uuid = UUID.fromString(uuidArgs); + final uuid = uuidArgs.toUUID(); final properties = propertyNumbersArgs.cast().map( (args) { final propertyArgs = MyGattCharacteristicPropertyArgs.values[args]; @@ -103,9 +111,10 @@ extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs { ).toList(); final descriptors = descriptorsArgs .cast() - .map((args) => args.toDescriptor2()) + .map((args) => args.toDescriptor2(peripheral)) .toList(); return MyGattCharacteristic2( + peripheral: peripheral, hashCode: hashCode, uuid: uuid, properties: properties, @@ -114,45 +123,39 @@ extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs { } } -extension MyGattDescriptorArgsX on MyGattDescriptorArgs { - MyGattDescriptor2 toDescriptor2() { +extension MyGattServiceArgsX on MyGattServiceArgs { + MyGattService2 toService2(MyPeripheral peripheral) { final hashCode = hashCodeArgs; - final uuid = UUID.fromString(uuidArgs); - return MyGattDescriptor2( + final uuid = uuidArgs.toUUID(); + final characteristics = characteristicsArgs + .cast() + .map((args) => args.toCharacteristic2(peripheral)) + .toList(); + return MyGattService2( + peripheral: peripheral, hashCode: hashCode, uuid: uuid, + characteristics: characteristics, ); } } -extension MyCentralArgsX on MyCentralArgs { - MyCentral toCentral() { - final hashCode = hashCodeArgs; - final uuid = UUID.fromString(uuidArgs); - return MyCentral( - hashCode: hashCode, - uuid: uuid, - ); +extension MyUuidArgsX on String { + UUID toUUID() { + return UUID.fromString(this); } } -extension AdvertisementX on Advertisement { - MyAdvertisementArgs toArgs() { - final nameArgs = name; - final serviceUUIDsArgs = - serviceUUIDs.map((uuid) => uuid.toString()).toList(); - final serviceDataArgs = serviceData.map((uuid, data) { - final uuidArgs = uuid.toString(); - final dataArgs = data; - return MapEntry(uuidArgs, dataArgs); - }); - final manufacturerSpecificDataArgs = manufacturerSpecificData?.toArgs(); - return MyAdvertisementArgs( - nameArgs: nameArgs, - serviceUUIDsArgs: serviceUUIDsArgs, - serviceDataArgs: serviceDataArgs, - manufacturerSpecificDataArgs: manufacturerSpecificDataArgs, - ); +// ToArgs +extension GattCharacteristicPropertyX on GattCharacteristicProperty { + MyGattCharacteristicPropertyArgs toArgs() { + return MyGattCharacteristicPropertyArgs.values[index]; + } +} + +extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType { + MyGattCharacteristicWriteTypeArgs toArgs() { + return MyGattCharacteristicWriteTypeArgs.values[index]; } } @@ -167,18 +170,34 @@ extension ManufacturerSpecificDataX on ManufacturerSpecificData { } } -extension MyGattServiceX on MyGattService { - MyGattServiceArgs toArgs() { +extension AdvertisementX on Advertisement { + MyAdvertisementArgs toArgs() { + final nameArgs = name; + final serviceUUIDsArgs = serviceUUIDs.map((uuid) => uuid.toArgs()).toList(); + final serviceDataArgs = serviceData.map((uuid, data) { + final uuidArgs = uuid.toArgs(); + final dataArgs = data; + return MapEntry(uuidArgs, dataArgs); + }); + final manufacturerSpecificDataArgs = manufacturerSpecificData?.toArgs(); + return MyAdvertisementArgs( + nameArgs: nameArgs, + serviceUUIDsArgs: serviceUUIDsArgs, + serviceDataArgs: serviceDataArgs, + manufacturerSpecificDataArgs: manufacturerSpecificDataArgs, + ); + } +} + +extension MyGattDescriptorX on MyGattDescriptor { + MyGattDescriptorArgs toArgs() { final hashCodeArgs = hashCode; - final uuidArgs = uuid.toString(); - final characteristicsArgs = characteristics - .cast() - .map((characteristic) => characteristic.toArgs()) - .toList(); - return MyGattServiceArgs( + final uuidArgs = uuid.toArgs(); + final valueArgs = value; + return MyGattDescriptorArgs( hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, - characteristicsArgs: characteristicsArgs, + valueArgs: valueArgs, ); } } @@ -186,7 +205,7 @@ extension MyGattServiceX on MyGattService { extension MyGattCharacteristicX on MyGattCharacteristic { MyGattCharacteristicArgs toArgs() { final hashCodeArgs = hashCode; - final uuidArgs = uuid.toString(); + final uuidArgs = uuid.toArgs(); final propertyNumbersArgs = properties.map((property) { final propertyArgs = property.toArgs(); return propertyArgs.index; @@ -204,21 +223,24 @@ extension MyGattCharacteristicX on MyGattCharacteristic { } } -extension MyGattDescriptorX on MyGattDescriptor { - MyGattDescriptorArgs toArgs() { +extension MyGattServiceX on MyGattService { + MyGattServiceArgs toArgs() { final hashCodeArgs = hashCode; - final uuidArgs = uuid.toString(); - final valueArgs = value; - return MyGattDescriptorArgs( + final uuidArgs = uuid.toArgs(); + final characteristicsArgs = characteristics + .cast() + .map((characteristic) => characteristic.toArgs()) + .toList(); + return MyGattServiceArgs( hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, - valueArgs: valueArgs, + characteristicsArgs: characteristicsArgs, ); } } -extension GattCharacteristicPropertyX on GattCharacteristicProperty { - MyGattCharacteristicPropertyArgs toArgs() { - return MyGattCharacteristicPropertyArgs.values[index]; +extension UuidX on UUID { + String toArgs() { + return toString().toLowerCase(); } } diff --git a/bluetooth_low_energy_darwin/lib/src/my_api.g.dart b/bluetooth_low_energy_darwin/lib/src/my_api.g.dart index b275177..e775b0d 100644 --- a/bluetooth_low_energy_darwin/lib/src/my_api.g.dart +++ b/bluetooth_low_energy_darwin/lib/src/my_api.g.dart @@ -1,12 +1,20 @@ -// Autogenerated from Pigeon (v12.0.1), do not edit directly. +// Autogenerated from Pigeon (v15.0.2), 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 +// 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 import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; @@ -19,6 +27,7 @@ List wrapResponse({Object? result, PlatformException? error, bool empty enum MyBluetoothLowEnergyStateArgs { unknown, + resetting, unsupported, unauthorized, poweredOff, @@ -38,96 +47,49 @@ enum MyGattCharacteristicWriteTypeArgs { withoutResponse, } -class MyCentralManagerArgs { - MyCentralManagerArgs({ - required this.stateNumberArgs, - }); - - int stateNumberArgs; - - Object encode() { - return [ - stateNumberArgs, - ]; - } - - static MyCentralManagerArgs decode(Object result) { - result as List; - return MyCentralManagerArgs( - stateNumberArgs: result[0]! as int, - ); - } +enum MyGattErrorArgs { + success, + invalidHandle, + readNotPermitted, + writeNotPermitted, + invalidPDU, + insufficientAuthentication, + requestNotSupported, + invalidOffset, + insufficientAuthorization, + prepareQueueFull, + attributeNotFound, + attributeNotLong, + insufficientEncryptionKeySize, + invalidAttributeValueLength, + unlikelyError, + insufficientEncryption, + unsupportedGroupType, + insufficientResources, } -class MyPeripheralManagerArgs { - MyPeripheralManagerArgs({ - required this.stateNumberArgs, +class MyManufacturerSpecificDataArgs { + MyManufacturerSpecificDataArgs({ + required this.idArgs, + required this.dataArgs, }); - int stateNumberArgs; + int idArgs; + + Uint8List dataArgs; Object encode() { return [ - stateNumberArgs, + idArgs, + dataArgs, ]; } - static MyPeripheralManagerArgs decode(Object result) { + static MyManufacturerSpecificDataArgs decode(Object result) { result as List; - return MyPeripheralManagerArgs( - stateNumberArgs: result[0]! as int, - ); - } -} - -class MyCentralArgs { - MyCentralArgs({ - required this.hashCodeArgs, - required this.uuidArgs, - }); - - int hashCodeArgs; - - String uuidArgs; - - Object encode() { - return [ - hashCodeArgs, - uuidArgs, - ]; - } - - static MyCentralArgs decode(Object result) { - result as List; - return MyCentralArgs( - hashCodeArgs: result[0]! as int, - uuidArgs: result[1]! as String, - ); - } -} - -class MyPeripheralArgs { - MyPeripheralArgs({ - required this.hashCodeArgs, - required this.uuidArgs, - }); - - int hashCodeArgs; - - String uuidArgs; - - Object encode() { - return [ - hashCodeArgs, - uuidArgs, - ]; - } - - static MyPeripheralArgs decode(Object result) { - result as List; - return MyPeripheralArgs( - hashCodeArgs: result[0]! as int, - uuidArgs: result[1]! as String, + return MyManufacturerSpecificDataArgs( + idArgs: result[0]! as int, + dataArgs: result[1]! as Uint8List, ); } } @@ -170,59 +132,75 @@ class MyAdvertisementArgs { } } -class MyManufacturerSpecificDataArgs { - MyManufacturerSpecificDataArgs({ - required this.idArgs, - required this.dataArgs, +class MyCentralArgs { + MyCentralArgs({ + required this.uuidArgs, }); - int idArgs; - - Uint8List dataArgs; + String uuidArgs; Object encode() { return [ - idArgs, - dataArgs, + uuidArgs, ]; } - static MyManufacturerSpecificDataArgs decode(Object result) { + static MyCentralArgs decode(Object result) { result as List; - return MyManufacturerSpecificDataArgs( - idArgs: result[0]! as int, - dataArgs: result[1]! as Uint8List, + return MyCentralArgs( + uuidArgs: result[0]! as String, ); } } -class MyGattServiceArgs { - MyGattServiceArgs({ +class MyPeripheralArgs { + MyPeripheralArgs({ + required this.uuidArgs, + }); + + String uuidArgs; + + Object encode() { + return [ + uuidArgs, + ]; + } + + static MyPeripheralArgs decode(Object result) { + result as List; + return MyPeripheralArgs( + uuidArgs: result[0]! as String, + ); + } +} + +class MyGattDescriptorArgs { + MyGattDescriptorArgs({ required this.hashCodeArgs, required this.uuidArgs, - required this.characteristicsArgs, + this.valueArgs, }); int hashCodeArgs; String uuidArgs; - List characteristicsArgs; + Uint8List? valueArgs; Object encode() { return [ hashCodeArgs, uuidArgs, - characteristicsArgs, + valueArgs, ]; } - static MyGattServiceArgs decode(Object result) { + static MyGattDescriptorArgs decode(Object result) { result as List; - return MyGattServiceArgs( + return MyGattDescriptorArgs( hashCodeArgs: result[0]! as int, uuidArgs: result[1]! as String, - characteristicsArgs: (result[2] as List?)!.cast(), + valueArgs: result[2] as Uint8List?, ); } } @@ -263,33 +241,33 @@ class MyGattCharacteristicArgs { } } -class MyGattDescriptorArgs { - MyGattDescriptorArgs({ +class MyGattServiceArgs { + MyGattServiceArgs({ required this.hashCodeArgs, required this.uuidArgs, - this.valueArgs, + required this.characteristicsArgs, }); int hashCodeArgs; String uuidArgs; - Uint8List? valueArgs; + List characteristicsArgs; Object encode() { return [ hashCodeArgs, uuidArgs, - valueArgs, + characteristicsArgs, ]; } - static MyGattDescriptorArgs decode(Object result) { + static MyGattServiceArgs decode(Object result) { result as List; - return MyGattDescriptorArgs( + return MyGattServiceArgs( hashCodeArgs: result[0]! as int, uuidArgs: result[1]! as String, - valueArgs: result[2] as Uint8List?, + characteristicsArgs: (result[2] as List?)!.cast(), ); } } @@ -298,17 +276,14 @@ class _MyCentralManagerHostApiCodec extends StandardMessageCodec { const _MyCentralManagerHostApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is MyCentralManagerArgs) { + if (value is MyGattCharacteristicArgs) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is MyGattCharacteristicArgs) { + } else if (value is MyGattDescriptorArgs) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is MyGattDescriptorArgs) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); } else if (value is MyGattServiceArgs) { - buffer.putUint8(131); + buffer.putUint8(130); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -319,12 +294,10 @@ class _MyCentralManagerHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return MyCentralManagerArgs.decode(readValue(buffer)!); - case 129: return MyGattCharacteristicArgs.decode(readValue(buffer)!); - case 130: + case 129: return MyGattDescriptorArgs.decode(readValue(buffer)!); - case 131: + case 130: return MyGattServiceArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -337,54 +310,49 @@ class MyCentralManagerHostApi { /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. MyCentralManagerHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec codec = _MyCentralManagerHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyCentralManagerHostApiCodec(); - Future setUp() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setUp', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + Future setUp() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setUp'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { - return (replyList[0] as MyCentralManagerArgs?)!; + return; } } Future startDiscovery() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.startDiscovery', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.startDiscovery'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; @@ -392,266 +360,320 @@ class MyCentralManagerHostApi { } Future stopDiscovery() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.stopDiscovery', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.stopDiscovery'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future connect(int arg_peripheralHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.connect', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs]) as List?; - if (replyList == null) { + Future connect(String uuidArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.connect'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future disconnect(int arg_peripheralHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.disconnect', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs]) as List?; - if (replyList == null) { + Future disconnect(String uuidArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.disconnect'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future getMaximumWriteLength(int arg_peripheralHashCodeArgs, int arg_typeNumberArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.getMaximumWriteLength', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_typeNumberArgs]) as List?; - if (replyList == null) { + Future getMaximumWriteValueLength(String uuidArgs, int typeNumberArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.getMaximumWriteValueLength'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs, typeNumberArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as int?)!; + return (__pigeon_replyList[0] as int?)!; } } - Future readRSSI(int arg_peripheralHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readRSSI', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs]) as List?; - if (replyList == null) { + Future readRSSI(String uuidArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readRSSI'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as int?)!; + return (__pigeon_replyList[0] as int?)!; } } - Future> discoverGATT(int arg_peripheralHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverGATT', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs]) as List?; - if (replyList == null) { + Future> discoverServices(String uuidArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverServices'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as List?)!.cast(); + return (__pigeon_replyList[0] as List?)!.cast(); } } - Future readCharacteristic(int arg_peripheralHashCodeArgs, int arg_characteristicHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readCharacteristic', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_characteristicHashCodeArgs]) as List?; - if (replyList == null) { + Future> discoverCharacteristics(String uuidArgs, int hashCodeArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverCharacteristics'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs, hashCodeArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as Uint8List?)!; + return (__pigeon_replyList[0] as List?)!.cast(); } } - Future writeCharacteristic(int arg_peripheralHashCodeArgs, int arg_characteristicHashCodeArgs, Uint8List arg_valueArgs, int arg_typeNumberArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.writeCharacteristic', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_characteristicHashCodeArgs, arg_valueArgs, arg_typeNumberArgs]) as List?; - if (replyList == null) { + Future> discoverDescriptors(String uuidArgs, int hashCodeArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverDescriptors'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs, hashCodeArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as List?)!.cast(); + } + } + + Future readCharacteristic(String uuidArgs, int hashCodeArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readCharacteristic'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs, hashCodeArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + 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 (__pigeon_replyList[0] as Uint8List?)!; + } + } + + Future writeCharacteristic(String uuidArgs, int hashCodeArgs, Uint8List valueArgs, int typeNumberArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.writeCharacteristic'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs, hashCodeArgs, valueArgs, typeNumberArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future notifyCharacteristic(int arg_peripheralHashCodeArgs, int arg_characteristicHashCodeArgs, bool arg_stateArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.notifyCharacteristic', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_characteristicHashCodeArgs, arg_stateArgs]) as List?; - if (replyList == null) { + Future setCharacteristicNotifyState(String uuidArgs, int hashCodeArgs, bool stateArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setCharacteristicNotifyState'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs, hashCodeArgs, stateArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future readDescriptor(int arg_peripheralHashCodeArgs, int arg_descriptorHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readDescriptor', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_descriptorHashCodeArgs]) as List?; - if (replyList == null) { + Future readDescriptor(String uuidArgs, int hashCodeArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readDescriptor'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs, hashCodeArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as Uint8List?)!; + return (__pigeon_replyList[0] as Uint8List?)!; } } - Future writeDescriptor(int arg_peripheralHashCodeArgs, int arg_descriptorHashCodeArgs, Uint8List arg_valueArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.writeDescriptor', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_peripheralHashCodeArgs, arg_descriptorHashCodeArgs, arg_valueArgs]) as List?; - if (replyList == null) { + Future writeDescriptor(String uuidArgs, int hashCodeArgs, Uint8List valueArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.writeDescriptor'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs, hashCodeArgs, valueArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; @@ -666,17 +688,11 @@ class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { if (value is MyAdvertisementArgs) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is MyGattCharacteristicArgs) { + } else if (value is MyManufacturerSpecificDataArgs) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is MyGattDescriptorArgs) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is MyManufacturerSpecificDataArgs) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); } else if (value is MyPeripheralArgs) { - buffer.putUint8(132); + buffer.putUint8(130); writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); @@ -689,12 +705,8 @@ class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { case 128: return MyAdvertisementArgs.decode(readValue(buffer)!); case 129: - return MyGattCharacteristicArgs.decode(readValue(buffer)!); - case 130: - return MyGattDescriptorArgs.decode(readValue(buffer)!); - case 131: return MyManufacturerSpecificDataArgs.decode(readValue(buffer)!); - case 132: + case 130: return MyPeripheralArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -703,25 +715,25 @@ class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { } abstract class MyCentralManagerFlutterApi { - static const MessageCodec codec = _MyCentralManagerFlutterApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyCentralManagerFlutterApiCodec(); void onStateChanged(int stateNumberArgs); void onDiscovered(MyPeripheralArgs peripheralArgs, int rssiArgs, MyAdvertisementArgs advertisementArgs); - void onPeripheralStateChanged(MyPeripheralArgs peripheralArgs, bool stateArgs); + void onConnectionStateChanged(String uuidArgs, bool stateArgs); - void onCharacteristicValueChanged(MyGattCharacteristicArgs characteristicArgs, Uint8List valueArgs); + void onCharacteristicNotified(String uuidArgs, int hashCodeArgs, Uint8List valueArgs); static void setup(MyCentralManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onStateChanged', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onStateChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onStateChanged was null.'); final List args = (message as List?)!; @@ -740,13 +752,13 @@ abstract class MyCentralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onDiscovered', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onDiscovered', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onDiscovered was null.'); final List args = (message as List?)!; @@ -771,24 +783,24 @@ abstract class MyCentralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onPeripheralStateChanged', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onConnectionStateChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onPeripheralStateChanged was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onConnectionStateChanged was null.'); final List args = (message as List?)!; - final MyPeripheralArgs? arg_peripheralArgs = (args[0] as MyPeripheralArgs?); - assert(arg_peripheralArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onPeripheralStateChanged was null, expected non-null MyPeripheralArgs.'); + final String? arg_uuidArgs = (args[0] as String?); + assert(arg_uuidArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onConnectionStateChanged was null, expected non-null String.'); final bool? arg_stateArgs = (args[1] as bool?); assert(arg_stateArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onPeripheralStateChanged was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onConnectionStateChanged was null, expected non-null bool.'); try { - api.onPeripheralStateChanged(arg_peripheralArgs!, arg_stateArgs!); + api.onConnectionStateChanged(arg_uuidArgs!, arg_stateArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -799,24 +811,27 @@ abstract class MyCentralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicValueChanged', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicNotified', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicValueChanged was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicNotified was null.'); final List args = (message as List?)!; - final MyGattCharacteristicArgs? arg_characteristicArgs = (args[0] as MyGattCharacteristicArgs?); - assert(arg_characteristicArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicValueChanged was null, expected non-null MyGattCharacteristicArgs.'); - final Uint8List? arg_valueArgs = (args[1] as Uint8List?); + final String? arg_uuidArgs = (args[0] as String?); + assert(arg_uuidArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicNotified was null, expected non-null String.'); + final int? arg_hashCodeArgs = (args[1] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicNotified was null, expected non-null int.'); + final Uint8List? arg_valueArgs = (args[2] as Uint8List?); assert(arg_valueArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicValueChanged was null, expected non-null Uint8List.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicNotified was null, expected non-null Uint8List.'); try { - api.onCharacteristicValueChanged(arg_characteristicArgs!, arg_valueArgs!); + api.onCharacteristicNotified(arg_uuidArgs!, arg_hashCodeArgs!, arg_valueArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -848,9 +863,6 @@ class _MyPeripheralManagerHostApiCodec extends StandardMessageCodec { } else if (value is MyManufacturerSpecificDataArgs) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is MyPeripheralManagerArgs) { - buffer.putUint8(133); - writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -869,8 +881,6 @@ class _MyPeripheralManagerHostApiCodec extends StandardMessageCodec { return MyGattServiceArgs.decode(readValue(buffer)!); case 132: return MyManufacturerSpecificDataArgs.decode(readValue(buffer)!); - case 133: - return MyPeripheralManagerArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -882,76 +892,71 @@ class MyPeripheralManagerHostApi { /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. MyPeripheralManagerHostApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec codec = _MyPeripheralManagerHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerHostApiCodec(); - Future setUp() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.setUp', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + Future setUp() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.setUp'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as MyPeripheralManagerArgs?)!; - } - } - - Future addService(MyGattServiceArgs arg_serviceArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.addService', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_serviceArgs]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future removeService(int arg_serviceHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.removeService', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_serviceHashCodeArgs]) as List?; - if (replyList == null) { + Future addService(MyGattServiceArgs serviceArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.addService'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([serviceArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { + } else { + return; + } + } + + Future removeService(int hashCodeArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.removeService'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([hashCodeArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; @@ -959,43 +964,43 @@ class MyPeripheralManagerHostApi { } Future clearServices() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.clearServices', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.clearServices'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future startAdvertising(MyAdvertisementArgs arg_advertisementArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.startAdvertising', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_advertisementArgs]) as List?; - if (replyList == null) { + Future startAdvertising(MyAdvertisementArgs advertisementArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.startAdvertising'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([advertisementArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; @@ -1003,114 +1008,92 @@ class MyPeripheralManagerHostApi { } Future stopAdvertising() async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.stopAdvertising', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send(null) as List?; - if (replyList == null) { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.stopAdvertising'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future getMaximumWriteLength(int arg_centralHashCodeArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.getMaximumWriteLength', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_centralHashCodeArgs]) as List?; - if (replyList == null) { + Future getMaximumUpdateValueLength(String uuidArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.getMaximumUpdateValueLength'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([uuidArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as int?)!; + return (__pigeon_replyList[0] as int?)!; } } - Future sendReadCharacteristicReply(int arg_centralHashCodeArgs, int arg_characteristicHashCodeArgs, int arg_idArgs, int arg_offsetArgs, bool arg_statusArgs, Uint8List arg_valueArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.sendReadCharacteristicReply', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_centralHashCodeArgs, arg_characteristicHashCodeArgs, arg_idArgs, arg_offsetArgs, arg_statusArgs, arg_valueArgs]) as List?; - if (replyList == null) { + Future respond(int idArgs, int errorNumberArgs, Uint8List? valueArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.respond'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([idArgs, errorNumberArgs, valueArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future sendWriteCharacteristicReply(int arg_centralHashCodeArgs, int arg_characteristicHashCodeArgs, int arg_idArgs, int arg_offsetArgs, bool arg_statusArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.sendWriteCharacteristicReply', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_centralHashCodeArgs, arg_characteristicHashCodeArgs, arg_idArgs, arg_offsetArgs, arg_statusArgs]) as List?; - if (replyList == null) { + Future updateCharacteristic(int hashCodeArgs, Uint8List valueArgs, List? uuidsArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.updateCharacteristic'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([hashCodeArgs, valueArgs, uuidsArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else { - return; - } - } - - Future notifyCharacteristicValueChanged(int arg_centralHashCodeArgs, int arg_characteristicHashCodeArgs, Uint8List arg_valueArgs) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.notifyCharacteristicValueChanged', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_centralHashCodeArgs, arg_characteristicHashCodeArgs, arg_valueArgs]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; @@ -1125,12 +1108,6 @@ class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { if (value is MyCentralArgs) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is MyGattCharacteristicArgs) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is MyGattDescriptorArgs) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -1141,10 +1118,6 @@ class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { switch (type) { case 128: return MyCentralArgs.decode(readValue(buffer)!); - case 129: - return MyGattCharacteristicArgs.decode(readValue(buffer)!); - case 130: - return MyGattDescriptorArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -1152,25 +1125,25 @@ class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { } abstract class MyPeripheralManagerFlutterApi { - static const MessageCodec codec = _MyPeripheralManagerFlutterApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerFlutterApiCodec(); void onStateChanged(int stateNumberArgs); - void onReadCharacteristicCommandReceived(MyCentralArgs centralArgs, MyGattCharacteristicArgs characteristicArgs, int idArgs, int offsetArgs); + void onCharacteristicReadRequest(MyCentralArgs centralArgs, int hashCodeArgs, int idArgs, int offsetArgs); - void onWriteCharacteristicCommandReceived(MyCentralArgs centralArgs, MyGattCharacteristicArgs characteristicArgs, int idArgs, int offsetArgs, Uint8List valueArgs); + void onCharacteristicWriteRequest(MyCentralArgs centralArgs, int hashCodeArgs, int idArgs, int offsetArgs, Uint8List valueArgs); - void onNotifyCharacteristicCommandReceived(MyCentralArgs centralArgs, MyGattCharacteristicArgs characteristicArgs, bool stateArgs); + void onCharacteristicNotifyStateChanged(MyCentralArgs centralArgs, int hashCodeArgs, bool stateArgs); static void setup(MyPeripheralManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onStateChanged', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onStateChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onStateChanged was null.'); final List args = (message as List?)!; @@ -1189,30 +1162,30 @@ abstract class MyPeripheralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was null.'); final List args = (message as List?)!; final MyCentralArgs? arg_centralArgs = (args[0] as MyCentralArgs?); assert(arg_centralArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null, expected non-null MyCentralArgs.'); - final MyGattCharacteristicArgs? arg_characteristicArgs = (args[1] as MyGattCharacteristicArgs?); - assert(arg_characteristicArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null, expected non-null MyGattCharacteristicArgs.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was null, expected non-null MyCentralArgs.'); + final int? arg_hashCodeArgs = (args[1] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was null, expected non-null int.'); final int? arg_idArgs = (args[2] as int?); assert(arg_idArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was null, expected non-null int.'); final int? arg_offsetArgs = (args[3] as int?); assert(arg_offsetArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was null, expected non-null int.'); try { - api.onReadCharacteristicCommandReceived(arg_centralArgs!, arg_characteristicArgs!, arg_idArgs!, arg_offsetArgs!); + api.onCharacteristicReadRequest(arg_centralArgs!, arg_hashCodeArgs!, arg_idArgs!, arg_offsetArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1223,33 +1196,33 @@ abstract class MyPeripheralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null.'); final List args = (message as List?)!; final MyCentralArgs? arg_centralArgs = (args[0] as MyCentralArgs?); assert(arg_centralArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null MyCentralArgs.'); - final MyGattCharacteristicArgs? arg_characteristicArgs = (args[1] as MyGattCharacteristicArgs?); - assert(arg_characteristicArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null MyGattCharacteristicArgs.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null MyCentralArgs.'); + final int? arg_hashCodeArgs = (args[1] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null int.'); final int? arg_idArgs = (args[2] as int?); assert(arg_idArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null int.'); final int? arg_offsetArgs = (args[3] as int?); assert(arg_offsetArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null int.'); final Uint8List? arg_valueArgs = (args[4] as Uint8List?); assert(arg_valueArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null Uint8List.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null, expected non-null Uint8List.'); try { - api.onWriteCharacteristicCommandReceived(arg_centralArgs!, arg_characteristicArgs!, arg_idArgs!, arg_offsetArgs!, arg_valueArgs!); + api.onCharacteristicWriteRequest(arg_centralArgs!, arg_hashCodeArgs!, arg_idArgs!, arg_offsetArgs!, arg_valueArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1260,27 +1233,27 @@ abstract class MyPeripheralManagerFlutterApi { } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived', codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - channel.setMessageHandler(null); + __pigeon_channel.setMessageHandler(null); } else { - channel.setMessageHandler((Object? message) async { + __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null.'); final List args = (message as List?)!; final MyCentralArgs? arg_centralArgs = (args[0] as MyCentralArgs?); assert(arg_centralArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived was null, expected non-null MyCentralArgs.'); - final MyGattCharacteristicArgs? arg_characteristicArgs = (args[1] as MyGattCharacteristicArgs?); - assert(arg_characteristicArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived was null, expected non-null MyGattCharacteristicArgs.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null, expected non-null MyCentralArgs.'); + final int? arg_hashCodeArgs = (args[1] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null, expected non-null int.'); final bool? arg_stateArgs = (args[2] as bool?); assert(arg_stateArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null, expected non-null bool.'); try { - api.onNotifyCharacteristicCommandReceived(arg_centralArgs!, arg_characteristicArgs!, arg_stateArgs!); + api.onCharacteristicNotifyStateChanged(arg_centralArgs!, arg_hashCodeArgs!, arg_stateArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/bluetooth_low_energy_darwin/lib/src/my_central_manager.dart b/bluetooth_low_energy_darwin/lib/src/my_central_manager.dart new file mode 100644 index 0000000..dbd8f41 --- /dev/null +++ b/bluetooth_low_energy_darwin/lib/src/my_central_manager.dart @@ -0,0 +1,331 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:flutter/foundation.dart'; + +import 'my_api.dart'; +import 'my_gatt_characteristic2.dart'; +import 'my_gatt_descriptor2.dart'; + +class MyCentralManager extends CentralManager + implements MyCentralManagerFlutterApi { + final MyCentralManagerHostApi _api; + final StreamController + _stateChangedController; + final StreamController _discoveredController; + final StreamController + _connectionStateChangedController; + final StreamController + _characteristicNotifiedController; + final Map _peripherals; + final Map> _characteristics; + + BluetoothLowEnergyState _state; + + MyCentralManager() + : _api = MyCentralManagerHostApi(), + _stateChangedController = StreamController.broadcast(), + _discoveredController = StreamController.broadcast(), + _connectionStateChangedController = StreamController.broadcast(), + _characteristicNotifiedController = StreamController.broadcast(), + _peripherals = {}, + _characteristics = {}, + _state = BluetoothLowEnergyState.unknown; + + @override + Stream get stateChanged => + _stateChangedController.stream; + @override + Stream get discovered => _discoveredController.stream; + @override + Stream get connectionStateChanged => + _connectionStateChangedController.stream; + @override + Stream get characteristicNotified => + _characteristicNotifiedController.stream; + + @override + Future setUp() async { + logger.info('setUp'); + await _api.setUp(); + MyCentralManagerFlutterApi.setup(this); + } + + @override + Future getState() { + logger.info('getState'); + return Future.value(_state); + } + + @override + Future startDiscovery() async { + logger.info('startDiscovery'); + await _api.startDiscovery(); + } + + @override + Future stopDiscovery() async { + logger.info('stopDiscovery'); + await _api.stopDiscovery(); + } + + @override + Future connect(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { + throw TypeError(); + } + final uuidArgs = peripheral.uuid.toArgs(); + logger.info('connect: $uuidArgs'); + await _api.connect(uuidArgs); + } + + @override + Future disconnect(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { + throw TypeError(); + } + final uuidArgs = peripheral.uuid.toArgs(); + logger.info('disconnect: $uuidArgs'); + await _api.disconnect(uuidArgs); + } + + @override + Future readRSSI(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { + throw TypeError(); + } + final uuidArgs = peripheral.uuid.toArgs(); + logger.info('readRSSI: $uuidArgs'); + final rssi = await _api.readRSSI(uuidArgs); + return rssi; + } + + @override + Future> discoverGATT(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { + throw TypeError(); + } + // 发现 GATT 服务 + final uuidArgs = peripheral.uuid.toArgs(); + logger.info('discoverServices: $uuidArgs'); + final servicesArgs = await _api + .discoverServices(uuidArgs) + .then((args) => args.cast()); + for (var serviceArgs in servicesArgs) { + // 发现 GATT 特征值 + final hashCodeArgs = serviceArgs.hashCodeArgs; + logger.info('discoverCharacteristics: $uuidArgs.$hashCodeArgs'); + final characteristicsArgs = await _api + .discoverCharacteristics(uuidArgs, hashCodeArgs) + .then((args) => args.cast()); + for (var characteristicArgs in characteristicsArgs) { + // 发现 GATT 描述值 + final hashCodeArgs = characteristicArgs.hashCodeArgs; + logger.info('discoverDescriptors: $uuidArgs.$hashCodeArgs'); + final descriptorsArgs = await _api + .discoverDescriptors(uuidArgs, hashCodeArgs) + .then((args) => args.cast()); + characteristicArgs.descriptorsArgs = descriptorsArgs; + } + serviceArgs.characteristicsArgs = characteristicsArgs; + } + final services = + servicesArgs.map((args) => args.toService2(peripheral)).toList(); + final characteristics = + services.expand((service) => service.characteristics).toList(); + _characteristics[uuidArgs] = { + for (var characteristic in characteristics) + characteristic.hashCode: characteristic + }; + return services; + } + + @override + Future readCharacteristic( + GattCharacteristic characteristic, + ) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final peripheral = characteristic.peripheral; + final uuidArgs = peripheral.uuid.toArgs(); + final hashCodeArgs = characteristic.hashCode; + logger.info('readCharacteristic: $uuidArgs.$hashCodeArgs'); + final value = await _api.readCharacteristic(uuidArgs, hashCodeArgs); + return value; + } + + @override + Future writeCharacteristic( + GattCharacteristic characteristic, { + required Uint8List value, + required GattCharacteristicWriteType type, + }) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final peripheral = characteristic.peripheral; + final uuidArgs = peripheral.uuid.toArgs(); + final hashCodeArgs = characteristic.hashCode; + final trimmedValueArgs = value.trimGATT(); + final typeArgs = type.toArgs(); + final typeNumberArgs = typeArgs.index; + final fragmentSize = await _api.getMaximumWriteValueLength( + uuidArgs, + typeNumberArgs, + ); + var start = 0; + while (start < trimmedValueArgs.length) { + final end = start + fragmentSize; + final fragmentedValueArgs = end < trimmedValueArgs.length + ? trimmedValueArgs.sublist(start, end) + : trimmedValueArgs.sublist(start); + logger.info( + 'writeCharacteristic: $uuidArgs.$hashCodeArgs - $fragmentedValueArgs, $typeArgs'); + await _api.writeCharacteristic( + uuidArgs, + hashCodeArgs, + fragmentedValueArgs, + typeNumberArgs, + ); + start = end; + } + } + + @override + Future setCharacteristicNotifyState( + GattCharacteristic characteristic, { + required bool state, + }) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final peripheral = characteristic.peripheral; + final uuidArgs = peripheral.uuid.toArgs(); + final hashCodeArgs = characteristic.hashCode; + final stateArgs = state; + logger.info( + 'setCharacteristicNotifyState: $uuidArgs.$hashCodeArgs - $stateArgs'); + await _api.setCharacteristicNotifyState( + uuidArgs, + hashCodeArgs, + stateArgs, + ); + } + + @override + Future readDescriptor(GattDescriptor descriptor) async { + if (descriptor is! MyGattDescriptor2) { + throw TypeError(); + } + final peripheral = descriptor.peripheral; + final uuidArgs = peripheral.uuid.toArgs(); + final hashCodeArgs = descriptor.hashCode; + logger.info('readDescriptor: $uuidArgs.$hashCodeArgs'); + final value = await _api.readDescriptor( + uuidArgs, + hashCodeArgs, + ); + return value; + } + + @override + Future writeDescriptor( + GattDescriptor descriptor, { + required Uint8List value, + }) async { + if (descriptor is! MyGattDescriptor2) { + throw TypeError(); + } + final peripheral = descriptor.peripheral; + final uuidArgs = peripheral.uuid.toArgs(); + final hashCodeArgs = descriptor.hashCode; + final trimmedValueArgs = value.trimGATT(); + logger.info('writeDescriptor: $uuidArgs.$hashCodeArgs - $trimmedValueArgs'); + await _api.writeDescriptor(uuidArgs, hashCodeArgs, trimmedValueArgs); + } + + @override + void onStateChanged(int stateNumberArgs) { + final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; + logger.info('onStateChanged: $stateArgs'); + final state = stateArgs.toState(); + if (_state == state) { + return; + } + _state = state; + final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); + _stateChangedController.add(eventArgs); + } + + @override + void onDiscovered( + MyPeripheralArgs peripheralArgs, + int rssiArgs, + MyAdvertisementArgs advertisementArgs, + ) { + final uuidArgs = peripheralArgs.uuidArgs; + logger.info('onDiscovered: $uuidArgs - $rssiArgs, $advertisementArgs'); + final peripheral = _peripherals.putIfAbsent( + peripheralArgs.uuidArgs, + () => peripheralArgs.toPeripheral(), + ); + final rssi = rssiArgs; + final advertisement = advertisementArgs.toAdvertisement(); + final eventArgs = DiscoveredEventArgs( + peripheral, + rssi, + advertisement, + ); + _discoveredController.add(eventArgs); + } + + @override + void onConnectionStateChanged( + String uuidArgs, + bool stateArgs, + ) { + logger.info('onConnectionStateChanged: $uuidArgs - $stateArgs'); + final peripheral = _peripherals[uuidArgs]; + if (peripheral == null) { + return; + } + final state = stateArgs; + final eventArgs = ConnectionStateChangedEventArgs(peripheral, state); + _connectionStateChangedController.add(eventArgs); + if (!state) { + _characteristics.remove(uuidArgs); + } + } + + @override + void onCharacteristicNotified( + String uuidArgs, + int hashCodeArgs, + Uint8List valueArgs, + ) { + logger + .info('onCharacteristicNotified: $uuidArgs.$hashCodeArgs - $valueArgs'); + final characteristic = _retrieveCharacteristic(uuidArgs, hashCodeArgs); + if (characteristic == null) { + return; + } + final value = valueArgs; + final eventArgs = GattCharacteristicNotifiedEventArgs( + characteristic, + value, + ); + _characteristicNotifiedController.add(eventArgs); + } + + MyGattCharacteristic2? _retrieveCharacteristic( + String uuidArgs, + int hashCodeArgs, + ) { + final characteristics = _characteristics[uuidArgs]; + if (characteristics == null) { + return null; + } + return characteristics[hashCodeArgs]; + } +} diff --git a/bluetooth_low_energy_darwin/lib/src/my_central_manager_2.dart b/bluetooth_low_energy_darwin/lib/src/my_central_manager_2.dart deleted file mode 100644 index ea87df2..0000000 --- a/bluetooth_low_energy_darwin/lib/src/my_central_manager_2.dart +++ /dev/null @@ -1,297 +0,0 @@ -import 'dart:async'; - -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:flutter/foundation.dart'; - -import 'my_api.dart'; -import 'my_gatt_characteristic2.dart'; -import 'my_gatt_descriptor2.dart'; - -class MyCentralManager2 extends MyCentralManager - implements MyCentralManagerFlutterApi { - final MyCentralManagerHostApi _api; - BluetoothLowEnergyState _state; - final StreamController - _stateChangedController; - final StreamController _discoveredController; - final StreamController - _peripheralStateChangedController; - final StreamController - _characteristicValueChangedController; - - MyCentralManager2() - : _api = MyCentralManagerHostApi(), - _state = BluetoothLowEnergyState.unknown, - _stateChangedController = StreamController.broadcast(), - _discoveredController = StreamController.broadcast(), - _peripheralStateChangedController = StreamController.broadcast(), - _characteristicValueChangedController = StreamController.broadcast(); - - @override - BluetoothLowEnergyState get state => _state; - @protected - set state(BluetoothLowEnergyState value) { - if (_state == value) { - return; - } - _state = value; - final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); - _stateChangedController.add(eventArgs); - } - - @override - Stream get stateChanged => - _stateChangedController.stream; - @override - Stream get discovered => _discoveredController.stream; - @override - Stream get peripheralStateChanged => - _peripheralStateChangedController.stream; - @override - Stream - get characteristicValueChanged => - _characteristicValueChangedController.stream; - - Future _throwWithoutState(BluetoothLowEnergyState state) async { - if (this.state != state) { - throw StateError( - '$state is expected, but current state is ${this.state}.'); - } - } - - @override - Future setUp() async { - final args = await _api.setUp(); - final stateArgs = - MyBluetoothLowEnergyStateArgs.values[args.stateNumberArgs]; - state = stateArgs.toState(); - MyCentralManagerFlutterApi.setup(this); - } - - @override - Future startDiscovery() async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - await _api.startDiscovery(); - } - - @override - Future stopDiscovery() async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - await _api.stopDiscovery(); - } - - @override - Future connect(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final peripheralHashCodeArgs = peripheral.hashCode; - await _api.connect(peripheralHashCodeArgs); - } - - @override - Future disconnect(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final peripheralHashCodeArgs = peripheral.hashCode; - await _api.disconnect(peripheralHashCodeArgs); - } - - @override - Future getMaximumWriteLength( - Peripheral peripheral, { - required GattCharacteristicWriteType type, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final peripheralHashCodeArgs = peripheral.hashCode; - final typeArgs = type.toArgs(); - final typeNumberArgs = typeArgs.index; - final maximumWriteLength = await _api.getMaximumWriteLength( - peripheralHashCodeArgs, - typeNumberArgs, - ); - return maximumWriteLength; - } - - @override - Future readRSSI(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final peripheralHashCodeArgs = peripheral.hashCode; - final rssi = await _api.readRSSI(peripheralHashCodeArgs); - return rssi; - } - - @override - Future> discoverGATT(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (peripheral is! MyPeripheral) { - throw TypeError(); - } - final peripheralHashCodeArgs = peripheral.hashCode; - final servicesArgs = await _api.discoverGATT(peripheralHashCodeArgs); - final services = servicesArgs - .cast() - .map((args) => args.toService2()) - .toList(); - for (var service in services) { - for (var charactersitic in service.characteristics) { - for (var descriptor in charactersitic.descriptors) { - descriptor.characteristic = charactersitic; - } - charactersitic.service = service; - } - service.peripheral = peripheral; - } - return services; - } - - @override - Future readCharacteristic( - GattCharacteristic characteristic, - ) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (characteristic is! MyGattCharacteristic2) { - throw TypeError(); - } - final service = characteristic.service; - final peripheral = service.peripheral; - final peripheralHashCodeArgs = peripheral.hashCode; - final characteristicHashCodeArgs = characteristic.hashCode; - final value = await _api.readCharacteristic( - peripheralHashCodeArgs, - characteristicHashCodeArgs, - ); - return value; - } - - @override - Future writeCharacteristic( - GattCharacteristic characteristic, { - required Uint8List value, - required GattCharacteristicWriteType type, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (characteristic is! MyGattCharacteristic2) { - throw TypeError(); - } - final service = characteristic.service; - final peripheral = service.peripheral; - final peripheralHashCodeArgs = peripheral.hashCode; - final characteristicHashCodeArgs = characteristic.hashCode; - final valueArgs = value; - final typeArgs = type.toArgs(); - final typeNumberArgs = typeArgs.index; - await _api.writeCharacteristic( - peripheralHashCodeArgs, - characteristicHashCodeArgs, - valueArgs, - typeNumberArgs, - ); - } - - @override - Future notifyCharacteristic( - GattCharacteristic characteristic, { - required bool state, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (characteristic is! MyGattCharacteristic2) { - throw TypeError(); - } - final service = characteristic.service; - final peripheral = service.peripheral; - final peripheralHashCodeArgs = peripheral.hashCode; - final characteristicHashCodeArgs = characteristic.hashCode; - final stateArgs = state; - await _api.notifyCharacteristic( - peripheralHashCodeArgs, - characteristicHashCodeArgs, - stateArgs, - ); - } - - @override - Future readDescriptor(GattDescriptor descriptor) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (descriptor is! MyGattDescriptor2) { - throw TypeError(); - } - final characteristic = descriptor.characteristic; - final service = characteristic.service; - final peripheral = service.peripheral; - final peripheralHashCodeArgs = peripheral.hashCode; - final descriptorHashCodeArgs = descriptor.hashCode; - final value = await _api.readDescriptor( - peripheralHashCodeArgs, - descriptorHashCodeArgs, - ); - return value; - } - - @override - Future writeDescriptor( - GattDescriptor descriptor, { - required Uint8List value, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (descriptor is! MyGattDescriptor2) { - throw TypeError(); - } - final characteristic = descriptor.characteristic; - final service = characteristic.service; - final peripheral = service.peripheral; - final peripheralHashCodeArgs = peripheral.hashCode; - final descriptorHashCodeArgs = descriptor.hashCode; - final valueArgs = value; - await _api.writeDescriptor( - peripheralHashCodeArgs, - descriptorHashCodeArgs, - valueArgs, - ); - } - - @override - void onStateChanged(int stateNumberArgs) { - final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; - state = stateArgs.toState(); - } - - @override - void onDiscovered( - MyPeripheralArgs peripheralArgs, - int rssiArgs, - MyAdvertisementArgs advertisementArgs, - ) { - final peripheral = peripheralArgs.toPeripheral(); - final rssi = rssiArgs; - final advertisement = advertisementArgs.toAdvertisement(); - final eventArgs = DiscoveredEventArgs( - peripheral, - rssi, - advertisement, - ); - _discoveredController.add(eventArgs); - } - - @override - void onPeripheralStateChanged( - MyPeripheralArgs peripheralArgs, - bool stateArgs, - ) { - final peripheral = peripheralArgs.toPeripheral(); - final state = stateArgs; - final eventArgs = PeripheralStateChangedEventArgs(peripheral, state); - _peripheralStateChangedController.add(eventArgs); - } - - @override - void onCharacteristicValueChanged( - MyGattCharacteristicArgs characteristicArgs, - Uint8List valueArgs, - ) { - final characteristic = characteristicArgs.toCharacteristic2(); - final value = valueArgs; - final eventArgs = GattCharacteristicValueChangedEventArgs( - characteristic, - value, - ); - _characteristicValueChangedController.add(eventArgs); - } -} diff --git a/bluetooth_low_energy_darwin/lib/src/my_gatt_characteristic2.dart b/bluetooth_low_energy_darwin/lib/src/my_gatt_characteristic2.dart index b8c8a15..5c0cde6 100644 --- a/bluetooth_low_energy_darwin/lib/src/my_gatt_characteristic2.dart +++ b/bluetooth_low_energy_darwin/lib/src/my_gatt_characteristic2.dart @@ -1,13 +1,15 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'my_gatt_descriptor2.dart'; -import 'my_gatt_service2.dart'; class MyGattCharacteristic2 extends MyGattCharacteristic { - late final MyGattService2 service; + final MyPeripheral peripheral; + @override + final int hashCode; MyGattCharacteristic2({ - super.hashCode, + required this.peripheral, + required this.hashCode, required super.uuid, required super.properties, required List descriptors, @@ -16,4 +18,11 @@ class MyGattCharacteristic2 extends MyGattCharacteristic { @override List get descriptors => super.descriptors.cast(); + + @override + bool operator ==(Object other) { + return other is MyGattCharacteristic2 && + other.peripheral == peripheral && + other.hashCode == hashCode; + } } diff --git a/bluetooth_low_energy_darwin/lib/src/my_gatt_descriptor2.dart b/bluetooth_low_energy_darwin/lib/src/my_gatt_descriptor2.dart index 5bc7dc4..658a94f 100644 --- a/bluetooth_low_energy_darwin/lib/src/my_gatt_descriptor2.dart +++ b/bluetooth_low_energy_darwin/lib/src/my_gatt_descriptor2.dart @@ -1,12 +1,20 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'my_gatt_characteristic2.dart'; - class MyGattDescriptor2 extends MyGattDescriptor { - late final MyGattCharacteristic2 characteristic; + final MyPeripheral peripheral; + @override + final int hashCode; MyGattDescriptor2({ - super.hashCode, + required this.peripheral, + required this.hashCode, required super.uuid, }); + + @override + bool operator ==(Object other) { + return other is MyGattDescriptor2 && + other.peripheral == peripheral && + other.hashCode == hashCode; + } } diff --git a/bluetooth_low_energy_darwin/lib/src/my_gatt_service2.dart b/bluetooth_low_energy_darwin/lib/src/my_gatt_service2.dart index ac74a6d..e57024c 100644 --- a/bluetooth_low_energy_darwin/lib/src/my_gatt_service2.dart +++ b/bluetooth_low_energy_darwin/lib/src/my_gatt_service2.dart @@ -3,10 +3,13 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla import 'my_gatt_characteristic2.dart'; class MyGattService2 extends MyGattService { - late final MyPeripheral peripheral; + final MyPeripheral peripheral; + @override + final int hashCode; MyGattService2({ - super.hashCode, + required this.peripheral, + required this.hashCode, required super.uuid, required List characteristics, }) : super(characteristics: characteristics); @@ -14,4 +17,11 @@ class MyGattService2 extends MyGattService { @override List get characteristics => super.characteristics.cast(); + + @override + bool operator ==(Object other) { + return other is MyGattService2 && + other.peripheral == peripheral && + other.hashCode == hashCode; + } } diff --git a/bluetooth_low_energy_darwin/lib/src/my_peripheral_manager.dart b/bluetooth_low_energy_darwin/lib/src/my_peripheral_manager.dart new file mode 100644 index 0000000..491a28a --- /dev/null +++ b/bluetooth_low_energy_darwin/lib/src/my_peripheral_manager.dart @@ -0,0 +1,315 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:flutter/foundation.dart'; + +import 'my_api.dart'; + +class MyPeripheralManager extends PeripheralManager + implements MyPeripheralManagerFlutterApi { + final MyPeripheralManagerHostApi _api; + final StreamController + _stateChangedController; + final StreamController + _characteristicReadController; + final StreamController + _characteristicWrittenController; + final StreamController + _characteristicNotifyStateChangedController; + + final Map> _characteristics; + final Map> _listeners; + + BluetoothLowEnergyState _state; + + MyPeripheralManager() + : _api = MyPeripheralManagerHostApi(), + _stateChangedController = StreamController.broadcast(), + _characteristicReadController = StreamController.broadcast(), + _characteristicWrittenController = StreamController.broadcast(), + _characteristicNotifyStateChangedController = + StreamController.broadcast(), + _characteristics = {}, + _listeners = {}, + _state = BluetoothLowEnergyState.unknown; + + @override + Stream get stateChanged => + _stateChangedController.stream; + @override + Stream get characteristicRead => + _characteristicReadController.stream; + @override + Stream get characteristicWritten => + _characteristicWrittenController.stream; + @override + Stream + get characteristicNotifyStateChanged => + _characteristicNotifyStateChangedController.stream; + + @override + Future setUp() async { + logger.info('setUp'); + await _api.setUp(); + MyPeripheralManagerFlutterApi.setup(this); + } + + @override + Future getState() { + logger.info('getState'); + return Future.value(_state); + } + + @override + Future addService(GattService service) async { + if (service is! MyGattService) { + throw TypeError(); + } + final serviceArgs = service.toArgs(); + final hashCodeArgs = serviceArgs.hashCodeArgs; + logger.info('addService: $hashCodeArgs'); + await _api.addService(serviceArgs); + _characteristics[hashCodeArgs] = { + for (var characteristics in service.characteristics) + characteristics.hashCode: characteristics + }; + } + + @override + Future removeService(GattService service) async { + final hashCodeArgs = service.hashCode; + logger.info('removeService: $hashCodeArgs'); + await _api.removeService(hashCodeArgs); + _characteristics.remove(hashCodeArgs); + } + + @override + Future clearServices() async { + logger.info('clearServices'); + await _api.clearServices(); + _characteristics.clear(); + } + + @override + Future startAdvertising(Advertisement advertisement) async { + final advertisementArgs = advertisement.toArgs(); + logger.info('startAdvertising: $advertisementArgs'); + await _api.startAdvertising(advertisementArgs); + } + + @override + Future stopAdvertising() async { + logger.info('stopAdvertising'); + await _api.stopAdvertising(); + } + + @override + Future readCharacteristic(GattCharacteristic characteristic) { + if (characteristic is! MyGattCharacteristic) { + throw TypeError(); + } + final hashCodeArgs = characteristic.hashCode; + logger.info('readCharacteristic: $hashCodeArgs'); + final value = characteristic.value; + return Future.value(value); + } + + @override + Future writeCharacteristic( + GattCharacteristic characteristic, { + required Uint8List value, + Central? central, + }) async { + if (characteristic is! MyGattCharacteristic) { + throw TypeError(); + } + characteristic.value = value; + if (central == null) { + return; + } + if (central is! MyCentral) { + throw TypeError(); + } + final uuidArgs = central.uuid.toArgs(); + final hashCodeArgs = characteristic.hashCode; + final listener = _retrieveListener(uuidArgs, hashCodeArgs); + if (listener == null) { + logger.warning('The central is not listening.'); + return; + } + final uuidsArgs = [uuidArgs]; + final trimmedValueArgs = characteristic.value; + final fragmentSize = await _api.getMaximumUpdateValueLength(uuidArgs); + var start = 0; + while (start < trimmedValueArgs.length) { + final end = start + fragmentSize; + final fragmentedValueArgs = end < trimmedValueArgs.length + ? trimmedValueArgs.sublist(start, end) + : trimmedValueArgs.sublist(start); + logger.info( + 'notifyCharacteristicChanged: $hashCodeArgs - $fragmentedValueArgs, $uuidsArgs'); + await _api.updateCharacteristic( + hashCodeArgs, + fragmentedValueArgs, + uuidsArgs, + ); + start = end; + } + } + + @override + void onStateChanged(int stateNumberArgs) { + final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; + logger.info('onStateChanged: $stateArgs'); + final state = stateArgs.toState(); + if (_state == state) { + return; + } + _state = state; + final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); + _stateChangedController.add(eventArgs); + } + + @override + void onCharacteristicReadRequest( + MyCentralArgs centralArgs, + int hashCodeArgs, + int idArgs, + int offsetArgs, + ) async { + final uuidArgs = centralArgs.uuidArgs; + logger.info( + 'onCharacteristicReadRequest: $uuidArgs.$hashCodeArgs - $idArgs, $offsetArgs'); + final central = centralArgs.toCentral(); + final characteristic = _retrieveCharacteristic(hashCodeArgs); + if (characteristic == null) { + return; + } + const errorArgs = MyGattErrorArgs.success; + final offset = offsetArgs; + final valueArgs = _onCharacteristicRead(central, characteristic, offset); + await _tryRespond( + idArgs, + errorArgs, + valueArgs, + ); + } + + @override + void onCharacteristicWriteRequest( + MyCentralArgs centralArgs, + int hashCodeArgs, + int idArgs, + int offsetArgs, + Uint8List valueArgs, + ) async { + final uuidArgs = centralArgs.uuidArgs; + logger.info( + 'onCharacteristicWriteRequest: $uuidArgs.$hashCodeArgs - $idArgs, $offsetArgs, $valueArgs'); + final central = centralArgs.toCentral(); + final characteristic = _retrieveCharacteristic(hashCodeArgs); + if (characteristic == null) { + return; + } + const errorArgs = MyGattErrorArgs.success; + final value = valueArgs; + _onCharacteristicWritten(central, characteristic, value); + await _tryRespond( + idArgs, + errorArgs, + null, + ); + } + + @override + void onCharacteristicNotifyStateChanged( + MyCentralArgs centralArgs, + int hashCodeArgs, + bool stateArgs, + ) { + final uuidArgs = centralArgs.uuidArgs; + logger.info( + 'onCharacteristicNotifyStateChanged: $uuidArgs.$hashCodeArgs - $stateArgs'); + final central = centralArgs.toCentral(); + final characteristic = _retrieveCharacteristic(hashCodeArgs); + if (characteristic == null) { + return; + } + final state = stateArgs; + final listeners = _listeners.putIfAbsent(uuidArgs, () => {}); + if (state) { + listeners[hashCodeArgs] = true; + } else { + listeners.remove(hashCodeArgs); + } + final eventArgs = GattCharacteristicNotifyStateChangedEventArgs( + central, + characteristic, + state, + ); + _characteristicNotifyStateChangedController.add(eventArgs); + } + + MyGattCharacteristic? _retrieveCharacteristic(int hashCodeArgs) { + final characteristics = _characteristics.values + .reduce((value, element) => value..addAll(element)); + return characteristics[hashCodeArgs]; + } + + bool? _retrieveListener(String uuidArgs, int hashCodeArgs) { + final listeners = _listeners[uuidArgs]; + if (listeners == null) { + return null; + } + return listeners[hashCodeArgs]; + } + + Future _tryRespond( + int idArgs, + MyGattErrorArgs errorArgs, + Uint8List? valueArgs, + ) async { + final errorNumberArgs = errorArgs.index; + try { + _api.respond( + idArgs, + errorNumberArgs, + valueArgs, + ); + } catch (e, stack) { + logger.shout('Respond failed.', e, stack); + } + } + + Uint8List _onCharacteristicRead( + MyCentral central, + MyGattCharacteristic characteristic, + int offset, + ) { + final value = characteristic.value; + final trimmedValue = value.sublist(offset); + if (offset == 0) { + final eventArgs = GattCharacteristicReadEventArgs( + central, + characteristic, + value, + ); + _characteristicReadController.add(eventArgs); + } + return trimmedValue; + } + + void _onCharacteristicWritten( + MyCentral central, + MyGattCharacteristic characteristic, + Uint8List value, + ) async { + final trimmedValue = value.trimGATT(); + final eventArgs = GattCharacteristicWrittenEventArgs( + central, + characteristic, + trimmedValue, + ); + _characteristicWrittenController.add(eventArgs); + } +} diff --git a/bluetooth_low_energy_darwin/lib/src/my_peripheral_manager_2.dart b/bluetooth_low_energy_darwin/lib/src/my_peripheral_manager_2.dart deleted file mode 100644 index c0b5473..0000000 --- a/bluetooth_low_energy_darwin/lib/src/my_peripheral_manager_2.dart +++ /dev/null @@ -1,252 +0,0 @@ -import 'dart:async'; - -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:flutter/foundation.dart'; - -import 'my_api.dart'; - -class MyPeripheralManager2 extends MyPeripheralManager - implements MyPeripheralManagerFlutterApi { - final MyPeripheralManagerHostApi _api; - BluetoothLowEnergyState _state; - final StreamController - _stateChangedController; - final StreamController - _readCharacteristicCommandReceivedController; - final StreamController - _writeCharacteristicCommandReceivedController; - final StreamController - _notifyCharacteristicCommandReceivedController; - - MyPeripheralManager2() - : _api = MyPeripheralManagerHostApi(), - _state = BluetoothLowEnergyState.unknown, - _stateChangedController = StreamController.broadcast(), - _readCharacteristicCommandReceivedController = - StreamController.broadcast(), - _writeCharacteristicCommandReceivedController = - StreamController.broadcast(), - _notifyCharacteristicCommandReceivedController = - StreamController.broadcast(); - - @override - BluetoothLowEnergyState get state => _state; - @protected - set state(BluetoothLowEnergyState value) { - if (_state == value) { - return; - } - _state = value; - final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); - _stateChangedController.add(eventArgs); - } - - @override - Stream get stateChanged => - _stateChangedController.stream; - @override - Stream - get readCharacteristicCommandReceived => - _readCharacteristicCommandReceivedController.stream; - @override - Stream - get writeCharacteristicCommandReceived => - _writeCharacteristicCommandReceivedController.stream; - @override - Stream - get notifyCharacteristicCommandReceived => - _notifyCharacteristicCommandReceivedController.stream; - - Future _throwWithoutState(BluetoothLowEnergyState state) async { - if (this.state != state) { - throw StateError( - '$state is expected, but current state is ${this.state}.'); - } - } - - @override - Future setUp() async { - final args = await _api.setUp(); - final stateArgs = - MyBluetoothLowEnergyStateArgs.values[args.stateNumberArgs]; - state = stateArgs.toState(); - MyPeripheralManagerFlutterApi.setup(this); - } - - @override - Future addService(GattService service) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - if (service is! MyGattService) { - throw TypeError(); - } - final serviceArgs = service.toArgs(); - await _api.addService(serviceArgs); - } - - @override - Future removeService(GattService service) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final serviceHashCodeArgs = service.hashCode; - await _api.removeService(serviceHashCodeArgs); - } - - @override - Future clearServices() async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - await _api.clearServices(); - } - - @override - Future startAdvertising(Advertisement advertisement) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final advertisementArgs = advertisement.toArgs(); - await _api.startAdvertising(advertisementArgs); - } - - @override - Future stopAdvertising() async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - await _api.stopAdvertising(); - } - - @override - Future getMaximumWriteLength(Central central) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final centralHashCodeArgs = central.hashCode; - final maximumWriteLength = - await _api.getMaximumWriteLength(centralHashCodeArgs); - return maximumWriteLength; - } - - @override - Future sendReadCharacteristicReply( - Central central, { - required GattCharacteristic characteristic, - required int id, - required int offset, - required bool status, - required Uint8List value, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final centralHashCodeArgs = central.hashCode; - final characteristicHashCodeArgs = characteristic.hashCode; - final idArgs = id; - final offsetArgs = offset; - final statusArgs = status; - final valueArgs = value; - await _api.sendReadCharacteristicReply( - centralHashCodeArgs, - characteristicHashCodeArgs, - idArgs, - offsetArgs, - statusArgs, - valueArgs, - ); - } - - @override - Future sendWriteCharacteristicReply( - Central central, { - required GattCharacteristic characteristic, - required int id, - required int offset, - required bool status, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final centralHashCodeArgs = central.hashCode; - final characteristicHashCodeArgs = characteristic.hashCode; - final idArgs = id; - final offsetArgs = offset; - final statusArgs = status; - await _api.sendWriteCharacteristicReply( - centralHashCodeArgs, - characteristicHashCodeArgs, - idArgs, - offsetArgs, - statusArgs, - ); - } - - @override - Future notifyCharacteristicValueChanged( - Central central, { - required GattCharacteristic characteristic, - required Uint8List value, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final centralHashCodeArgs = central.hashCode; - final characteristicHashCodeArgs = characteristic.hashCode; - final valueArgs = value; - await _api.notifyCharacteristicValueChanged( - centralHashCodeArgs, - characteristicHashCodeArgs, - valueArgs, - ); - } - - @override - void onStateChanged(int stateNumberArgs) { - final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; - state = stateArgs.toState(); - } - - @override - void onReadCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - int idArgs, - int offsetArgs, - ) { - final central = centralArgs.toCentral(); - final characteristic = characteristicArgs.toCharacteristic2(); - final id = idArgs; - final offset = offsetArgs; - final eventArgs = ReadGattCharacteristicCommandEventArgs( - central, - characteristic, - id, - offset, - ); - _readCharacteristicCommandReceivedController.add(eventArgs); - } - - @override - void onWriteCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - int idArgs, - int offsetArgs, - Uint8List valueArgs, - ) { - final central = centralArgs.toCentral(); - final characteristic = characteristicArgs.toCharacteristic2(); - final id = idArgs; - final offset = offsetArgs; - final value = valueArgs; - final eventArgs = WriteGattCharacteristicCommandEventArgs( - central, - characteristic, - id, - offset, - value, - ); - _writeCharacteristicCommandReceivedController.add(eventArgs); - } - - @override - void onNotifyCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - bool stateArgs, - ) { - final central = centralArgs.toCentral(); - final characteristic = characteristicArgs.toCharacteristic2(); - final state = stateArgs; - final eventArgs = NotifyGattCharacteristicCommandEventArgs( - central, - characteristic, - state, - ); - _notifyCharacteristicCommandReceivedController.add(eventArgs); - } -} diff --git a/bluetooth_low_energy_darwin/my_api.dart b/bluetooth_low_energy_darwin/my_api.dart index 16dc110..35d562a 100644 --- a/bluetooth_low_energy_darwin/my_api.dart +++ b/bluetooth_low_energy_darwin/my_api.dart @@ -8,152 +8,54 @@ import 'package:pigeon/pigeon.dart'; swiftOptions: SwiftOptions(), ), ) -@HostApi() -abstract class MyCentralManagerHostApi { - @async - MyCentralManagerArgs setUp(); - void startDiscovery(); - void stopDiscovery(); - @async - void connect(int peripheralHashCodeArgs); - @async - void disconnect(int peripheralHashCodeArgs); - int getMaximumWriteLength(int peripheralHashCodeArgs, int typeNumberArgs); - @async - int readRSSI(int peripheralHashCodeArgs); - @async - List discoverGATT(int peripheralHashCodeArgs); - @async - Uint8List readCharacteristic( - int peripheralHashCodeArgs, - int characteristicHashCodeArgs, - ); - @async - void writeCharacteristic( - int peripheralHashCodeArgs, - int characteristicHashCodeArgs, - Uint8List valueArgs, - int typeNumberArgs, - ); - @async - void notifyCharacteristic( - int peripheralHashCodeArgs, - int characteristicHashCodeArgs, - bool stateArgs, - ); - @async - Uint8List readDescriptor( - int peripheralHashCodeArgs, - int descriptorHashCodeArgs, - ); - @async - void writeDescriptor( - int peripheralHashCodeArgs, - int descriptorHashCodeArgs, - Uint8List valueArgs, - ); +enum MyBluetoothLowEnergyStateArgs { + unknown, + resetting, + unsupported, + unauthorized, + poweredOff, + poweredOn, } -@FlutterApi() -abstract class MyCentralManagerFlutterApi { - void onStateChanged(int stateNumberArgs); - void onDiscovered( - MyPeripheralArgs peripheralArgs, - int rssiArgs, - MyAdvertisementArgs advertisementArgs, - ); - void onPeripheralStateChanged( - MyPeripheralArgs peripheralArgs, - bool stateArgs, - ); - void onCharacteristicValueChanged( - MyGattCharacteristicArgs characteristicArgs, - Uint8List valueArgs, - ); +enum MyGattCharacteristicPropertyArgs { + read, + write, + writeWithoutResponse, + notify, + indicate, } -@HostApi() -abstract class MyPeripheralManagerHostApi { - @async - MyPeripheralManagerArgs setUp(); - @async - void addService(MyGattServiceArgs serviceArgs); - void removeService(int serviceHashCodeArgs); - void clearServices(); - @async - void startAdvertising(MyAdvertisementArgs advertisementArgs); - void stopAdvertising(); - int getMaximumWriteLength(int centralHashCodeArgs); - void sendReadCharacteristicReply( - int centralHashCodeArgs, - int characteristicHashCodeArgs, - int idArgs, - int offsetArgs, - bool statusArgs, - Uint8List valueArgs, - ); - void sendWriteCharacteristicReply( - int centralHashCodeArgs, - int characteristicHashCodeArgs, - int idArgs, - int offsetArgs, - bool statusArgs, - ); - @async - void notifyCharacteristicValueChanged( - int centralHashCodeArgs, - int characteristicHashCodeArgs, - Uint8List valueArgs, - ); +enum MyGattCharacteristicWriteTypeArgs { + withResponse, + withoutResponse, } -@FlutterApi() -abstract class MyPeripheralManagerFlutterApi { - void onStateChanged(int stateNumberArgs); - void onReadCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - int idArgs, - int offsetArgs, - ); - void onWriteCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - int idArgs, - int offsetArgs, - Uint8List valueArgs, - ); - void onNotifyCharacteristicCommandReceived( - MyCentralArgs centralArgs, - MyGattCharacteristicArgs characteristicArgs, - bool stateArgs, - ); +enum MyGattErrorArgs { + success, + invalidHandle, + readNotPermitted, + writeNotPermitted, + invalidPDU, + insufficientAuthentication, + requestNotSupported, + invalidOffset, + insufficientAuthorization, + prepareQueueFull, + attributeNotFound, + attributeNotLong, + insufficientEncryptionKeySize, + invalidAttributeValueLength, + unlikelyError, + insufficientEncryption, + unsupportedGroupType, + insufficientResources, } -class MyCentralManagerArgs { - final int stateNumberArgs; +class MyManufacturerSpecificDataArgs { + final int idArgs; + final Uint8List dataArgs; - MyCentralManagerArgs(this.stateNumberArgs); -} - -class MyPeripheralManagerArgs { - final int stateNumberArgs; - - MyPeripheralManagerArgs(this.stateNumberArgs); -} - -class MyCentralArgs { - final int hashCodeArgs; - final String uuidArgs; - - MyCentralArgs(this.hashCodeArgs, this.uuidArgs); -} - -class MyPeripheralArgs { - final int hashCodeArgs; - final String uuidArgs; - - MyPeripheralArgs(this.hashCodeArgs, this.uuidArgs); + MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs); } class MyAdvertisementArgs { @@ -170,22 +72,27 @@ class MyAdvertisementArgs { ); } -class MyManufacturerSpecificDataArgs { - final int idArgs; - final Uint8List dataArgs; +class MyCentralArgs { + final String uuidArgs; - MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs); + MyCentralArgs(this.uuidArgs); } -class MyGattServiceArgs { +class MyPeripheralArgs { + final String uuidArgs; + + MyPeripheralArgs(this.uuidArgs); +} + +class MyGattDescriptorArgs { final int hashCodeArgs; final String uuidArgs; - final List characteristicsArgs; + final Uint8List? valueArgs; - MyGattServiceArgs( + MyGattDescriptorArgs( this.hashCodeArgs, this.uuidArgs, - this.characteristicsArgs, + this.valueArgs, ); } @@ -203,39 +110,118 @@ class MyGattCharacteristicArgs { ); } -class MyGattDescriptorArgs { +class MyGattServiceArgs { final int hashCodeArgs; final String uuidArgs; - final Uint8List? valueArgs; + final List characteristicsArgs; - MyGattDescriptorArgs( + MyGattServiceArgs( this.hashCodeArgs, this.uuidArgs, - this.valueArgs, + this.characteristicsArgs, ); } -enum MyBluetoothLowEnergyStateArgs { - unknown, - unsupported, - unauthorized, - poweredOff, - poweredOn, +@HostApi() +abstract class MyCentralManagerHostApi { + void setUp(); + void startDiscovery(); + void stopDiscovery(); + @async + void connect(String uuidArgs); + @async + void disconnect(String uuidArgs); + int getMaximumWriteValueLength(String uuidArgs, int typeNumberArgs); + @async + int readRSSI(String uuidArgs); + @async + List discoverServices(String uuidArgs); + @async + List discoverCharacteristics( + String uuidArgs, + int hashCodeArgs, + ); + @async + List discoverDescriptors( + String uuidArgs, + int hashCodeArgs, + ); + @async + Uint8List readCharacteristic(String uuidArgs, int hashCodeArgs); + @async + void writeCharacteristic( + String uuidArgs, + int hashCodeArgs, + Uint8List valueArgs, + int typeNumberArgs, + ); + @async + void setCharacteristicNotifyState( + String uuidArgs, + int hashCodeArgs, + bool stateArgs, + ); + @async + Uint8List readDescriptor(String uuidArgs, int hashCodeArgs); + @async + void writeDescriptor(String uuidArgs, int hashCodeArgs, Uint8List valueArgs); } -enum MyGattCharacteristicPropertyArgs { - read, - write, - writeWithoutResponse, - notify, - indicate, +@FlutterApi() +abstract class MyCentralManagerFlutterApi { + void onStateChanged(int stateNumberArgs); + void onDiscovered( + MyPeripheralArgs peripheralArgs, + int rssiArgs, + MyAdvertisementArgs advertisementArgs, + ); + void onConnectionStateChanged(String uuidArgs, bool stateArgs); + void onCharacteristicNotified( + String uuidArgs, + int hashCodeArgs, + Uint8List valueArgs, + ); } -enum MyGattCharacteristicWriteTypeArgs { - // Write with response - withResponse, - // Write without response - withoutResponse, - // Write with response and waiting for confirmation - // reliable, +@HostApi() +abstract class MyPeripheralManagerHostApi { + void setUp(); + @async + void addService(MyGattServiceArgs serviceArgs); + void removeService(int hashCodeArgs); + void clearServices(); + @async + void startAdvertising(MyAdvertisementArgs advertisementArgs); + void stopAdvertising(); + int getMaximumUpdateValueLength(String uuidArgs); + void respond(int idArgs, int errorNumberArgs, Uint8List? valueArgs); + @async + void updateCharacteristic( + int hashCodeArgs, + Uint8List valueArgs, + List? uuidsArgs, + ); +} + +@FlutterApi() +abstract class MyPeripheralManagerFlutterApi { + void onStateChanged(int stateNumberArgs); + void onCharacteristicReadRequest( + MyCentralArgs centralArgs, + int hashCodeArgs, + int idArgs, + int offsetArgs, + ); + void onCharacteristicWriteRequest( + MyCentralArgs centralArgs, + int hashCodeArgs, + int idArgs, + int offsetArgs, + Uint8List valueArgs, + ); + void onCharacteristicNotifyStateChanged( + MyCentralArgs centralArgs, + int hashCodeArgs, + bool stateArgs, + ); } diff --git a/bluetooth_low_energy_darwin/pubspec.yaml b/bluetooth_low_energy_darwin/pubspec.yaml index a4eddc8..48ccc98 100644 --- a/bluetooth_low_energy_darwin/pubspec.yaml +++ b/bluetooth_low_energy_darwin/pubspec.yaml @@ -1,6 +1,6 @@ name: bluetooth_low_energy_darwin description: iOS and macOS implementation of the bluetooth_low_energy plugin. -version: 4.0.0 +version: 5.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy environment: @@ -10,13 +10,13 @@ environment: dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^4.0.0 + bluetooth_low_energy_platform_interface: ^5.0.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^3.0.0 - pigeon: ^12.0.1 + pigeon: ^15.0.2 flutter: plugin: diff --git a/bluetooth_low_energy_linux/CHANGELOG.md b/bluetooth_low_energy_linux/CHANGELOG.md index c1e304e..bd776b2 100644 --- a/bluetooth_low_energy_linux/CHANGELOG.md +++ b/bluetooth_low_energy_linux/CHANGELOG.md @@ -1,3 +1,33 @@ +## 5.0.0 + +* Now `CentralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes. +* Add `UUID#fromAddress` constructor. +* Remove `CentralManager#getMaximumWriteLength` method. +* Move `CentralManager#state` to `CentralManager#getState()`. +* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`. +* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`. +* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`. +* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`. +* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`. + +## 5.0.0-dev.4 + +* Add event logs. + +## 5.0.0-dev.3 + +* Implements new Api. + +## 5.0.0-dev.2 + +* Update interface to 5.0.0-dev.4. + +## 5.0.0-dev.1 + +* Implement the `5.0.0` api. +* [Fix the issue that the same device was discovered multi times.](https://github.com/yanshouwang/bluetooth_low_energy/issues/32) +* [Fix the issue that some devices can't be discovered.](https://github.com/yanshouwang/bluetooth_low_energy/issues/32) + ## 4.0.0 * Remove `BluetoothLowEnergy` class. diff --git a/bluetooth_low_energy_linux/example/lib/main.dart b/bluetooth_low_energy_linux/example/lib/main.dart index 813aa77..f5c3eb0 100644 --- a/bluetooth_low_energy_linux/example/lib/main.dart +++ b/bluetooth_low_energy_linux/example/lib/main.dart @@ -8,9 +8,6 @@ import 'package:convert/convert.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; -CentralManager get centralManager => CentralManager.instance; -PeripheralManager get peripheralManager => PeripheralManager.instance; - void main() { runZonedGuarded(onStartUp, onCrashed); } @@ -18,8 +15,8 @@ void main() { void onStartUp() async { Logger.root.onRecord.listen(onLogRecord); WidgetsFlutterBinding.ensureInitialized(); - await centralManager.setUp(); - await peripheralManager.setUp(); + await CentralManager.instance.setUp(); + // await peripheralManager.setUp(); runApp(const MyApp()); } @@ -58,6 +55,8 @@ class _MyAppState extends State { return MaterialApp( theme: ThemeData.light( useMaterial3: true, + ).copyWith( + materialTapTargetSize: MaterialTapTargetSize.padded, ), home: const HomeView(), routes: { @@ -73,81 +72,18 @@ class _MyAppState extends State { } } -class HomeView extends StatefulWidget { +class HomeView extends StatelessWidget { const HomeView({super.key}); - @override - State createState() => _HomeViewState(); -} - -class _HomeViewState extends State { - late final PageController controller; - - @override - void initState() { - super.initState(); - controller = PageController(); - } - @override Widget build(BuildContext context) { return Scaffold( body: buildBody(context), - bottomNavigationBar: buildBottomNavigationBar(context), ); } Widget buildBody(BuildContext context) { - return PageView.builder( - controller: controller, - itemBuilder: (context, i) { - switch (i) { - case 0: - return const ScannerView(); - case 1: - return const AdvertiserView(); - default: - throw ArgumentError.value(i); - } - }, - itemCount: 2, - ); - } - - Widget buildBottomNavigationBar(BuildContext context) { - return ListenableBuilder( - listenable: controller, - builder: (context, child) { - return BottomNavigationBar( - onTap: (i) { - const duration = Duration(milliseconds: 300); - const curve = Curves.ease; - controller.animateToPage( - i, - duration: duration, - curve: curve, - ); - }, - currentIndex: controller.page?.toInt() ?? 0, - items: const [ - BottomNavigationBarItem( - icon: Icon(Icons.radar), - label: 'scanner', - ), - BottomNavigationBarItem( - icon: Icon(Icons.sensors), - label: 'advertiser', - ), - ], - ); - }, - ); - } - - @override - void dispose() { - super.dispose(); - controller.dispose(); + return const ScannerView(); } } @@ -168,16 +104,20 @@ class _ScannerViewState extends State { @override void initState() { super.initState(); - state = ValueNotifier(centralManager.state); + state = ValueNotifier(BluetoothLowEnergyState.unknown); discovering = ValueNotifier(false); discoveredEventArgs = ValueNotifier([]); - stateChangedSubscription = centralManager.stateChanged.listen( + stateChangedSubscription = CentralManager.instance.stateChanged.listen( (eventArgs) { state.value = eventArgs.state; }, ); - discoveredSubscription = centralManager.discovered.listen( + discoveredSubscription = CentralManager.instance.discovered.listen( (eventArgs) { + final name = eventArgs.advertisement.name; + if (name == null || name.isEmpty) { + return; + } final items = discoveredEventArgs.value; final i = items.indexWhere( (item) => item.peripheral == eventArgs.peripheral, @@ -190,6 +130,11 @@ class _ScannerViewState extends State { } }, ); + setUp(); + } + + void setUp() async { + state.value = await CentralManager.instance.getState(); } @override @@ -233,12 +178,13 @@ class _ScannerViewState extends State { } Future startDiscovery() async { - await centralManager.startDiscovery(); + discoveredEventArgs.value = []; + await CentralManager.instance.startDiscovery(); discovering.value = true; } Future stopDiscovery() async { - await centralManager.stopDiscovery(); + await CentralManager.instance.stopDiscovery(); discovering.value = false; } @@ -340,7 +286,13 @@ class _ScannerViewState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, ), - trailing: RssiWidget(rssi), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RssiWidget(rssi), + Text('$rssi'), + ], + ), ); }, separatorBuilder: (context, i) { @@ -378,44 +330,39 @@ class PeripheralView extends StatefulWidget { } class _PeripheralViewState extends State { - late final ValueNotifier state; + late final ValueNotifier connectionState; late final DiscoveredEventArgs eventArgs; late final ValueNotifier> services; late final ValueNotifier> characteristics; late final ValueNotifier service; late final ValueNotifier characteristic; late final ValueNotifier writeType; - late final ValueNotifier maximumWriteLength; - late final ValueNotifier rssi; late final ValueNotifier> logs; late final TextEditingController writeController; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription valueChangedSubscription; - late final StreamSubscription rssiChangedSubscription; - late final Timer rssiTimer; + late final StreamSubscription connectionStateChangedSubscription; + late final StreamSubscription characteristicNotifiedSubscription; @override void initState() { super.initState(); eventArgs = widget.eventArgs; - state = ValueNotifier(false); + connectionState = ValueNotifier(false); services = ValueNotifier([]); characteristics = ValueNotifier([]); service = ValueNotifier(null); characteristic = ValueNotifier(null); writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); - maximumWriteLength = ValueNotifier(0); - rssi = ValueNotifier(-100); logs = ValueNotifier([]); writeController = TextEditingController(); - stateChangedSubscription = centralManager.peripheralStateChanged.listen( + connectionStateChangedSubscription = + CentralManager.instance.connectionStateChanged.listen( (eventArgs) { if (eventArgs.peripheral != this.eventArgs.peripheral) { return; } - final state = eventArgs.state; - this.state.value = state; - if (!state) { + final connectionState = eventArgs.connectionState; + this.connectionState.value = connectionState; + if (!connectionState) { services.value = []; characteristics.value = []; service.value = null; @@ -424,12 +371,13 @@ class _PeripheralViewState extends State { } }, ); - valueChangedSubscription = centralManager.characteristicValueChanged.listen( + characteristicNotifiedSubscription = + CentralManager.instance.characteristicNotified.listen( (eventArgs) { - final characteristic = this.characteristic.value; - if (eventArgs.characteristic != characteristic) { - return; - } + // final characteristic = this.characteristic.value; + // if (eventArgs.characteristic != characteristic) { + // return; + // } const type = LogType.notify; final log = Log(type, eventArgs.value); logs.value = [ @@ -438,28 +386,16 @@ class _PeripheralViewState extends State { ]; }, ); - rssiTimer = Timer.periodic( - const Duration(seconds: 5), - (timer) async { - final state = this.state.value; - if (state) { - rssi.value = await centralManager.readRSSI(eventArgs.peripheral); - } else { - rssi.value = -100; - } - }, - ); } @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () async { - if (state.value) { + return PopScope( + onPopInvoked: (didPop) async { + if (connectionState.value) { final peripheral = eventArgs.peripheral; - await centralManager.disconnect(peripheral); + await CentralManager.instance.disconnect(peripheral); } - return true; }, child: Scaffold( appBar: buildAppBar(context), @@ -474,25 +410,17 @@ class _PeripheralViewState extends State { title: Text(title), actions: [ ValueListenableBuilder( - valueListenable: state, + valueListenable: connectionState, builder: (context, state, child) { return TextButton( onPressed: () async { final peripheral = eventArgs.peripheral; if (state) { - await centralManager.disconnect(peripheral); - maximumWriteLength.value = 0; - rssi.value = 0; + await CentralManager.instance.disconnect(peripheral); } else { - await centralManager.connect(peripheral); + await CentralManager.instance.connect(peripheral); services.value = - await centralManager.discoverGATT(peripheral); - maximumWriteLength.value = - await centralManager.getMaximumWriteLength( - peripheral, - type: writeType.value, - ); - rssi.value = await centralManager.readRSSI(peripheral); + await CentralManager.instance.discoverGATT(peripheral); } }, child: Text(state ? 'DISCONNECT' : 'CONNECT'), @@ -566,7 +494,32 @@ class _PeripheralViewState extends State { hint: const Text('CHOOSE A CHARACTERISTIC'), value: characteristic, onChanged: (characteristic) { + if (characteristic == null) { + return; + } this.characteristic.value = characteristic; + final writeType = this.writeType.value; + final canWrite = characteristic.properties.contains( + GattCharacteristicProperty.write, + ); + final canWriteWithoutResponse = + characteristic.properties.contains( + GattCharacteristicProperty.writeWithoutResponse, + ); + if (writeType == + GattCharacteristicWriteType.withResponse && + !canWrite && + canWriteWithoutResponse) { + this.writeType.value = + GattCharacteristicWriteType.withoutResponse; + } + if (writeType == + GattCharacteristicWriteType.withoutResponse && + !canWriteWithoutResponse && + canWrite) { + this.writeType.value = + GattCharacteristicWriteType.withResponse; + } }, ); }, @@ -624,129 +577,45 @@ class _PeripheralViewState extends State { }, ), ), - Row( - children: [ - ValueListenableBuilder( - valueListenable: writeType, - builder: (context, writeType, child) { - return ToggleButtons( - onPressed: (i) async { - final type = GattCharacteristicWriteType.values[i]; - this.writeType.value = type; - maximumWriteLength.value = - await centralManager.getMaximumWriteLength( - eventArgs.peripheral, - type: type, - ); - }, - constraints: const BoxConstraints( - minWidth: 0.0, - minHeight: 0.0, - ), - borderRadius: BorderRadius.circular(4.0), - isSelected: GattCharacteristicWriteType.values - .map((type) => type == writeType) - .toList(), - children: GattCharacteristicWriteType.values.map((type) { - return Container( - margin: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 4.0, - ), - child: Text(type.name), - ); - }).toList(), - ); - // final segments = - // GattCharacteristicWriteType.values.map((type) { - // return ButtonSegment( - // value: type, - // label: Text(type.name), - // ); - // }).toList(); - // return SegmentedButton( - // segments: segments, - // selected: {writeType}, - // showSelectedIcon: false, - // style: OutlinedButton.styleFrom( - // tapTargetSize: MaterialTapTargetSize.shrinkWrap, - // padding: EdgeInsets.zero, - // visualDensity: VisualDensity.compact, - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(8.0), - // ), - // ), - // ); - }, - ), - const SizedBox(width: 8.0), - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: maximumWriteLength, - builder: (context, maximumWriteLength, child) { - return Text('$maximumWriteLength'); - }, - ); - }, - ), - const Spacer(), - ValueListenableBuilder( - valueListenable: rssi, - builder: (context, rssi, child) { - return RssiWidget(rssi); - }, - ), - ], - ), - Container( - margin: const EdgeInsets.only(bottom: 16.0), - height: 160.0, - child: ValueListenableBuilder( - valueListenable: characteristic, - builder: (context, characteristic, child) { - final bool canNotify, canRead, canWrite; - if (characteristic == null) { - canNotify = canRead = canWrite = false; - } else { - final properties = characteristic.properties; - canNotify = properties.contains( - GattCharacteristicProperty.notify, - ); - canRead = properties.contains( - GattCharacteristicProperty.read, - ); - canWrite = properties.contains( - GattCharacteristicProperty.write, - ); - } - return Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded( - child: TextField( - controller: writeController, - enabled: canWrite, - expands: true, - maxLines: null, - textAlignVertical: TextAlignVertical.top, - decoration: const InputDecoration( - border: OutlineInputBorder(), - contentPadding: EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 8.0, - ), - ), - ), - ), - Column( - mainAxisSize: MainAxisSize.min, + ValueListenableBuilder( + valueListenable: characteristic, + builder: (context, characteristic, chld) { + final bool canNotify, canRead, canWrite, canWriteWithoutResponse; + if (characteristic == null) { + canNotify = + canRead = canWrite = canWriteWithoutResponse = false; + } else { + final properties = characteristic.properties; + canNotify = properties.contains( + GattCharacteristicProperty.notify, + ) || + properties.contains( + GattCharacteristicProperty.indicate, + ); + canRead = properties.contains( + GattCharacteristicProperty.read, + ); + canWrite = properties.contains( + GattCharacteristicProperty.write, + ); + canWriteWithoutResponse = properties.contains( + GattCharacteristicProperty.writeWithoutResponse, + ); + } + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + margin: const EdgeInsets.symmetric(vertical: 4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ - TextButton( + ElevatedButton( onPressed: characteristic != null && canNotify ? () async { - await centralManager.notifyCharacteristic( + await CentralManager.instance + .setCharacteristicNotifyState( characteristic, state: true, ); @@ -754,10 +623,11 @@ class _PeripheralViewState extends State { : null, child: const Text('NOTIFY'), ), - TextButton( + const SizedBox(width: 8.0), + ElevatedButton( onPressed: characteristic != null && canRead ? () async { - final value = await centralManager + final value = await CentralManager.instance .readCharacteristic(characteristic); const type = LogType.read; final log = Log(type, value); @@ -765,31 +635,124 @@ class _PeripheralViewState extends State { } : null, child: const Text('READ'), - ), - TextButton( - onPressed: characteristic != null && canWrite - ? () async { - final text = writeController.text; - final elements = utf8.encode(text); - final value = Uint8List.fromList(elements); - final type = writeType.value; - await centralManager.writeCharacteristic( - characteristic, - value: value, - type: type, - ); - final log = Log(LogType.write, value); - logs.value = [...logs.value, log]; - } - : null, - child: const Text('WRITE'), - ), + ) ], ), - ], - ); - }, - ), + ), + SizedBox( + height: 160.0, + child: TextField( + controller: writeController, + enabled: canWrite || canWriteWithoutResponse, + expands: true, + maxLines: null, + textAlignVertical: TextAlignVertical.top, + decoration: const InputDecoration( + border: OutlineInputBorder(), + contentPadding: EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 8.0, + ), + ), + ), + ), + Row( + children: [ + ValueListenableBuilder( + valueListenable: writeType, + builder: (context, writeType, child) { + return ToggleButtons( + onPressed: canWrite || canWriteWithoutResponse + ? (i) { + if (!canWrite || !canWriteWithoutResponse) { + return; + } + final type = + GattCharacteristicWriteType.values[i]; + this.writeType.value = type; + } + : null, + constraints: const BoxConstraints( + minWidth: 0.0, + minHeight: 0.0, + ), + borderRadius: BorderRadius.circular(4.0), + isSelected: GattCharacteristicWriteType.values + .map((type) => type == writeType) + .toList(), + children: GattCharacteristicWriteType.values.map( + (type) { + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, + ), + child: Text(type.name), + ); + }, + ).toList(), + ); + // final segments = + // GattCharacteristicWriteType.values.map((type) { + // return ButtonSegment( + // value: type, + // label: Text(type.name), + // ); + // }).toList(); + // return SegmentedButton( + // segments: segments, + // selected: {writeType}, + // showSelectedIcon: false, + // style: OutlinedButton.styleFrom( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, + // padding: EdgeInsets.zero, + // visualDensity: VisualDensity.compact, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8.0), + // ), + // ), + // ); + }, + ), + const Spacer(), + ElevatedButton( + onPressed: characteristic != null && canWrite + ? () async { + final text = writeController.text; + final elements = utf8.encode(text); + final value = Uint8List.fromList(elements); + final type = writeType.value; + // Fragments the value by 512 bytes. + const fragmentSize = 512; + var start = 0; + while (start < value.length) { + final end = start + fragmentSize; + final fragmentedValue = end < value.length + ? value.sublist(start, end) + : value.sublist(start); + await CentralManager.instance + .writeCharacteristic( + characteristic, + value: fragmentedValue, + type: type, + ); + final log = Log( + LogType.write, + fragmentedValue, + ); + logs.value = [...logs.value, log]; + start = end; + } + } + : null, + child: const Text('WRITE'), + ), + ], + ), + const SizedBox(height: 16.0), + ], + ); + }, ), ], ), @@ -799,290 +762,19 @@ class _PeripheralViewState extends State { @override void dispose() { super.dispose(); - rssiTimer.cancel(); - stateChangedSubscription.cancel(); - valueChangedSubscription.cancel(); - state.dispose(); + connectionStateChangedSubscription.cancel(); + characteristicNotifiedSubscription.cancel(); + connectionState.dispose(); services.dispose(); characteristics.dispose(); service.dispose(); characteristic.dispose(); writeType.dispose(); - maximumWriteLength.dispose(); - rssi.dispose(); logs.dispose(); writeController.dispose(); } } -class AdvertiserView extends StatefulWidget { - const AdvertiserView({super.key}); - - @override - State createState() => _AdvertiserViewState(); -} - -class _AdvertiserViewState extends State - with SingleTickerProviderStateMixin { - late final ValueNotifier state; - late final ValueNotifier advertising; - late final ValueNotifier> logs; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription readCharacteristicCommandReceivedSubscription; - late final StreamSubscription writeCharacteristicCommandReceivedSubscription; - late final StreamSubscription notifyCharacteristicCommandReceivedSubscription; - - @override - void initState() { - super.initState(); - state = ValueNotifier(peripheralManager.state); - advertising = ValueNotifier(false); - logs = ValueNotifier([]); - stateChangedSubscription = peripheralManager.stateChanged.listen( - (eventArgs) { - state.value = eventArgs.state; - }, - ); - readCharacteristicCommandReceivedSubscription = - peripheralManager.readCharacteristicCommandReceived.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final id = eventArgs.id; - final offset = eventArgs.offset; - final log = Log( - LogType.read, - Uint8List.fromList([]), - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', - ); - logs.value = [ - ...logs.value, - log, - ]; - // final maximumWriteLength = peripheralManager.getMaximumWriteLength( - // central, - // ); - const status = true; - final value = Uint8List.fromList([0x01, 0x02, 0x03]); - await peripheralManager.sendReadCharacteristicReply( - central, - characteristic: characteristic, - id: id, - offset: offset, - status: status, - value: value, - ); - }, - ); - writeCharacteristicCommandReceivedSubscription = - peripheralManager.writeCharacteristicCommandReceived.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final id = eventArgs.id; - final offset = eventArgs.offset; - final value = eventArgs.value; - final log = Log( - LogType.write, - value, - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', - ); - logs.value = [ - ...logs.value, - log, - ]; - const status = true; - await peripheralManager.sendWriteCharacteristicReply( - central, - characteristic: characteristic, - id: id, - offset: offset, - status: status, - ); - }, - ); - notifyCharacteristicCommandReceivedSubscription = - peripheralManager.notifyCharacteristicCommandReceived.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final state = eventArgs.state; - final log = Log( - LogType.write, - Uint8List.fromList([]), - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state', - ); - logs.value = [ - ...logs.value, - log, - ]; - // Write someting to the central when notify started. - if (state) { - final value = Uint8List.fromList([0x03, 0x02, 0x01]); - await peripheralManager.notifyCharacteristicValueChanged( - central, - characteristic: characteristic, - value: value, - ); - } - }, - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), - ); - } - - PreferredSizeWidget buildAppBar(BuildContext context) { - return AppBar( - title: const Text('Advertiser'), - actions: [ - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: advertising, - builder: (context, advertising, child) { - return TextButton( - onPressed: state == BluetoothLowEnergyState.poweredOn - ? () async { - if (advertising) { - await stopAdvertising(); - } else { - await startAdvertising(); - } - } - : null, - child: Text( - advertising ? 'END' : 'BEGIN', - ), - ); - }, - ); - }, - ), - ], - ); - } - - Future startAdvertising() async { - await peripheralManager.clearServices(); - final service = GattService( - uuid: UUID.short(100), - characteristics: [ - GattCharacteristic( - uuid: UUID.short(200), - properties: [ - GattCharacteristicProperty.read, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(201), - properties: [ - GattCharacteristicProperty.read, - GattCharacteristicProperty.write, - GattCharacteristicProperty.writeWithoutResponse, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(202), - properties: [ - GattCharacteristicProperty.notify, - GattCharacteristicProperty.indicate, - ], - descriptors: [], - ), - ], - ); - await peripheralManager.addService(service); - final advertisement = Advertisement( - name: 'flutter', - manufacturerSpecificData: ManufacturerSpecificData( - id: 0x2e19, - data: Uint8List.fromList([0x01, 0x02, 0x03]), - ), - ); - await peripheralManager.startAdvertising(advertisement); - advertising.value = true; - } - - Future stopAdvertising() async { - await peripheralManager.stopAdvertising(); - advertising.value = false; - } - - Widget buildBody(BuildContext context) { - final theme = Theme.of(context); - return ValueListenableBuilder( - valueListenable: logs, - builder: (context, logs, child) { - return ListView.builder( - itemBuilder: (context, i) { - final log = logs[i]; - final type = log.type.name.toUpperCase().characters.first; - final Color typeColor; - switch (log.type) { - case LogType.read: - typeColor = Colors.blue; - break; - case LogType.write: - typeColor = Colors.amber; - break; - case LogType.notify: - typeColor = Colors.red; - break; - default: - typeColor = Colors.black; - } - final time = DateFormat.Hms().format(log.time); - final value = log.value; - final message = '${log.detail}; ${hex.encode(value)}'; - return Text.rich( - TextSpan( - text: '[$type:${value.length}]', - children: [ - TextSpan( - text: ' $time: ', - style: theme.textTheme.bodyMedium?.copyWith( - color: Colors.green, - ), - ), - TextSpan( - text: message, - style: theme.textTheme.bodyMedium, - ), - ], - style: theme.textTheme.bodyMedium?.copyWith( - color: typeColor, - ), - ), - ); - }, - itemCount: logs.length, - ); - }, - ); - } - - @override - void dispose() { - super.dispose(); - stateChangedSubscription.cancel(); - readCharacteristicCommandReceivedSubscription.cancel(); - writeCharacteristicCommandReceivedSubscription.cancel(); - notifyCharacteristicCommandReceivedSubscription.cancel(); - state.dispose(); - advertising.dispose(); - logs.dispose(); - } -} - class Log { final DateTime time; final LogType type; diff --git a/bluetooth_low_energy_linux/example/pubspec.lock b/bluetooth_low_energy_linux/example/pubspec.lock index 15358f2..7659b54 100644 --- a/bluetooth_low_energy_linux/example/pubspec.lock +++ b/bluetooth_low_energy_linux/example/pubspec.lock @@ -23,15 +23,15 @@ packages: path: ".." relative: true source: path - version: "4.0.0" + version: "5.0.0" bluetooth_low_energy_platform_interface: dependency: "direct main" description: name: bluetooth_low_energy_platform_interface - sha256: a01819f4ef89d033edaa979465ec8c3af13b2618dc718d90fe681be91b6c4356 + sha256: "54f92ab2d7746fb6f2b4a40a48cd7eb8e3bf772f3ee89e1979d4d7b741fb2a05" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" bluez: dependency: transitive description: @@ -68,10 +68,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: "direct main" description: @@ -92,10 +92,10 @@ packages: dependency: transitive description: name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.10" fake_async: dependency: transitive description: @@ -134,10 +134,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -157,10 +157,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" lints: dependency: transitive description: @@ -205,10 +205,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -221,26 +221,26 @@ 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: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" process: dependency: transitive description: @@ -266,18 +266,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -306,10 +306,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" typed_data: dependency: transitive description: @@ -330,18 +330,18 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 url: "https://pub.dev" source: hosted - version: "11.7.1" + version: "11.10.0" web: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" webdriver: dependency: transitive description: @@ -354,10 +354,10 @@ packages: 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.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.3.0" diff --git a/bluetooth_low_energy_linux/example/pubspec.yaml b/bluetooth_low_energy_linux/example/pubspec.yaml index e314fca..0fa0834 100644 --- a/bluetooth_low_energy_linux/example/pubspec.yaml +++ b/bluetooth_low_energy_linux/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^4.0.0 + bluetooth_low_energy_platform_interface: ^5.0.0 bluetooth_low_energy_linux: # When depending on this package from a real application you should use: # bluetooth_low_energy: ^x.y.z @@ -30,7 +30,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 convert: ^3.1.1 - intl: ^0.18.1 + intl: ^0.19.0 dev_dependencies: integration_test: diff --git a/bluetooth_low_energy_linux/lib/bluetooth_low_energy_linux.dart b/bluetooth_low_energy_linux/lib/bluetooth_low_energy_linux.dart index f6bc83d..4f11f9d 100644 --- a/bluetooth_low_energy_linux/lib/bluetooth_low_energy_linux.dart +++ b/bluetooth_low_energy_linux/lib/bluetooth_low_energy_linux.dart @@ -1,9 +1,9 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'src/my_central_manager2.dart'; +import 'src/my_central_manager.dart'; abstract class BluetoothLowEnergyLinux { static void registerWith() { - MyCentralManager.instance = MyCentralManager2(); + CentralManager.instance = MyCentralManager(); } } diff --git a/bluetooth_low_energy_linux/lib/src/my_bluez.dart b/bluetooth_low_energy_linux/lib/src/my_bluez.dart index c8ed459..7e5d714 100644 --- a/bluetooth_low_energy_linux/lib/src/my_bluez.dart +++ b/bluetooth_low_energy_linux/lib/src/my_bluez.dart @@ -7,81 +7,6 @@ import 'my_gatt_characteristic2.dart'; import 'my_gatt_descriptor2.dart'; import 'my_gatt_service2.dart'; -extension BlueZAdapterX on BlueZAdapter { - BluetoothLowEnergyState get myState { - return powered - ? BluetoothLowEnergyState.poweredOn - : BluetoothLowEnergyState.poweredOff; - } -} - -extension BlueZDeviceX on BlueZDevice { - BlueZUUID get uuid { - final node = address.replaceAll(':', ''); - // We don't know the timestamp of the bluetooth device, use nil UUID as prefix. - return BlueZUUID.fromString("00000000-0000-0000-0000-$node"); - } - - UUID get myUUID => uuid.toMyUUID(); - - List get myServices => - gattServices.map((service) => MyGattService2(service)).toList(); - - Advertisement get myAdvertisement { - final myName = name.isNotEmpty ? name : null; - final myServiceUUIDs = uuids.map((uuid) => uuid.toMyUUID()).toList(); - final myServiceData = serviceData.map((uuid, data) { - final myUUID = uuid.toMyUUID(); - final myData = Uint8List.fromList(data); - return MapEntry(myUUID, myData); - }); - return Advertisement( - name: myName, - serviceUUIDs: myServiceUUIDs, - serviceData: myServiceData, - manufacturerSpecificData: myManufacturerSpecificData, - ); - } - - ManufacturerSpecificData get myManufacturerSpecificData { - final entry = manufacturerData.entries.last; - final myId = entry.key.id; - final myData = Uint8List.fromList(entry.value); - return ManufacturerSpecificData( - id: myId, - data: myData, - ); - } -} - -extension BlueZGattServiceX on BlueZGattService { - UUID get myUUID => uuid.toMyUUID(); - - List get myCharacteristics => characteristics - .map((characteristic) => MyGattCharacteristic2(characteristic)) - .toList(); -} - -extension MyBlueZGattCharacteristic on BlueZGattCharacteristic { - UUID get myUUID => uuid.toMyUUID(); - - List get myProperties => flags - .map((e) => e.toMyProperty()) - .whereType() - .toList(); - - List get myDescriptors => - descriptors.map((descriptor) => MyGattDescriptor2(descriptor)).toList(); -} - -extension BlueZGattDescriptorX on BlueZGattDescriptor { - UUID get myUUID => uuid.toMyUUID(); -} - -extension BlueZUUIDX on BlueZUUID { - UUID toMyUUID() => UUID(value); -} - extension BlueZGattCharacteristicFlagX on BlueZGattCharacteristicFlag { GattCharacteristicProperty? toMyProperty() { switch (this) { @@ -113,3 +38,75 @@ extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType { } } } + +extension BlueZAdapterX on BlueZAdapter { + BluetoothLowEnergyState get myState { + return powered + ? BluetoothLowEnergyState.poweredOn + : BluetoothLowEnergyState.poweredOff; + } +} + +extension BlueZDeviceX on BlueZDevice { + UUID get myUUID => UUID.fromAddress(address); + + List get myServices => + gattServices.map((service) => MyGattService2(service)).toList(); + + Advertisement get myAdvertisement { + final myName = name.isNotEmpty ? name : null; + final myServiceUUIDs = uuids.map((uuid) => uuid.toMyUUID()).toList(); + final myServiceData = serviceData.map((uuid, data) { + final myUUID = uuid.toMyUUID(); + final myData = Uint8List.fromList(data); + return MapEntry(myUUID, myData); + }); + return Advertisement( + name: myName, + serviceUUIDs: myServiceUUIDs, + serviceData: myServiceData, + manufacturerSpecificData: myManufacturerSpecificData, + ); + } + + ManufacturerSpecificData? get myManufacturerSpecificData { + final entry = manufacturerData.entries.lastOrNull; + if (entry == null) { + return null; + } + final myId = entry.key.id; + final myData = Uint8List.fromList(entry.value); + return ManufacturerSpecificData( + id: myId, + data: myData, + ); + } +} + +extension BlueZGattDescriptorX on BlueZGattDescriptor { + UUID get myUUID => uuid.toMyUUID(); +} + +extension MyBlueZGattCharacteristic on BlueZGattCharacteristic { + UUID get myUUID => uuid.toMyUUID(); + + List get myProperties => flags + .map((e) => e.toMyProperty()) + .whereType() + .toList(); + + List get myDescriptors => + descriptors.map((descriptor) => MyGattDescriptor2(descriptor)).toList(); +} + +extension BlueZGattServiceX on BlueZGattService { + UUID get myUUID => uuid.toMyUUID(); + + List get myCharacteristics => characteristics + .map((characteristic) => MyGattCharacteristic2(characteristic)) + .toList(); +} + +extension BlueZUUIDX on BlueZUUID { + UUID toMyUUID() => UUID(value); +} diff --git a/bluetooth_low_energy_linux/lib/src/my_central_manager.dart b/bluetooth_low_energy_linux/lib/src/my_central_manager.dart new file mode 100644 index 0000000..4db6ccc --- /dev/null +++ b/bluetooth_low_energy_linux/lib/src/my_central_manager.dart @@ -0,0 +1,364 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluez/bluez.dart'; + +import 'my_bluez.dart'; +import 'my_event_args.dart'; +import 'my_gatt_characteristic2.dart'; +import 'my_gatt_descriptor2.dart'; +import 'my_gatt_service2.dart'; +import 'my_peripheral2.dart'; + +class MyCentralManager extends CentralManager { + final BlueZClient _blueZClient; + final StreamController + _stateChangedController; + final StreamController _discoveredController; + final StreamController + _connectionStateChangedController; + final StreamController + _characteristicNotifiedController; + final StreamController + _blueZServicesResolvedController; + + final Map + _blueZCharacteristicPropertiesChangedSubscriptions; + final Map> _services; + + BluetoothLowEnergyState _state; + + MyCentralManager() + : _blueZClient = BlueZClient(), + _stateChangedController = StreamController.broadcast(), + _discoveredController = StreamController.broadcast(), + _connectionStateChangedController = StreamController.broadcast(), + _characteristicNotifiedController = StreamController.broadcast(), + _blueZServicesResolvedController = StreamController.broadcast(), + _blueZCharacteristicPropertiesChangedSubscriptions = {}, + _services = {}, + _state = BluetoothLowEnergyState.unknown; + + BlueZAdapter get _blueZAdapter => _blueZClient.adapters.first; + + @override + Stream get stateChanged => + _stateChangedController.stream; + @override + Stream get discovered => _discoveredController.stream; + @override + Stream get connectionStateChanged => + _connectionStateChangedController.stream; + @override + Stream get characteristicNotified => + _characteristicNotifiedController.stream; + Stream get _blueZServicesResolved => + _blueZServicesResolvedController.stream; + + @override + Future setUp() async { + logger.info('setUp'); + await _blueZClient.connect(); + if (_blueZClient.adapters.isEmpty) { + _state = BluetoothLowEnergyState.unsupported; + return; + } + _state = _blueZAdapter.myState; + _blueZAdapter.propertiesChanged.listen(_onBlueZAdapterPropertiesChanged); + for (var blueZDevice in _blueZClient.devices) { + if (blueZDevice.adapter.address != _blueZAdapter.address) { + continue; + } + _beginBlueZDevicePropertiesChangedListener(blueZDevice); + } + _blueZClient.deviceAdded.listen(_onBlueZClientDeviceAdded); + } + + @override + Future getState() { + logger.info('getState'); + return Future.value(_state); + } + + @override + Future startDiscovery() async { + logger.info('startDiscovery'); + await _blueZAdapter.startDiscovery(); + } + + @override + Future stopDiscovery() async { + logger.info('stopDiscovery'); + await _blueZAdapter.stopDiscovery(); + } + + @override + Future connect(Peripheral peripheral) async { + if (peripheral is! MyPeripheral2) { + throw TypeError(); + } + final blueZDevice = peripheral.blueZDevice; + final blueZAddress = blueZDevice.address; + logger.info('connect: $blueZAddress'); + await blueZDevice.connect(); + } + + @override + Future disconnect(Peripheral peripheral) async { + if (peripheral is! MyPeripheral2) { + throw TypeError(); + } + final blueZDevice = peripheral.blueZDevice; + final blueZAddress = blueZDevice.address; + logger.info('disconnect: $blueZAddress'); + await blueZDevice.disconnect(); + } + + @override + Future readRSSI(Peripheral peripheral) async { + if (peripheral is! MyPeripheral2) { + throw TypeError(); + } + final blueZDevice = peripheral.blueZDevice; + final blueZAddress = blueZDevice.address; + logger.info('readRSSI: $blueZAddress'); + return blueZDevice.rssi; + } + + @override + Future> discoverGATT(Peripheral peripheral) async { + if (peripheral is! MyPeripheral2) { + throw TypeError(); + } + final blueZDevice = peripheral.blueZDevice; + final blueZAddress = blueZDevice.address; + logger.info('discoverGATT: $blueZAddress'); + if (!blueZDevice.connected) { + throw StateError('Peripheral is disconnected.'); + } + if (!blueZDevice.servicesResolved) { + await _blueZServicesResolved.firstWhere( + (eventArgs) => eventArgs.device == blueZDevice, + ); + } + final services = _services[blueZAddress] ?? []; + return services; + } + + @override + Future readCharacteristic( + GattCharacteristic characteristic, + ) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final blueZCharacteristic = characteristic.blueZCharacteristic; + final blueZUUID = blueZCharacteristic.uuid; + logger.info('readCharacteristic: $blueZUUID'); + final blueZValue = await blueZCharacteristic.readValue(); + return Uint8List.fromList(blueZValue); + } + + @override + Future writeCharacteristic( + GattCharacteristic characteristic, { + required Uint8List value, + required GattCharacteristicWriteType type, + }) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final blueZCharacteristic = characteristic.blueZCharacteristic; + final blueZUUID = blueZCharacteristic.uuid; + final trimmedValue = value.trimGATT(); + final blueZType = type.toBlueZWriteType(); + logger.info('writeCharacteristic: $blueZUUID - $trimmedValue, $blueZType'); + await blueZCharacteristic.writeValue( + trimmedValue, + type: blueZType, + ); + } + + @override + Future setCharacteristicNotifyState( + GattCharacteristic characteristic, { + required bool state, + }) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final blueZCharacteristic = characteristic.blueZCharacteristic; + final blueZUUID = blueZCharacteristic.uuid; + if (state) { + logger.info('startNotify: $blueZUUID'); + await blueZCharacteristic.startNotify(); + } else { + logger.info('stopNotify: $blueZUUID'); + await blueZCharacteristic.stopNotify(); + } + } + + @override + Future readDescriptor(GattDescriptor descriptor) async { + if (descriptor is! MyGattDescriptor2) { + throw TypeError(); + } + final blueZDescriptor = descriptor.blueZDescriptor; + final blueZUUID = blueZDescriptor.uuid; + logger.info('readDescriptor: $blueZUUID'); + final blueZValue = await blueZDescriptor.readValue(); + return Uint8List.fromList(blueZValue); + } + + @override + Future writeDescriptor( + GattDescriptor descriptor, { + required Uint8List value, + }) async { + if (descriptor is! MyGattDescriptor2) { + throw TypeError(); + } + final blueZDescriptor = descriptor.blueZDescriptor; + final blueZUUID = blueZDescriptor.uuid; + final trimmedValue = value.trimGATT(); + logger.info('writeDescriptor: $blueZUUID - $trimmedValue'); + await blueZDescriptor.writeValue(trimmedValue); + } + + void _onBlueZAdapterPropertiesChanged(List blueZAdapterProperties) { + logger.info('onBlueZAdapterPropertiesChanged: $blueZAdapterProperties'); + for (var blueZAdapterProperty in blueZAdapterProperties) { + switch (blueZAdapterProperty) { + case 'Powered': + final state = _blueZAdapter.myState; + if (_state == state) { + return; + } + _state = state; + final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); + _stateChangedController.add(eventArgs); + break; + default: + break; + } + } + } + + void _onBlueZClientDeviceAdded(BlueZDevice blueZDevice) { + logger.info('onBlueZClientDeviceAdded: ${blueZDevice.address}'); + if (blueZDevice.adapter.address != _blueZAdapter.address) { + return; + } + _onBlueZDiscovered(blueZDevice); + _beginBlueZDevicePropertiesChangedListener(blueZDevice); + } + + void _onBlueZDiscovered(BlueZDevice blueZDevice) { + final peripheral = MyPeripheral2(blueZDevice); + final rssi = blueZDevice.rssi; + final advertisement = blueZDevice.myAdvertisement; + final eventArgs = DiscoveredEventArgs( + peripheral, + rssi, + advertisement, + ); + _discoveredController.add(eventArgs); + } + + void _beginBlueZDevicePropertiesChangedListener(BlueZDevice blueZDevice) { + blueZDevice.propertiesChanged.listen((blueZDeviceProperties) { + logger.info( + 'onBlueZDevicePropertiesChanged: ${blueZDevice.address}, $blueZDeviceProperties'); + for (var blueZDeviceProperty in blueZDeviceProperties) { + switch (blueZDeviceProperty) { + case 'RSSI': + _onBlueZDiscovered(blueZDevice); + break; + case 'Connected': + final peripheral = MyPeripheral2(blueZDevice); + final state = blueZDevice.connected; + final eventArgs = ConnectionStateChangedEventArgs( + peripheral, + state, + ); + _connectionStateChangedController.add(eventArgs); + if (!state) { + _endBlueZCharacteristicPropertiesChangedListener(blueZDevice); + } + break; + case 'UUIDs': + break; + case 'ServicesResolved': + if (blueZDevice.servicesResolved) { + _endBlueZCharacteristicPropertiesChangedListener(blueZDevice); + _services[blueZDevice.address] = blueZDevice.myServices; + _beginBlueZCharacteristicPropertiesChangedListener(blueZDevice); + final eventArgs = + BlueZDeviceServicesResolvedEventArgs(blueZDevice); + _blueZServicesResolvedController.add(eventArgs); + } + break; + default: + break; + } + } + }); + } + + void _beginBlueZCharacteristicPropertiesChangedListener( + BlueZDevice blueZDevice, + ) { + final services = _services[blueZDevice.address]; + if (services == null) { + return; + } + for (var service in services) { + final characteristics = service.characteristics; + for (var characteristic in characteristics) { + final blueZCharacteristic = characteristic.blueZCharacteristic; + final subscription = blueZCharacteristic.propertiesChanged.listen( + (blueZCharacteristicProperties) { + logger.info( + 'onBlueZCharacteristicPropertiesChanged: ${blueZDevice.address}.${blueZCharacteristic.uuid}, $blueZCharacteristicProperties'); + for (var blueZCharacteristicPropety + in blueZCharacteristicProperties) { + switch (blueZCharacteristicPropety) { + case 'Value': + final value = Uint8List.fromList(blueZCharacteristic.value); + final eventArgs = GattCharacteristicNotifiedEventArgs( + characteristic, + value, + ); + _characteristicNotifiedController.add(eventArgs); + break; + default: + break; + } + } + }, + ); + _blueZCharacteristicPropertiesChangedSubscriptions[ + blueZCharacteristic.hashCode] = subscription; + } + } + } + + void _endBlueZCharacteristicPropertiesChangedListener( + BlueZDevice blueZDevice, + ) { + final services = _services.remove(blueZDevice.address); + if (services == null) { + return; + } + for (var service in services) { + final characteristics = service.characteristics; + for (var characteristic in characteristics) { + final blueZCharacteristic = characteristic.blueZCharacteristic; + final subscription = _blueZCharacteristicPropertiesChangedSubscriptions + .remove(blueZCharacteristic.hashCode); + subscription?.cancel(); + } + } + } +} diff --git a/bluetooth_low_energy_linux/lib/src/my_central_manager2.dart b/bluetooth_low_energy_linux/lib/src/my_central_manager2.dart deleted file mode 100644 index 366c509..0000000 --- a/bluetooth_low_energy_linux/lib/src/my_central_manager2.dart +++ /dev/null @@ -1,341 +0,0 @@ -import 'dart:async'; -import 'dart:typed_data'; - -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:bluez/bluez.dart'; - -import 'my_bluez.dart'; -import 'my_event_args.dart'; -import 'my_gatt_characteristic2.dart'; -import 'my_gatt_descriptor2.dart'; -import 'my_gatt_service2.dart'; -import 'my_peripheral2.dart'; - -class MyCentralManager2 extends MyCentralManager { - MyCentralManager2() - : _client = BlueZClient(), - _stateChangedController = StreamController.broadcast(), - _discoveredController = StreamController.broadcast(), - _peripheralStateChangedController = StreamController.broadcast(), - _characteristicValueChangedController = StreamController.broadcast(), - _deviceServicesResolvedController = StreamController.broadcast(), - _myServicesOfMyPeripherals = {}, - _characteristicPropertiesChangedSubscriptions = {}, - _state = BluetoothLowEnergyState.unknown; - - final BlueZClient _client; - final StreamController - _stateChangedController; - final StreamController _discoveredController; - final StreamController - _peripheralStateChangedController; - final StreamController - _characteristicValueChangedController; - final StreamController - _deviceServicesResolvedController; - - final Map> _myServicesOfMyPeripherals; - final Map - _characteristicPropertiesChangedSubscriptions; - - BlueZAdapter get _adapter => _client.adapters.first; - BluetoothLowEnergyState _state; - @override - BluetoothLowEnergyState get state => _state; - - @override - Stream get stateChanged => - _stateChangedController.stream; - @override - Stream get discovered => _discoveredController.stream; - @override - Stream get peripheralStateChanged => - _peripheralStateChangedController.stream; - @override - Stream - get characteristicValueChanged => - _characteristicValueChangedController.stream; - Stream get _servicesResolved => - _deviceServicesResolvedController.stream; - - Future _throwWithoutState(BluetoothLowEnergyState state) async { - if (this.state != state) { - throw StateError( - '$state is expected, but current state is ${this.state}.'); - } - } - - @override - Future setUp() async { - // TODO: hot restart is not handled. - await _client.connect(); - _state = _client.adapters.isEmpty - ? BluetoothLowEnergyState.unsupported - : _adapter.myState; - if (_state == BluetoothLowEnergyState.unsupported) { - return; - } - for (var device in _client.devices) { - if (device.adapter.address != _adapter.address) { - continue; - } - _beginDevicePropertiesChangedListener(device); - } - _adapter.propertiesChanged.listen(_onAdapterPropertiesChanged); - _client.deviceAdded.listen(_onDeviceAdded); - } - - @override - Future startDiscovery() async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - await _adapter.startDiscovery(); - } - - @override - Future stopDiscovery() async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - await _adapter.stopDiscovery(); - } - - @override - Future connect(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final myPeripheral = peripheral as MyPeripheral2; - final device = myPeripheral.device; - await device.connect(); - } - - @override - Future disconnect(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final myPeripheral = peripheral as MyPeripheral2; - final device = myPeripheral.device; - await device.disconnect(); - } - - @override - Future getMaximumWriteLength( - Peripheral peripheral, { - required GattCharacteristicWriteType type, - }) async { - // TODO: 当前版本 `bluez` 插件不支持获取 MTU,返回最小值 20. - return 20; - } - - @override - Future readRSSI(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final myPeripheral = peripheral as MyPeripheral2; - final device = myPeripheral.device; - return device.rssi; - } - - @override - Future> discoverGATT(Peripheral peripheral) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final myPeripheral = peripheral as MyPeripheral2; - final device = myPeripheral.device; - if (!device.connected) { - throw StateError('Peripheral is disconnected.'); - } - if (!device.servicesResolved) { - await _servicesResolved.firstWhere( - (eventArgs) => eventArgs.device == device, - ); - } - final myServices = _myServicesOfMyPeripherals[myPeripheral.hashCode]; - if (myServices == null) { - throw ArgumentError.notNull(); - } - return myServices; - } - - @override - Future readCharacteristic( - GattCharacteristic characteristic, - ) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final myCharacteristic = characteristic as MyGattCharacteristic2; - final blueZCharacteristic = myCharacteristic.characteristic; - final blueZValue = await blueZCharacteristic.readValue(); - return Uint8List.fromList(blueZValue); - } - - @override - Future writeCharacteristic( - GattCharacteristic characteristic, { - required Uint8List value, - required GattCharacteristicWriteType type, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final myCharacteristic = characteristic as MyGattCharacteristic2; - final blueZCharacteristic = myCharacteristic.characteristic; - await blueZCharacteristic.writeValue( - value, - type: type.toBlueZWriteType(), - ); - } - - @override - Future notifyCharacteristic( - GattCharacteristic characteristic, { - required bool state, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final myCharacteristic = characteristic as MyGattCharacteristic2; - final blueZCharacteristic = myCharacteristic.characteristic; - if (state) { - await blueZCharacteristic.startNotify(); - } else { - await blueZCharacteristic.stopNotify(); - } - } - - @override - Future readDescriptor(GattDescriptor descriptor) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final myDescriptor = descriptor as MyGattDescriptor2; - final blueZDescriptor = myDescriptor.descriptor; - final blueZValue = await blueZDescriptor.readValue(); - return Uint8List.fromList(blueZValue); - } - - @override - Future writeDescriptor( - GattDescriptor descriptor, { - required Uint8List value, - }) async { - await _throwWithoutState(BluetoothLowEnergyState.poweredOn); - final myDescriptor = descriptor as MyGattDescriptor2; - final blueZDescriptor = myDescriptor.descriptor; - await blueZDescriptor.writeValue(value); - } - - void _onAdapterPropertiesChanged(List properties) { - for (var property in properties) { - switch (property) { - case 'Powered': - final myState = _adapter.myState; - if (_state == myState) { - return; - } - _state = myState; - final eventArgs = BluetoothLowEnergyStateChangedEventArgs(myState); - _stateChangedController.add(eventArgs); - break; - default: - break; - } - } - } - - void _onDeviceAdded(BlueZDevice device) { - if (device.adapter.address != _adapter.address) { - return; - } - _onDiscovered(device); - _beginDevicePropertiesChangedListener(device); - } - - void _onDiscovered(BlueZDevice device) { - final myPeripheral = MyPeripheral2(device); - final myRSSI = device.rssi; - final myAdvertiseData = device.myAdvertisement; - final eventArgs = DiscoveredEventArgs( - myPeripheral, - myRSSI, - myAdvertiseData, - ); - _discoveredController.add(eventArgs); - } - - void _beginDevicePropertiesChangedListener(BlueZDevice device) { - device.propertiesChanged.listen((properties) { - for (var property in properties) { - switch (property) { - case 'RSSI': - _onDiscovered(device); - break; - case 'Connected': - final myPeripheral = MyPeripheral2(device); - final state = device.connected; - final eventArgs = PeripheralStateChangedEventArgs( - myPeripheral, - state, - ); - _peripheralStateChangedController.add(eventArgs); - if (!state) { - _endCharacteristicPropertiesChangedListener(myPeripheral); - } - break; - case 'UUIDs': - break; - case 'ServicesResolved': - if (device.servicesResolved) { - final myPeripheral = MyPeripheral2(device); - _endCharacteristicPropertiesChangedListener(myPeripheral); - final myServices = device.myServices; - _myServicesOfMyPeripherals[myPeripheral.hashCode] = myServices; - _beginCharacteristicPropertiesChangedListener(myPeripheral); - final eventArgs = BlueZDeviceServicesResolvedEventArgs(device); - _deviceServicesResolvedController.add(eventArgs); - } - break; - default: - break; - } - } - }); - } - - void _beginCharacteristicPropertiesChangedListener( - MyPeripheral myPeripheral, - ) { - final myServices = _myServicesOfMyPeripherals[myPeripheral.hashCode]; - if (myServices == null) { - throw ArgumentError.notNull(); - } - for (var myService in myServices) { - final myCharacteristics = - myService.characteristics.cast(); - for (var myCharacteristic in myCharacteristics) { - final characteristic = myCharacteristic.characteristic; - final subscription = characteristic.propertiesChanged.listen( - (properties) { - for (var property in properties) { - switch (property) { - case 'Value': - final myValue = Uint8List.fromList(characteristic.value); - final eventArgs = GattCharacteristicValueChangedEventArgs( - myCharacteristic, - myValue, - ); - _characteristicValueChangedController.add(eventArgs); - break; - default: - break; - } - } - }, - ); - _characteristicPropertiesChangedSubscriptions[ - myCharacteristic.hashCode] = subscription; - } - } - } - - void _endCharacteristicPropertiesChangedListener(MyPeripheral myPeripheral) { - final myServices = _myServicesOfMyPeripherals.remove(myPeripheral.hashCode); - if (myServices == null) { - return; - } - for (var myService in myServices) { - final myCharacteristics = myService.characteristics; - for (var myCharacteristic in myCharacteristics) { - final subscription = _characteristicPropertiesChangedSubscriptions - .remove(myCharacteristic.hashCode); - subscription?.cancel(); - } - } - } -} diff --git a/bluetooth_low_energy_linux/lib/src/my_gatt_characteristic2.dart b/bluetooth_low_energy_linux/lib/src/my_gatt_characteristic2.dart index 9bf0311..8aca310 100644 --- a/bluetooth_low_energy_linux/lib/src/my_gatt_characteristic2.dart +++ b/bluetooth_low_energy_linux/lib/src/my_gatt_characteristic2.dart @@ -2,15 +2,28 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla import 'package:bluez/bluez.dart'; import 'my_bluez.dart'; +import 'my_gatt_descriptor2.dart'; class MyGattCharacteristic2 extends MyGattCharacteristic { - final BlueZGattCharacteristic characteristic; + final BlueZGattCharacteristic blueZCharacteristic; - MyGattCharacteristic2(this.characteristic) + MyGattCharacteristic2(this.blueZCharacteristic) : super( - hashCode: characteristic.hashCode, - uuid: characteristic.myUUID, - properties: characteristic.myProperties, - descriptors: characteristic.myDescriptors, + uuid: blueZCharacteristic.myUUID, + properties: blueZCharacteristic.myProperties, + descriptors: blueZCharacteristic.myDescriptors, ); + + @override + List get descriptors => + super.descriptors.cast(); + + @override + int get hashCode => blueZCharacteristic.hashCode; + + @override + bool operator ==(Object other) { + return other is MyGattCharacteristic2 && + other.blueZCharacteristic == blueZCharacteristic; + } } diff --git a/bluetooth_low_energy_linux/lib/src/my_gatt_descriptor2.dart b/bluetooth_low_energy_linux/lib/src/my_gatt_descriptor2.dart index b8774ff..4cb4ba7 100644 --- a/bluetooth_low_energy_linux/lib/src/my_gatt_descriptor2.dart +++ b/bluetooth_low_energy_linux/lib/src/my_gatt_descriptor2.dart @@ -4,11 +4,19 @@ import 'package:bluez/bluez.dart'; import 'my_bluez.dart'; class MyGattDescriptor2 extends MyGattDescriptor { - final BlueZGattDescriptor descriptor; + final BlueZGattDescriptor blueZDescriptor; - MyGattDescriptor2(this.descriptor) + MyGattDescriptor2(this.blueZDescriptor) : super( - hashCode: descriptor.hashCode, - uuid: descriptor.myUUID, + uuid: blueZDescriptor.myUUID, ); + + @override + int get hashCode => blueZDescriptor.hashCode; + + @override + bool operator ==(Object other) { + return other is MyGattDescriptor2 && + other.blueZDescriptor == blueZDescriptor; + } } diff --git a/bluetooth_low_energy_linux/lib/src/my_gatt_service2.dart b/bluetooth_low_energy_linux/lib/src/my_gatt_service2.dart index 2c8cd41..bf7670a 100644 --- a/bluetooth_low_energy_linux/lib/src/my_gatt_service2.dart +++ b/bluetooth_low_energy_linux/lib/src/my_gatt_service2.dart @@ -2,14 +2,26 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla import 'package:bluez/bluez.dart'; import 'my_bluez.dart'; +import 'my_gatt_characteristic2.dart'; class MyGattService2 extends MyGattService { - final BlueZGattService service; + final BlueZGattService blueZService; - MyGattService2(this.service) + MyGattService2(this.blueZService) : super( - hashCode: service.hashCode, - uuid: service.myUUID, - characteristics: service.myCharacteristics, + uuid: blueZService.myUUID, + characteristics: blueZService.myCharacteristics, ); + + @override + List get characteristics => + super.characteristics.cast(); + + @override + int get hashCode => blueZService.hashCode; + + @override + bool operator ==(Object other) { + return other is MyGattService2 && other.blueZService == blueZService; + } } diff --git a/bluetooth_low_energy_linux/lib/src/my_peripheral2.dart b/bluetooth_low_energy_linux/lib/src/my_peripheral2.dart index 86d1cbb..77a5e22 100644 --- a/bluetooth_low_energy_linux/lib/src/my_peripheral2.dart +++ b/bluetooth_low_energy_linux/lib/src/my_peripheral2.dart @@ -4,11 +4,10 @@ import 'package:bluez/bluez.dart'; import 'my_bluez.dart'; class MyPeripheral2 extends MyPeripheral { - final BlueZDevice device; + final BlueZDevice blueZDevice; - MyPeripheral2(this.device) + MyPeripheral2(this.blueZDevice) : super( - hashCode: device.hashCode, - uuid: device.myUUID, + uuid: blueZDevice.myUUID, ); } diff --git a/bluetooth_low_energy_linux/pubspec.yaml b/bluetooth_low_energy_linux/pubspec.yaml index 6b39a76..ad9b15b 100644 --- a/bluetooth_low_energy_linux/pubspec.yaml +++ b/bluetooth_low_energy_linux/pubspec.yaml @@ -1,6 +1,6 @@ name: bluetooth_low_energy_linux description: Linux implementation of the bluetooth_low_energy plugin. -version: 4.0.0 +version: 5.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy environment: @@ -10,7 +10,7 @@ environment: dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^4.0.0 + bluetooth_low_energy_platform_interface: ^5.0.0 bluez: ^0.8.1 dev_dependencies: diff --git a/bluetooth_low_energy_platform_interface/CHANGELOG.md b/bluetooth_low_energy_platform_interface/CHANGELOG.md index 9c5059e..61b8593 100644 --- a/bluetooth_low_energy_platform_interface/CHANGELOG.md +++ b/bluetooth_low_energy_platform_interface/CHANGELOG.md @@ -1,3 +1,86 @@ +## 5.0.0 + +* Now `CentralManager#writeCharacteristic` and `PeripheralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes. +* Add `UUID#fromAddress` constructor. +* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`. +* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`. +* Add `PeripheralManager#readCharacteristic`. +* Remove `CentralManager#getMaximumWriteLength` method. +* Remove `PeripheralManager#getMaximumWriteLength` method. +* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`. +* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`. +* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`. +* Move `CentralManager#state` to `CentralManager#getState()`. +* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`. +* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`. +* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`. +* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`. +* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`. +* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#writeCharacteristic`. +* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`. +* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`. + + +## 5.0.0-dev.10 + +* Fix `Uint8List#trimGATT` throws when the value is not exceeded 512 bytes. + +## 5.0.0-dev.9 + +* Add `PeripheralManager#characteristicRead`. + +## 5.0.0-dev.8 + +* Add `PeripheralManager#readCharacteristic`. +* Move `PeripheralManager#notifyCharacteristic` to `PeripheralManager#writeCharacteristic`. + +## 5.0.0-dev.7 + +* Remove `GattCharacteristicReadEventArgs`. +* Remove `PeripheralManager#characteristicRead`. + +## 5.0.0-dev.6 + +* Remove the final modifier form `MyGattCharacteristic#value` and `MyGattDescriptor#value` and trim by 512 bytes. + +## 5.0.0-dev.5 + +* Move `CentralManager#state` to `CentralManager#getState()`. +* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`. +* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`. +* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`. +* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`. +* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`. +* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`. +* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`. +* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`. +* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`. +* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`. +* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`. +* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`. +* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#notifyCharacteristic`. +* Remove `MyCentralManager` and `MyPeripheralManager`. + +## 5.0.0-dev.4 + +* Optimize `MyGattService` and `MyGattCharacteristic`. + +## 5.0.0-dev.3 + +* Remove `CentralManager#getMaximumWriteLength` method. +* Remove `PeripheralManager#getMaximumWriteLength` method. + +## 5.0.0-dev.2 + +* Add `UUID#fromAddress` constructor. +* Override `hashCode` and `==` of `MyCentral` and `MyPeripheral`. + +## 5.0.0-dev.1 + +* Add `MyBluetoothLowEnergyPeer` and `MyGattAttribute`. +* Remove `MyObject` base class. +* Use `LoggerProvider` instead of custom logger. + ## 4.0.0 * Remove `BluetoothLowEnergy` class. diff --git a/bluetooth_low_energy_platform_interface/README.md b/bluetooth_low_energy_platform_interface/README.md index 0e3c975..f97371b 100644 --- a/bluetooth_low_energy_platform_interface/README.md +++ b/bluetooth_low_energy_platform_interface/README.md @@ -9,12 +9,12 @@ same interface. # Usage To implement a new platform-specific implementation of `bluetooth_low_energy`, -extend [`MyCentralManager`][2] with an implementation that performs the +extend [`CentralManager`][2] with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default -`MyCentralManager` by calling `MyCentralManager.instance = MyCentralManagerImpl()`, -extend [`MyPeripheralManager`][3] with an implementation that performs the +`CentralManager` by calling `CentralManager.instance = MyCentralManager()`, +extend [`PeripheralManager`][3] with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default -`MyPeripheralManager` by calling `MyPeripheralManager.instance = MyPeripheralManagerImpl()`. +`PeripheralManager` by calling `PeripheralManager.instance = MyPeripheralManager()`. # Note on breaking changes diff --git a/bluetooth_low_energy_platform_interface/lib/bluetooth_low_energy_platform_interface.dart b/bluetooth_low_energy_platform_interface/lib/bluetooth_low_energy_platform_interface.dart index 67d9b82..46a68fd 100644 --- a/bluetooth_low_energy_platform_interface/lib/bluetooth_low_energy_platform_interface.dart +++ b/bluetooth_low_energy_platform_interface/lib/bluetooth_low_energy_platform_interface.dart @@ -14,10 +14,10 @@ export 'src/bluetooth_low_energy_state.dart'; export 'src/bluetooth_low_energy_event_args.dart'; export 'src/bluetooth_low_energy_manager.dart'; export 'src/bluetooth_low_energy_peer.dart'; -export 'src/central_manager_event_args.dart'; +export 'src/central_event_args.dart'; export 'src/central_manager.dart'; export 'src/central.dart'; -export 'src/peripheral_manager_event_args.dart'; +export 'src/peripheral_event_args.dart'; export 'src/peripheral_manager.dart'; export 'src/peripheral.dart'; export 'src/uuid.dart'; @@ -28,11 +28,10 @@ export 'src/gatt_characteristic.dart'; export 'src/gatt_characteristic_property.dart'; export 'src/gatt_characteristic_write_type.dart'; export 'src/gatt_descriptor.dart'; -export 'src/my_central_manager.dart'; -export 'src/my_peripheral_manager.dart'; -export 'src/my_object.dart'; +export 'src/my_bluetooth_low_energy_peer.dart'; export 'src/my_central.dart'; export 'src/my_peripheral.dart'; +export 'src/my_gatt_attribute.dart'; export 'src/my_gatt_service.dart'; export 'src/my_gatt_characteristic.dart'; export 'src/my_gatt_descriptor.dart'; diff --git a/bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_manager.dart b/bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_manager.dart index b432a17..754f887 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_manager.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_manager.dart @@ -5,12 +5,12 @@ import 'bluetooth_low_energy_state.dart'; /// The abstract base class that manages central and peripheral objects. abstract class BluetoothLowEnergyManager implements LogController { - /// The current state of the manager. - BluetoothLowEnergyState get state; - - /// Tells the manager’s state updated. + /// Tells the manager's state updated. Stream get stateChanged; /// Sets up the manager. Future setUp(); + + /// Gets the manager's state. + Future getState(); } diff --git a/bluetooth_low_energy_platform_interface/lib/src/central_event_args.dart b/bluetooth_low_energy_platform_interface/lib/src/central_event_args.dart new file mode 100644 index 0000000..87cc55d --- /dev/null +++ b/bluetooth_low_energy_platform_interface/lib/src/central_event_args.dart @@ -0,0 +1,51 @@ +import 'dart:typed_data'; + +import 'advertisement.dart'; +import 'event_args.dart'; +import 'gatt_characteristic.dart'; +import 'peripheral.dart'; + +/// The discovered event arguments. +class DiscoveredEventArgs extends EventArgs { + /// The disvered peripheral. + final Peripheral peripheral; + + /// The rssi of the peripheral. + final int rssi; + + /// The advertisement of the peripheral. + final Advertisement advertisement; + + /// Constructs a [DiscoveredEventArgs]. + DiscoveredEventArgs(this.peripheral, this.rssi, this.advertisement); +} + +/// The connection state cahnged event arguments. +class ConnectionStateChangedEventArgs extends EventArgs { + /// The peripheral which connection state changed. + final Peripheral peripheral; + + /// The connection state. + final bool connectionState; + + /// Constructs a [ConnectionStateChangedEventArgs]. + ConnectionStateChangedEventArgs( + this.peripheral, + this.connectionState, + ); +} + +/// The GATT characteristic notified event arguments. +class GattCharacteristicNotifiedEventArgs extends EventArgs { + /// The GATT characteristic which notified. + final GattCharacteristic characteristic; + + /// The notified value. + final Uint8List value; + + /// Constructs a [GattCharacteristicNotifiedEventArgs]. + GattCharacteristicNotifiedEventArgs( + this.characteristic, + this.value, + ); +} diff --git a/bluetooth_low_energy_platform_interface/lib/src/central_manager.dart b/bluetooth_low_energy_platform_interface/lib/src/central_manager.dart index ed5bd53..f43c3d1 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/central_manager.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/central_manager.dart @@ -1,28 +1,55 @@ import 'dart:typed_data'; +import 'package:log_service/log_service.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + import 'bluetooth_low_energy_manager.dart'; -import 'central_manager_event_args.dart'; +import 'central_event_args.dart'; import 'gatt_characteristic.dart'; import 'gatt_characteristic_write_type.dart'; import 'gatt_descriptor.dart'; import 'gatt_service.dart'; -import 'my_central_manager.dart'; import 'peripheral.dart'; /// An object that scans for, discovers, connects to, and manages peripherals. -abstract class CentralManager extends BluetoothLowEnergyManager { - /// The instance of [CentralManager] to use. - static CentralManager get instance => MyCentralManager.instance; +/// +/// Platform-specific implementations should implement this class to support [CentralManager]. +abstract class CentralManager extends PlatformInterface + with LoggerProvider, LoggerController + implements BluetoothLowEnergyManager { + static final Object _token = Object(); + + static CentralManager? _instance; + + /// The default instance of [CentralManager] to use. + static CentralManager get instance { + final instance = _instance; + if (instance == null) { + throw UnimplementedError( + 'CentralManager is not implemented on this platform.'); + } + return instance; + } + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [CentralManager] when + /// they register themselves. + static set instance(CentralManager instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + /// Constructs a [CentralManager]. + CentralManager() : super(token: _token); /// Tells the central manager discovered a peripheral while scanning for devices. Stream get discovered; - /// Tells that retrieving the specified peripheral's state changed. - Stream get peripheralStateChanged; + /// Tells that retrieving the specified peripheral's connection lost. + Stream get connectionStateChanged; /// Tells that retrieving the specified characteristic’s value changed. - Stream - get characteristicValueChanged; + Stream get characteristicNotified; /// Scans for peripherals that are advertising services. Future startDiscovery(); @@ -36,12 +63,6 @@ abstract class CentralManager extends BluetoothLowEnergyManager { /// Cancels an active or pending local connection to a peripheral. Future disconnect(Peripheral peripheral); - /// Gets the maximum amount of data, in bytes, you can send to a characteristic in a single write type. - Future getMaximumWriteLength( - Peripheral peripheral, { - required GattCharacteristicWriteType type, - }); - /// Retrieves the current RSSI value for the peripheral while connected to the central manager. Future readRSSI(Peripheral peripheral); @@ -52,6 +73,8 @@ abstract class CentralManager extends BluetoothLowEnergyManager { Future readCharacteristic(GattCharacteristic characteristic); /// Writes the value of a characteristic. + /// + /// The maximum size of the value is 512, all bytes that exceed this size will be discarded. Future writeCharacteristic( GattCharacteristic characteristic, { required Uint8List value, @@ -59,7 +82,7 @@ abstract class CentralManager extends BluetoothLowEnergyManager { }); /// Sets notifications or indications for the value of a specified characteristic. - Future notifyCharacteristic( + Future setCharacteristicNotifyState( GattCharacteristic characteristic, { required bool state, }); diff --git a/bluetooth_low_energy_platform_interface/lib/src/central_manager_event_args.dart b/bluetooth_low_energy_platform_interface/lib/src/central_manager_event_args.dart deleted file mode 100644 index 59c2482..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/central_manager_event_args.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'dart:typed_data'; - -import 'advertisement.dart'; -import 'event_args.dart'; -import 'gatt_characteristic.dart'; -import 'peripheral.dart'; - -/// The discovered event arguments. -class DiscoveredEventArgs extends EventArgs { - /// The disvered peripheral. - final Peripheral peripheral; - - /// The rssi of the peripheral. - final int rssi; - - /// The advertisement of the peripheral. - final Advertisement advertisement; - - /// Constructs a [DiscoveredEventArgs]. - DiscoveredEventArgs(this.peripheral, this.rssi, this.advertisement); -} - -/// The peripheral state changed event arguments. -class PeripheralStateChangedEventArgs extends EventArgs { - /// The peripheral which state is changed. - final Peripheral peripheral; - - /// The new state of the peripheral. - final bool state; - - /// Constructs a [PeripheralStateChangedEventArgs]. - PeripheralStateChangedEventArgs(this.peripheral, this.state); -} - -/// The GATT characteristic value changed event arguments. -class GattCharacteristicValueChangedEventArgs extends EventArgs { - /// The GATT characteristic which value is changed. - final GattCharacteristic characteristic; - - /// The changed value of the characteristic. - final Uint8List value; - - /// Constructs a [GattCharacteristicValueChangedEventArgs]. - GattCharacteristicValueChangedEventArgs(this.characteristic, this.value); -} diff --git a/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic.dart b/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic.dart index 97a4f0a..be9ca64 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'gatt_attribute.dart'; import 'gatt_characteristic_property.dart'; import 'gatt_descriptor.dart'; @@ -17,11 +19,13 @@ abstract class GattCharacteristic extends GattAttribute { factory GattCharacteristic({ required UUID uuid, required List properties, + required Uint8List value, required List descriptors, }) => MyGattCharacteristic( uuid: uuid, properties: properties, + value: value, descriptors: descriptors.cast(), ); } diff --git a/bluetooth_low_energy_platform_interface/lib/src/my_bluetooth_low_energy_peer.dart b/bluetooth_low_energy_platform_interface/lib/src/my_bluetooth_low_energy_peer.dart new file mode 100644 index 0000000..0d29c99 --- /dev/null +++ b/bluetooth_low_energy_platform_interface/lib/src/my_bluetooth_low_energy_peer.dart @@ -0,0 +1,11 @@ +import 'bluetooth_low_energy_peer.dart'; +import 'uuid.dart'; + +abstract class MyBluetoothLowEnergyPeer implements BluetoothLowEnergyPeer { + @override + final UUID uuid; + + MyBluetoothLowEnergyPeer({ + required this.uuid, + }); +} diff --git a/bluetooth_low_energy_platform_interface/lib/src/my_central.dart b/bluetooth_low_energy_platform_interface/lib/src/my_central.dart index 348cea2..f27009b 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/my_central.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/my_central.dart @@ -1,13 +1,16 @@ import 'central.dart'; -import 'my_object.dart'; -import 'uuid.dart'; - -class MyCentral extends MyObject implements Central { - @override - final UUID uuid; +import 'my_bluetooth_low_energy_peer.dart'; +class MyCentral extends MyBluetoothLowEnergyPeer implements Central { MyCentral({ - required super.hashCode, - required this.uuid, + required super.uuid, }); + + @override + int get hashCode => uuid.hashCode; + + @override + bool operator ==(Object other) { + return other is Central && other.uuid == uuid; + } } diff --git a/bluetooth_low_energy_platform_interface/lib/src/my_central_manager.dart b/bluetooth_low_energy_platform_interface/lib/src/my_central_manager.dart deleted file mode 100644 index 256f840..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/my_central_manager.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:log_service/log_service.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'central_manager.dart'; - -/// Platform-specific implementations should implement this class to support -/// [CentralManager]. -abstract class MyCentralManager extends PlatformInterface - with LoggerController - implements CentralManager { - static final Object _token = Object(); - - static MyCentralManager? _instance; - - /// The default instance of [MyCentralManager] to use. - static MyCentralManager get instance { - final instance = _instance; - if (instance == null) { - throw UnimplementedError( - 'CentralManager is not implemented on this platform.', - ); - } - return instance; - } - - /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [MyCentralManager] when - /// they register themselves. - static set instance(MyCentralManager instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - /// Constructs a [MyCentralManager]. - MyCentralManager() : super(token: _token); - - @protected - @override - Logger get logger => Logger('$CentralManager'); -} diff --git a/bluetooth_low_energy_platform_interface/lib/src/my_gatt_attribute.dart b/bluetooth_low_energy_platform_interface/lib/src/my_gatt_attribute.dart new file mode 100644 index 0000000..7add4ff --- /dev/null +++ b/bluetooth_low_energy_platform_interface/lib/src/my_gatt_attribute.dart @@ -0,0 +1,19 @@ +import 'dart:typed_data'; + +import 'gatt_attribute.dart'; +import 'uuid.dart'; + +abstract class MyGattAttribute implements GattAttribute { + @override + final UUID uuid; + + MyGattAttribute({ + required this.uuid, + }); +} + +extension MyGattAttributeUint8List on Uint8List { + Uint8List trimGATT() { + return length > 512 ? sublist(0, 512) : this; + } +} diff --git a/bluetooth_low_energy_platform_interface/lib/src/my_gatt_characteristic.dart b/bluetooth_low_energy_platform_interface/lib/src/my_gatt_characteristic.dart index a5cea0a..082ab43 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/my_gatt_characteristic.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/my_gatt_characteristic.dart @@ -1,21 +1,27 @@ +import 'dart:typed_data'; + import 'gatt_characteristic.dart'; import 'gatt_characteristic_property.dart'; +import 'my_gatt_attribute.dart'; import 'my_gatt_descriptor.dart'; -import 'my_object.dart'; -import 'uuid.dart'; -class MyGattCharacteristic extends MyObject implements GattCharacteristic { - @override - final UUID uuid; +class MyGattCharacteristic extends MyGattAttribute + implements GattCharacteristic { @override final List properties; + Uint8List? _value; @override final List descriptors; MyGattCharacteristic({ - super.hashCode, - required this.uuid, + required super.uuid, required this.properties, + Uint8List? value, required this.descriptors, - }); + }) : _value = value?.trimGATT(); + + Uint8List get value => _value ?? Uint8List.fromList([]); + set value(Uint8List value) { + _value = value.trimGATT(); + } } diff --git a/bluetooth_low_energy_platform_interface/lib/src/my_gatt_descriptor.dart b/bluetooth_low_energy_platform_interface/lib/src/my_gatt_descriptor.dart index f421203..3ea7b3f 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/my_gatt_descriptor.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/my_gatt_descriptor.dart @@ -1,17 +1,18 @@ import 'dart:typed_data'; import 'gatt_descriptor.dart'; -import 'my_object.dart'; -import 'uuid.dart'; +import 'my_gatt_attribute.dart'; -class MyGattDescriptor extends MyObject implements GattDescriptor { - @override - final UUID uuid; - final Uint8List? value; +class MyGattDescriptor extends MyGattAttribute implements GattDescriptor { + Uint8List? _value; MyGattDescriptor({ - super.hashCode, - required this.uuid, - this.value, - }); + required super.uuid, + Uint8List? value, + }) : _value = value?.trimGATT(); + + Uint8List get value => _value ?? Uint8List.fromList([]); + set value(Uint8List value) { + _value = value.trimGATT(); + } } diff --git a/bluetooth_low_energy_platform_interface/lib/src/my_gatt_service.dart b/bluetooth_low_energy_platform_interface/lib/src/my_gatt_service.dart index 7ed4721..b1a21a0 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/my_gatt_service.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/my_gatt_service.dart @@ -1,17 +1,13 @@ import 'gatt_service.dart'; +import 'my_gatt_attribute.dart'; import 'my_gatt_characteristic.dart'; -import 'my_object.dart'; -import 'uuid.dart'; -class MyGattService extends MyObject implements GattService { - @override - final UUID uuid; +class MyGattService extends MyGattAttribute implements GattService { @override final List characteristics; MyGattService({ - super.hashCode, - required this.uuid, + required super.uuid, required this.characteristics, }); } diff --git a/bluetooth_low_energy_platform_interface/lib/src/my_object.dart b/bluetooth_low_energy_platform_interface/lib/src/my_object.dart deleted file mode 100644 index 084e1c1..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/my_object.dart +++ /dev/null @@ -1,13 +0,0 @@ -abstract class MyObject { - final int? _hashCode; - - MyObject({int? hashCode}) : _hashCode = hashCode; - - @override - int get hashCode => _hashCode ?? super.hashCode; - - @override - bool operator ==(Object other) { - return other is MyObject && other.hashCode == hashCode; - } -} diff --git a/bluetooth_low_energy_platform_interface/lib/src/my_peripheral.dart b/bluetooth_low_energy_platform_interface/lib/src/my_peripheral.dart index e4c4d36..47d9d13 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/my_peripheral.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/my_peripheral.dart @@ -1,13 +1,16 @@ -import 'my_object.dart'; +import 'my_bluetooth_low_energy_peer.dart'; import 'peripheral.dart'; -import 'uuid.dart'; - -class MyPeripheral extends MyObject implements Peripheral { - @override - final UUID uuid; +class MyPeripheral extends MyBluetoothLowEnergyPeer implements Peripheral { MyPeripheral({ - required super.hashCode, - required this.uuid, + required super.uuid, }); + + @override + int get hashCode => uuid.hashCode; + + @override + bool operator ==(Object other) { + return other is Peripheral && other.uuid == uuid; + } } diff --git a/bluetooth_low_energy_platform_interface/lib/src/my_peripheral_manager.dart b/bluetooth_low_energy_platform_interface/lib/src/my_peripheral_manager.dart deleted file mode 100644 index 489eea7..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/my_peripheral_manager.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:log_service/log_service.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'peripheral_manager.dart'; - -/// Platform-specific implementations should implement this class to support -/// [PeripheralManager]. -abstract class MyPeripheralManager extends PlatformInterface - with LoggerController - implements PeripheralManager { - static final Object _token = Object(); - - static MyPeripheralManager? _instance; - - /// The default instance of [MyPeripheralManager] to use. - static MyPeripheralManager get instance { - final instance = _instance; - if (instance == null) { - throw UnimplementedError( - 'PeripheralManager is not implemented on this platform.', - ); - } - return instance; - } - - /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [MyPeripheralManager] when - /// they register themselves. - static set instance(MyPeripheralManager instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - /// Constructs a [MyPeripheralManager]. - MyPeripheralManager() : super(token: _token); - - @protected - @override - Logger get logger => Logger('$PeripheralManager'); -} diff --git a/bluetooth_low_energy_platform_interface/lib/src/peripheral_event_args.dart b/bluetooth_low_energy_platform_interface/lib/src/peripheral_event_args.dart new file mode 100644 index 0000000..ddef1c7 --- /dev/null +++ b/bluetooth_low_energy_platform_interface/lib/src/peripheral_event_args.dart @@ -0,0 +1,61 @@ +import 'dart:typed_data'; + +import 'central.dart'; +import 'gatt_characteristic.dart'; + +/// The GATT characteristic written event arguments. +class GattCharacteristicReadEventArgs { + /// The central which read this characteristic. + final Central central; + + /// The GATT characteristic which value is read. + final GattCharacteristic characteristic; + + /// The value. + final Uint8List value; + + /// Constructs a [GattCharacteristicReadEventArgs]. + GattCharacteristicReadEventArgs( + this.central, + this.characteristic, + this.value, + ); +} + +/// The GATT characteristic written event arguments. +class GattCharacteristicWrittenEventArgs { + /// The central which wrote this characteristic. + final Central central; + + /// The GATT characteristic which value is written. + final GattCharacteristic characteristic; + + /// The value. + final Uint8List value; + + /// Constructs a [GattCharacteristicWrittenEventArgs]. + GattCharacteristicWrittenEventArgs( + this.central, + this.characteristic, + this.value, + ); +} + +/// The GATT characteristic notify state changed event arguments. +class GattCharacteristicNotifyStateChangedEventArgs { + /// The central which set this notify state. + final Central central; + + /// The GATT characteristic which notify state changed. + final GattCharacteristic characteristic; + + /// The notify state. + final bool state; + + /// Constructs a [GattCharacteristicNotifyStateChangedEventArgs]. + GattCharacteristicNotifyStateChangedEventArgs( + this.central, + this.characteristic, + this.state, + ); +} diff --git a/bluetooth_low_energy_platform_interface/lib/src/peripheral_manager.dart b/bluetooth_low_energy_platform_interface/lib/src/peripheral_manager.dart index 52e92d7..674e967 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/peripheral_manager.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/peripheral_manager.dart @@ -1,29 +1,55 @@ import 'dart:typed_data'; +import 'package:log_service/log_service.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + import 'advertisement.dart'; import 'bluetooth_low_energy_manager.dart'; import 'central.dart'; import 'gatt_characteristic.dart'; import 'gatt_service.dart'; -import 'my_peripheral_manager.dart'; -import 'peripheral_manager_event_args.dart'; +import 'peripheral_event_args.dart'; /// An object that manages and advertises peripheral services exposed by this app. -abstract class PeripheralManager extends BluetoothLowEnergyManager { - /// The instance of [PeripheralManger] to use. - static PeripheralManager get instance => MyPeripheralManager.instance; +/// +/// Platform-specific implementations should implement this class to support [PeripheralManager]. +abstract class PeripheralManager extends PlatformInterface + with LoggerProvider, LoggerController + implements BluetoothLowEnergyManager { + static final Object _token = Object(); - /// Tells that the local peripheral received an Attribute Protocol (ATT) read request for a characteristic with a dynamic value. - Stream - get readCharacteristicCommandReceived; + static PeripheralManager? _instance; + + /// The default instance of [PeripheralManager] to use. + static PeripheralManager get instance { + final instance = _instance; + if (instance == null) { + throw UnimplementedError( + 'PeripheralManager is not implemented on this platform.'); + } + return instance; + } + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [PeripheralManager] when + /// they register themselves. + static set instance(PeripheralManager instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + /// Constructs a [PeripheralManager]. + PeripheralManager() : super(token: _token); + + /// Tells that the local peripheral device received an Attribute Protocol (ATT) read request for a characteristic with a dynamic value. + Stream get characteristicRead; /// Tells that the local peripheral device received an Attribute Protocol (ATT) write request for a characteristic with a dynamic value. - Stream - get writeCharacteristicCommandReceived; + Stream get characteristicWritten; /// Tells that the peripheral manager received a characteristic’s notify changed. - Stream - get notifyCharacteristicCommandReceived; + Stream + get characteristicNotifyStateChanged; /// Publishes a service and any of its associated characteristics and characteristic descriptors to the local GATT database. Future addService(GattService service); @@ -40,33 +66,15 @@ abstract class PeripheralManager extends BluetoothLowEnergyManager { /// Stops advertising peripheral manager data. Future stopAdvertising(); - /// Gets the maximum amount of data, in bytes, that the central can receive in a - /// single notification or indication. - Future getMaximumWriteLength(Central central); + /// Retrieves the value of a specified characteristic. + Future readCharacteristic(GattCharacteristic characteristic); - /// Responds to a read request from a connected central. - Future sendReadCharacteristicReply( - Central central, { - required GattCharacteristic characteristic, - required int id, - required int offset, - required bool status, - required Uint8List value, - }); - - /// Responds to a write request from a connected central. - Future sendWriteCharacteristicReply( - Central central, { - required GattCharacteristic characteristic, - required int id, - required int offset, - required bool status, - }); - - /// Send an updated characteristic value to one or more subscribed centrals, using a notification or indication. - Future notifyCharacteristicValueChanged( - Central central, { - required GattCharacteristic characteristic, + /// Writes the value of a characteristic and sends an updated characteristic value to one or more subscribed centrals, using a notification or indication. + /// + /// The maximum size of the value is 512, all bytes that exceed this size will be discarded. + Future writeCharacteristic( + GattCharacteristic characteristic, { required Uint8List value, + Central? central, }); } diff --git a/bluetooth_low_energy_platform_interface/lib/src/peripheral_manager_event_args.dart b/bluetooth_low_energy_platform_interface/lib/src/peripheral_manager_event_args.dart deleted file mode 100644 index 2ecc096..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/peripheral_manager_event_args.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'dart:typed_data'; - -import 'central.dart'; -import 'gatt_characteristic.dart'; - -/// The read GATT characteristic command event arguments. -class ReadGattCharacteristicCommandEventArgs { - /// The central which send this read command. - final Central central; - - /// The GATT characteristic which value is to read. - final GattCharacteristic characteristic; - - /// The id of this read command. - final int id; - - /// The offset of this read command. - final int offset; - - /// Constructs a [ReadGattCharacteristicCommandEventArgs]. - ReadGattCharacteristicCommandEventArgs( - this.central, - this.characteristic, - this.id, - this.offset, - ); -} - -/// The write GATT characteristic command event arguments. -class WriteGattCharacteristicCommandEventArgs { - /// The central which send this write command. - final Central central; - - /// The GATT characteristic which value is to write. - final GattCharacteristic characteristic; - - /// The id of this write command. - final int id; - - /// The offset of this write command. - final int offset; - - /// The value of this write command. - final Uint8List value; - - /// Constructs a [WriteGattCharacteristicCommandEventArgs]. - WriteGattCharacteristicCommandEventArgs( - this.central, - this.characteristic, - this.id, - this.offset, - this.value, - ); -} - -/// The notify GATT characteristic command event arguments. -class NotifyGattCharacteristicCommandEventArgs { - /// The central which send this notify command. - final Central central; - - /// The GATT characteristic which value is to notify. - final GattCharacteristic characteristic; - - /// The state of this notify command. - final bool state; - - /// Constructs a [NotifyGattCharacteristicCommandEventArgs]. - NotifyGattCharacteristicCommandEventArgs( - this.central, - this.characteristic, - this.state, - ); -} diff --git a/bluetooth_low_energy_platform_interface/lib/src/uuid.dart b/bluetooth_low_energy_platform_interface/lib/src/uuid.dart index 5e93092..49ade9d 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/uuid.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/uuid.dart @@ -95,6 +95,16 @@ class UUID { ]); } + /// Creates a new UUID form MAC address. + factory UUID.fromAddress(String address) { + final node = address.splitMapJoin( + ':', + onMatch: (m) => '', + ); + // We don't know the timestamp of the bluetooth device, use nil UUID as prefix. + return UUID.fromString("00000000-0000-0000-0000-$node"); + } + @override String toString() { var v0 = value[0].toRadixString(16).padLeft(2, '0'); diff --git a/bluetooth_low_energy_platform_interface/pubspec.yaml b/bluetooth_low_energy_platform_interface/pubspec.yaml index 1dde87b..cdcc7aa 100644 --- a/bluetooth_low_energy_platform_interface/pubspec.yaml +++ b/bluetooth_low_energy_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: bluetooth_low_energy_platform_interface description: A common platform interface for the bluetooth_low_energy plugin. -version: 4.0.0 +version: 5.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy environment: @@ -10,7 +10,7 @@ environment: dependencies: flutter: sdk: flutter - plugin_platform_interface: ^2.0.2 + plugin_platform_interface: ^2.1.7 log_service: ^1.0.0 dev_dependencies: diff --git a/bluetooth_low_energy_platform_interface/test/my_gatt_attribute_test.dart b/bluetooth_low_energy_platform_interface/test/my_gatt_attribute_test.dart new file mode 100644 index 0000000..435d6f7 --- /dev/null +++ b/bluetooth_low_energy_platform_interface/test/my_gatt_attribute_test.dart @@ -0,0 +1,46 @@ +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test( + 'Trim when value is empty.', + () { + final value = Uint8List.fromList([]); + final actual = value.trimGATT(); + final matcher = value; + expect(actual, matcher); + }, + ); + test( + 'Trim when value is 100 bytes.', + () { + final elements = List.generate(100, (i) => i % 0xff); + final value = Uint8List.fromList(elements); + final actual = value.trimGATT(); + final matcher = value; + expect(actual, matcher); + }, + ); + test( + 'Trim when value is 512 bytes.', + () { + final elements = List.generate(512, (i) => i % 0xff); + final value = Uint8List.fromList(elements); + final actual = value.trimGATT(); + final matcher = value; + expect(actual, matcher); + }, + ); + test( + 'Trim when value is 1000 bytes.', + () { + final elements = List.generate(1000, (i) => i % 0xff); + final value = Uint8List.fromList(elements); + final actual = value.trimGATT(); + final matcher = Uint8List.fromList(elements.take(512).toList()); + expect(actual, matcher); + }, + ); +} diff --git a/bluetooth_low_energy_platform_interface/test/uuid_test.dart b/bluetooth_low_energy_platform_interface/test/uuid_test.dart new file mode 100644 index 0000000..2e1d356 --- /dev/null +++ b/bluetooth_low_energy_platform_interface/test/uuid_test.dart @@ -0,0 +1,23 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test( + 'Create UUID form MAC address with colons.', + () { + const address = 'AA:BB:CC:DD:EE:FF'; + final actual = UUID.fromAddress(address); + final matcher = UUID.fromString('00000000-0000-0000-0000-AABBCCDDEEFF'); + expect(actual, matcher); + }, + ); + test( + 'Create UUID form MAC address without colons.', + () { + const address = 'AABBCCDDEEFF'; + final actual = UUID.fromAddress(address); + final matcher = UUID.fromString('00000000-0000-0000-0000-AABBCCDDEEFF'); + expect(actual, matcher); + }, + ); +} diff --git a/bluetooth_low_energy_windows/.metadata b/bluetooth_low_energy_windows/.metadata index 48fd03f..f382618 100644 --- a/bluetooth_low_energy_windows/.metadata +++ b/bluetooth_low_energy_windows/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "efbf63d9c66b9f6ec30e9ad4611189aa80003d31" + revision: "d211f42860350d914a5ad8102f9ec32764dc6d06" channel: "stable" project_type: plugin @@ -13,11 +13,11 @@ project_type: plugin migration: platforms: - platform: root - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 + base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 - platform: windows - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 + base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 # User provided section diff --git a/bluetooth_low_energy_windows/CHANGELOG.md b/bluetooth_low_energy_windows/CHANGELOG.md index af53ab5..2531be2 100644 --- a/bluetooth_low_energy_windows/CHANGELOG.md +++ b/bluetooth_low_energy_windows/CHANGELOG.md @@ -1,3 +1,40 @@ +## 5.0.0 + +* Add implementation of `CentralManagerApi`. + +## 5.0.0-dev.7 + +* Optimize project structure. + +## 5.0.0-dev.6 + +* Remove the fragment logic form `CentralManager#writeCharacteristic`. + +## 5.0.0-dev.5 + +* Add implementation of `CentralManagerApi`. + +## 5.0.0-dev.4 + +* Add event logs. +* Update dependency. +* Optimize instances' retrieve speed. +* Optimize example. +* Fix the issue that `CentralManager#connect` returns fake connection. + +## 5.0.0-dev.3 + +* Update interface to 5.0.0-dev.4. + +## 5.0.0-dev.2 + +* Optimize CentralManager's cache. +* Remove AdvertiserView form example. + +## 5.0.0-dev.1 + +* Implement the `CentralManager` api. + ## 2.2.0 * Add `CentralController#getMaximumWriteLength` method. diff --git a/bluetooth_low_energy_windows/example/lib/main.dart b/bluetooth_low_energy_windows/example/lib/main.dart index e539942..30150dc 100644 --- a/bluetooth_low_energy_windows/example/lib/main.dart +++ b/bluetooth_low_energy_windows/example/lib/main.dart @@ -13,17 +13,27 @@ void main() { } void onStartUp() async { + Logger.root.onRecord.listen(onLogRecord); WidgetsFlutterBinding.ensureInitialized(); await CentralManager.instance.setUp(); - await PeripheralManager.instance.setUp(); + // await peripheralManager.setUp(); runApp(const MyApp()); } void onCrashed(Object error, StackTrace stackTrace) { + Logger.root.shout('App crached.', error, stackTrace); +} + +void onLogRecord(LogRecord record) { log( - '$error', - error: error, - stackTrace: stackTrace, + record.message, + time: record.time, + sequenceNumber: record.sequenceNumber, + level: record.level.value, + name: record.loggerName, + zone: record.zone, + error: record.error, + stackTrace: record.stackTrace, ); } @@ -45,6 +55,8 @@ class _MyAppState extends State { return MaterialApp( theme: ThemeData.light( useMaterial3: true, + ).copyWith( + materialTapTargetSize: MaterialTapTargetSize.padded, ), home: const HomeView(), routes: { @@ -60,86 +72,21 @@ class _MyAppState extends State { } } -class HomeView extends StatefulWidget { +class HomeView extends StatelessWidget { const HomeView({super.key}); - @override - State createState() => _HomeViewState(); -} - -class _HomeViewState extends State { - late final PageController controller; - - @override - void initState() { - super.initState(); - controller = PageController(); - } - @override Widget build(BuildContext context) { return Scaffold( body: buildBody(context), - bottomNavigationBar: buildBottomNavigationBar(context), ); } Widget buildBody(BuildContext context) { - return PageView.builder( - controller: controller, - itemBuilder: (context, i) { - switch (i) { - case 0: - return const ScannerView(); - case 1: - return const AdvertiserView(); - default: - throw ArgumentError.value(i); - } - }, - itemCount: 2, - ); - } - - Widget buildBottomNavigationBar(BuildContext context) { - return ListenableBuilder( - listenable: controller, - builder: (context, child) { - return BottomNavigationBar( - onTap: (i) { - const duration = Duration(milliseconds: 300); - const curve = Curves.ease; - controller.animateToPage( - i, - duration: duration, - curve: curve, - ); - }, - currentIndex: controller.page?.toInt() ?? 0, - items: const [ - BottomNavigationBarItem( - icon: Icon(Icons.radar), - label: 'scanner', - ), - BottomNavigationBarItem( - icon: Icon(Icons.sensors), - label: 'advertiser', - ), - ], - ); - }, - ); - } - - @override - void dispose() { - super.dispose(); - controller.dispose(); + return const ScannerView(); } } -CentralManager get centralManager => CentralManager.instance; - class ScannerView extends StatefulWidget { const ScannerView({super.key}); @@ -157,15 +104,15 @@ class _ScannerViewState extends State { @override void initState() { super.initState(); - state = ValueNotifier(centralManager.state); + state = ValueNotifier(BluetoothLowEnergyState.unknown); discovering = ValueNotifier(false); discoveredEventArgs = ValueNotifier([]); - stateChangedSubscription = centralManager.stateChanged.listen( + stateChangedSubscription = CentralManager.instance.stateChanged.listen( (eventArgs) { state.value = eventArgs.state; }, ); - discoveredSubscription = centralManager.discovered.listen( + discoveredSubscription = CentralManager.instance.discovered.listen( (eventArgs) { final items = discoveredEventArgs.value; final i = items.indexWhere( @@ -179,6 +126,11 @@ class _ScannerViewState extends State { } }, ); + setUp(); + } + + void setUp() async { + state.value = await CentralManager.instance.getState(); } @override @@ -222,12 +174,13 @@ class _ScannerViewState extends State { } Future startDiscovery() async { - await centralManager.startDiscovery(); + discoveredEventArgs.value = []; + await CentralManager.instance.startDiscovery(); discovering.value = true; } Future stopDiscovery() async { - await centralManager.stopDiscovery(); + await CentralManager.instance.stopDiscovery(); discovering.value = false; } @@ -237,7 +190,7 @@ class _ScannerViewState extends State { builder: (context, discoveredEventArgs, child) { // final items = discoveredEventArgs; final items = discoveredEventArgs - .where((eventArgs) => eventArgs.advertiseData.name != null) + .where((eventArgs) => eventArgs.advertisement.name != null) .toList(); return ListView.separated( itemBuilder: (context, i) { @@ -245,8 +198,8 @@ class _ScannerViewState extends State { final item = items[i]; final uuid = item.peripheral.uuid; final rssi = item.rssi; - final advertiseData = item.advertiseData; - final name = advertiseData.name; + final advertisement = item.advertisement; + final name = advertisement.name; return ListTile( onTap: () async { final discovering = this.discovering.value; @@ -273,7 +226,7 @@ class _ScannerViewState extends State { clipBehavior: Clip.antiAlias, builder: (context) { final manufacturerSpecificData = - advertiseData.manufacturerSpecificData; + advertisement.manufacturerSpecificData; return ListView.separated( padding: const EdgeInsets.symmetric( horizontal: 16.0, @@ -329,7 +282,13 @@ class _ScannerViewState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, ), - trailing: RssiWidget(rssi), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RssiWidget(rssi), + Text('$rssi'), + ], + ), ); }, separatorBuilder: (context, i) { @@ -367,44 +326,39 @@ class PeripheralView extends StatefulWidget { } class _PeripheralViewState extends State { - late final ValueNotifier state; + late final ValueNotifier connectionState; late final DiscoveredEventArgs eventArgs; late final ValueNotifier> services; late final ValueNotifier> characteristics; late final ValueNotifier service; late final ValueNotifier characteristic; late final ValueNotifier writeType; - late final ValueNotifier maximumWriteLength; - late final ValueNotifier rssi; late final ValueNotifier> logs; late final TextEditingController writeController; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription valueChangedSubscription; - late final StreamSubscription rssiChangedSubscription; - late final Timer rssiTimer; + late final StreamSubscription connectionStateChangedSubscription; + late final StreamSubscription characteristicNotifiedSubscription; @override void initState() { super.initState(); eventArgs = widget.eventArgs; - state = ValueNotifier(false); + connectionState = ValueNotifier(false); services = ValueNotifier([]); characteristics = ValueNotifier([]); service = ValueNotifier(null); characteristic = ValueNotifier(null); writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); - maximumWriteLength = ValueNotifier(0); - rssi = ValueNotifier(-100); logs = ValueNotifier([]); writeController = TextEditingController(); - stateChangedSubscription = centralManager.peripheralStateChanged.listen( + connectionStateChangedSubscription = + CentralManager.instance.connectionStateChanged.listen( (eventArgs) { if (eventArgs.peripheral != this.eventArgs.peripheral) { return; } - final state = eventArgs.state; - this.state.value = state; - if (!state) { + final connectionState = eventArgs.connectionState; + this.connectionState.value = connectionState; + if (!connectionState) { services.value = []; characteristics.value = []; service.value = null; @@ -413,12 +367,13 @@ class _PeripheralViewState extends State { } }, ); - valueChangedSubscription = centralManager.characteristicValueChanged.listen( + characteristicNotifiedSubscription = + CentralManager.instance.characteristicNotified.listen( (eventArgs) { - final characteristic = this.characteristic.value; - if (eventArgs.characteristic != characteristic) { - return; - } + // final characteristic = this.characteristic.value; + // if (eventArgs.characteristic != characteristic) { + // return; + // } const type = LogType.notify; final log = Log(type, eventArgs.value); logs.value = [ @@ -427,28 +382,16 @@ class _PeripheralViewState extends State { ]; }, ); - rssiTimer = Timer.periodic( - const Duration(seconds: 1), - (timer) async { - final state = this.state.value; - if (state) { - rssi.value = await centralManager.readRSSI(eventArgs.peripheral); - } else { - rssi.value = -100; - } - }, - ); } @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () async { - if (state.value) { + return PopScope( + onPopInvoked: (didPop) async { + if (connectionState.value) { final peripheral = eventArgs.peripheral; - await centralManager.disconnect(peripheral); + await CentralManager.instance.disconnect(peripheral); } - return true; }, child: Scaffold( appBar: buildAppBar(context), @@ -458,30 +401,22 @@ class _PeripheralViewState extends State { } PreferredSizeWidget buildAppBar(BuildContext context) { - final title = eventArgs.advertiseData.name ?? ''; + final title = eventArgs.advertisement.name ?? ''; return AppBar( title: Text(title), actions: [ ValueListenableBuilder( - valueListenable: state, + valueListenable: connectionState, builder: (context, state, child) { return TextButton( onPressed: () async { final peripheral = eventArgs.peripheral; if (state) { - await centralManager.disconnect(peripheral); - maximumWriteLength.value = 0; - rssi.value = 0; + await CentralManager.instance.disconnect(peripheral); } else { - await centralManager.connect(peripheral); + await CentralManager.instance.connect(peripheral); services.value = - await centralManager.discoverGATT(peripheral); - maximumWriteLength.value = - await centralManager.getMaximumWriteLength( - peripheral, - type: writeType.value, - ); - rssi.value = await centralManager.readRSSI(peripheral); + await CentralManager.instance.discoverGATT(peripheral); } }, child: Text(state ? 'DISCONNECT' : 'CONNECT'), @@ -555,7 +490,32 @@ class _PeripheralViewState extends State { hint: const Text('CHOOSE A CHARACTERISTIC'), value: characteristic, onChanged: (characteristic) { + if (characteristic == null) { + return; + } this.characteristic.value = characteristic; + final writeType = this.writeType.value; + final canWrite = characteristic.properties.contains( + GattCharacteristicProperty.write, + ); + final canWriteWithoutResponse = + characteristic.properties.contains( + GattCharacteristicProperty.writeWithoutResponse, + ); + if (writeType == + GattCharacteristicWriteType.withResponse && + !canWrite && + canWriteWithoutResponse) { + this.writeType.value = + GattCharacteristicWriteType.withoutResponse; + } + if (writeType == + GattCharacteristicWriteType.withoutResponse && + !canWriteWithoutResponse && + canWrite) { + this.writeType.value = + GattCharacteristicWriteType.withResponse; + } }, ); }, @@ -613,129 +573,45 @@ class _PeripheralViewState extends State { }, ), ), - Row( - children: [ - ValueListenableBuilder( - valueListenable: writeType, - builder: (context, writeType, child) { - return ToggleButtons( - onPressed: (i) async { - final type = GattCharacteristicWriteType.values[i]; - this.writeType.value = type; - maximumWriteLength.value = - await centralManager.getMaximumWriteLength( - eventArgs.peripheral, - type: type, - ); - }, - constraints: const BoxConstraints( - minWidth: 0.0, - minHeight: 0.0, - ), - borderRadius: BorderRadius.circular(4.0), - isSelected: GattCharacteristicWriteType.values - .map((type) => type == writeType) - .toList(), - children: GattCharacteristicWriteType.values.map((type) { - return Container( - margin: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 4.0, - ), - child: Text(type.name), - ); - }).toList(), - ); - // final segments = - // GattCharacteristicWriteType.values.map((type) { - // return ButtonSegment( - // value: type, - // label: Text(type.name), - // ); - // }).toList(); - // return SegmentedButton( - // segments: segments, - // selected: {writeType}, - // showSelectedIcon: false, - // style: OutlinedButton.styleFrom( - // tapTargetSize: MaterialTapTargetSize.shrinkWrap, - // padding: EdgeInsets.zero, - // visualDensity: VisualDensity.compact, - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(8.0), - // ), - // ), - // ); - }, - ), - const SizedBox(width: 8.0), - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: maximumWriteLength, - builder: (context, maximumWriteLength, child) { - return Text('$maximumWriteLength'); - }, - ); - }, - ), - const Spacer(), - ValueListenableBuilder( - valueListenable: rssi, - builder: (context, rssi, child) { - return RssiWidget(rssi); - }, - ), - ], - ), - Container( - margin: const EdgeInsets.only(bottom: 16.0), - height: 160.0, - child: ValueListenableBuilder( - valueListenable: characteristic, - builder: (context, characteristic, child) { - final bool canNotify, canRead, canWrite; - if (characteristic == null) { - canNotify = canRead = canWrite = false; - } else { - final properties = characteristic.properties; - canNotify = properties.contains( - GattCharacteristicProperty.notify, - ); - canRead = properties.contains( - GattCharacteristicProperty.read, - ); - canWrite = properties.contains( - GattCharacteristicProperty.write, - ); - } - return Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded( - child: TextField( - controller: writeController, - enabled: canWrite, - expands: true, - maxLines: null, - textAlignVertical: TextAlignVertical.top, - decoration: const InputDecoration( - border: OutlineInputBorder(), - contentPadding: EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 8.0, - ), - ), - ), - ), - Column( - mainAxisSize: MainAxisSize.min, + ValueListenableBuilder( + valueListenable: characteristic, + builder: (context, characteristic, chld) { + final bool canNotify, canRead, canWrite, canWriteWithoutResponse; + if (characteristic == null) { + canNotify = + canRead = canWrite = canWriteWithoutResponse = false; + } else { + final properties = characteristic.properties; + canNotify = properties.contains( + GattCharacteristicProperty.notify, + ) || + properties.contains( + GattCharacteristicProperty.indicate, + ); + canRead = properties.contains( + GattCharacteristicProperty.read, + ); + canWrite = properties.contains( + GattCharacteristicProperty.write, + ); + canWriteWithoutResponse = properties.contains( + GattCharacteristicProperty.writeWithoutResponse, + ); + } + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + margin: const EdgeInsets.symmetric(vertical: 4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ - TextButton( + ElevatedButton( onPressed: characteristic != null && canNotify ? () async { - await centralManager.notifyCharacteristic( + await CentralManager.instance + .setCharacteristicNotifyState( characteristic, state: true, ); @@ -743,10 +619,11 @@ class _PeripheralViewState extends State { : null, child: const Text('NOTIFY'), ), - TextButton( + const SizedBox(width: 8.0), + ElevatedButton( onPressed: characteristic != null && canRead ? () async { - final value = await centralManager + final value = await CentralManager.instance .readCharacteristic(characteristic); const type = LogType.read; final log = Log(type, value); @@ -754,31 +631,124 @@ class _PeripheralViewState extends State { } : null, child: const Text('READ'), - ), - TextButton( - onPressed: characteristic != null && canWrite - ? () async { - final text = writeController.text; - final elements = utf8.encode(text); - final value = Uint8List.fromList(elements); - final type = writeType.value; - await centralManager.writeCharacteristic( - characteristic, - value: value, - type: type, - ); - final log = Log(LogType.write, value); - logs.value = [...logs.value, log]; - } - : null, - child: const Text('WRITE'), - ), + ) ], ), - ], - ); - }, - ), + ), + SizedBox( + height: 160.0, + child: TextField( + controller: writeController, + enabled: canWrite || canWriteWithoutResponse, + expands: true, + maxLines: null, + textAlignVertical: TextAlignVertical.top, + decoration: const InputDecoration( + border: OutlineInputBorder(), + contentPadding: EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 8.0, + ), + ), + ), + ), + Row( + children: [ + ValueListenableBuilder( + valueListenable: writeType, + builder: (context, writeType, child) { + return ToggleButtons( + onPressed: canWrite || canWriteWithoutResponse + ? (i) { + if (!canWrite || !canWriteWithoutResponse) { + return; + } + final type = + GattCharacteristicWriteType.values[i]; + this.writeType.value = type; + } + : null, + constraints: const BoxConstraints( + minWidth: 0.0, + minHeight: 0.0, + ), + borderRadius: BorderRadius.circular(4.0), + isSelected: GattCharacteristicWriteType.values + .map((type) => type == writeType) + .toList(), + children: GattCharacteristicWriteType.values.map( + (type) { + return Container( + margin: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, + ), + child: Text(type.name), + ); + }, + ).toList(), + ); + // final segments = + // GattCharacteristicWriteType.values.map((type) { + // return ButtonSegment( + // value: type, + // label: Text(type.name), + // ); + // }).toList(); + // return SegmentedButton( + // segments: segments, + // selected: {writeType}, + // showSelectedIcon: false, + // style: OutlinedButton.styleFrom( + // tapTargetSize: MaterialTapTargetSize.shrinkWrap, + // padding: EdgeInsets.zero, + // visualDensity: VisualDensity.compact, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(8.0), + // ), + // ), + // ); + }, + ), + const Spacer(), + ElevatedButton( + onPressed: characteristic != null && canWrite + ? () async { + final text = writeController.text; + final elements = utf8.encode(text); + final value = Uint8List.fromList(elements); + final type = writeType.value; + // Fragments the value by 512 bytes. + const fragmentSize = 512; + var start = 0; + while (start < value.length) { + final end = start + fragmentSize; + final fragmentedValue = end < value.length + ? value.sublist(start, end) + : value.sublist(start); + await CentralManager.instance + .writeCharacteristic( + characteristic, + value: fragmentedValue, + type: type, + ); + final log = Log( + LogType.write, + fragmentedValue, + ); + logs.value = [...logs.value, log]; + start = end; + } + } + : null, + child: const Text('WRITE'), + ), + ], + ), + const SizedBox(height: 16.0), + ], + ); + }, ), ], ), @@ -788,291 +758,19 @@ class _PeripheralViewState extends State { @override void dispose() { super.dispose(); - rssiTimer.cancel(); - stateChangedSubscription.cancel(); - valueChangedSubscription.cancel(); - state.dispose(); + connectionStateChangedSubscription.cancel(); + characteristicNotifiedSubscription.cancel(); + connectionState.dispose(); services.dispose(); characteristics.dispose(); service.dispose(); characteristic.dispose(); writeType.dispose(); - maximumWriteLength.dispose(); - rssi.dispose(); logs.dispose(); writeController.dispose(); } } -PeripheralManager get peripheralManager => PeripheralManager.instance; - -class AdvertiserView extends StatefulWidget { - const AdvertiserView({super.key}); - - @override - State createState() => _AdvertiserViewState(); -} - -class _AdvertiserViewState extends State { - late final ValueNotifier state; - late final ValueNotifier advertising; - late final ValueNotifier> logs; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription readCharacteristicCommandReceivedSubscription; - late final StreamSubscription writeCharacteristicCommandReceivedSubscription; - late final StreamSubscription notifyCharacteristicCommandReceivedSubscription; - - @override - void initState() { - super.initState(); - state = ValueNotifier(peripheralManager.state); - advertising = ValueNotifier(false); - logs = ValueNotifier([]); - stateChangedSubscription = peripheralManager.stateChanged.listen( - (eventArgs) { - state.value = eventArgs.state; - }, - ); - readCharacteristicCommandReceivedSubscription = - peripheralManager.readCharacteristicCommandReceived.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final id = eventArgs.id; - final offset = eventArgs.offset; - final log = Log( - LogType.read, - Uint8List.fromList([]), - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', - ); - logs.value = [ - ...logs.value, - log, - ]; - // final maximumWriteLength = peripheralManager.getMaximumWriteLength( - // central, - // ); - const status = true; - final value = Uint8List.fromList([0x01, 0x02, 0x03]); - await peripheralManager.sendReadCharacteristicReply( - central, - characteristic, - id, - offset, - status, - value, - ); - }, - ); - writeCharacteristicCommandReceivedSubscription = - peripheralManager.writeCharacteristicCommandReceived.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final id = eventArgs.id; - final offset = eventArgs.offset; - final value = eventArgs.value; - final log = Log( - LogType.write, - value, - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', - ); - logs.value = [ - ...logs.value, - log, - ]; - const status = true; - await peripheralManager.sendWriteCharacteristicReply( - central, - characteristic, - id, - offset, - status, - ); - }, - ); - notifyCharacteristicCommandReceivedSubscription = - peripheralManager.notifyCharacteristicCommandReceived.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final state = eventArgs.state; - final log = Log( - LogType.write, - Uint8List.fromList([]), - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state', - ); - logs.value = [ - ...logs.value, - log, - ]; - // Write someting to the central when notify started. - if (state) { - final value = Uint8List.fromList([0x03, 0x02, 0x01]); - await peripheralManager.notifyCharacteristicValueChanged( - central, - characteristic, - value, - ); - } - }, - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), - ); - } - - PreferredSizeWidget buildAppBar(BuildContext context) { - return AppBar( - title: const Text('Advertiser'), - actions: [ - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: advertising, - builder: (context, advertising, child) { - return TextButton( - onPressed: state == BluetoothLowEnergyState.poweredOn - ? () async { - if (advertising) { - await stopAdvertising(); - } else { - await startAdvertising(); - } - } - : null, - child: Text( - advertising ? 'END' : 'BEGIN', - ), - ); - }, - ); - }, - ), - ], - ); - } - - Future startAdvertising() async { - await peripheralManager.clearServices(); - final service = GattService( - uuid: UUID.short(100), - characteristics: [ - GattCharacteristic( - uuid: UUID.short(200), - properties: [ - GattCharacteristicProperty.read, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(201), - properties: [ - GattCharacteristicProperty.read, - GattCharacteristicProperty.write, - GattCharacteristicProperty.writeWithoutResponse, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(202), - properties: [ - GattCharacteristicProperty.notify, - GattCharacteristicProperty.indicate, - ], - descriptors: [], - ), - ], - ); - await peripheralManager.addService(service); - final advertiseData = AdvertiseData( - name: 'flutter', - manufacturerSpecificData: ManufacturerSpecificData( - id: 0x2e19, - data: Uint8List.fromList([0x01, 0x02, 0x03]), - ), - ); - await peripheralManager.startAdvertising(advertiseData); - advertising.value = true; - } - - Future stopAdvertising() async { - await peripheralManager.stopAdvertising(); - advertising.value = false; - } - - Widget buildBody(BuildContext context) { - final theme = Theme.of(context); - return ValueListenableBuilder( - valueListenable: logs, - builder: (context, logs, child) { - return ListView.builder( - itemBuilder: (context, i) { - final log = logs[i]; - final type = log.type.name.toUpperCase().characters.first; - final Color typeColor; - switch (log.type) { - case LogType.read: - typeColor = Colors.blue; - break; - case LogType.write: - typeColor = Colors.amber; - break; - case LogType.notify: - typeColor = Colors.red; - break; - default: - typeColor = Colors.black; - } - final time = DateFormat.Hms().format(log.time); - final value = log.value; - final message = '${log.detail}; ${hex.encode(value)}'; - return Text.rich( - TextSpan( - text: '[$type:${value.length}]', - children: [ - TextSpan( - text: ' $time: ', - style: theme.textTheme.bodyMedium?.copyWith( - color: Colors.green, - ), - ), - TextSpan( - text: message, - style: theme.textTheme.bodyMedium, - ), - ], - style: theme.textTheme.bodyMedium?.copyWith( - color: typeColor, - ), - ), - ); - }, - itemCount: logs.length, - ); - }, - ); - } - - @override - void dispose() { - super.dispose(); - stateChangedSubscription.cancel(); - readCharacteristicCommandReceivedSubscription.cancel(); - writeCharacteristicCommandReceivedSubscription.cancel(); - notifyCharacteristicCommandReceivedSubscription.cancel(); - state.dispose(); - advertising.dispose(); - logs.dispose(); - } -} - class Log { final DateTime time; final LogType type; diff --git a/bluetooth_low_energy_windows/example/pubspec.lock b/bluetooth_low_energy_windows/example/pubspec.lock index 041b9d5..c05931e 100644 --- a/bluetooth_low_energy_windows/example/pubspec.lock +++ b/bluetooth_low_energy_windows/example/pubspec.lock @@ -13,17 +13,17 @@ packages: dependency: "direct main" description: name: bluetooth_low_energy_platform_interface - sha256: "200e686247808591b6d3e355642ba296f0f651466c72efdd701be34116971473" + sha256: "54f92ab2d7746fb6f2b4a40a48cd7eb8e3bf772f3ee89e1979d4d7b741fb2a05" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "5.0.0" bluetooth_low_energy_windows: dependency: "direct main" description: path: ".." relative: true source: path - version: "3.0.0" + version: "5.0.0" boolean_selector: dependency: transitive description: @@ -52,10 +52,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: "direct main" description: @@ -80,14 +80,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" - url: "https://pub.dev" - source: hosted - version: "2.1.0" file: dependency: transitive description: @@ -110,10 +102,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -133,18 +125,34 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" + log_service: + dependency: transitive + description: + name: log_service + sha256: "21124936899e227d1779268077921d46d57456e2592d1562e455be273594e2e4" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: @@ -165,10 +173,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -181,18 +189,18 @@ packages: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" process: dependency: transitive description: @@ -218,18 +226,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -258,10 +266,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" typed_data: dependency: transitive description: @@ -282,18 +290,18 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 url: "https://pub.dev" source: hosted - version: "11.7.1" + version: "11.10.0" web: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" webdriver: dependency: transitive description: @@ -302,14 +310,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" - win32: - dependency: transitive - description: - name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" - url: "https://pub.dev" - source: hosted - version: "5.0.9" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.3.0" diff --git a/bluetooth_low_energy_windows/example/pubspec.yaml b/bluetooth_low_energy_windows/example/pubspec.yaml index 882d276..880a455 100644 --- a/bluetooth_low_energy_windows/example/pubspec.yaml +++ b/bluetooth_low_energy_windows/example/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^3.0.0 + bluetooth_low_energy_platform_interface: ^5.0.0 bluetooth_low_energy_windows: # When depending on this package from a real application you should use: # bluetooth_low_energy: ^x.y.z @@ -30,7 +30,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 convert: ^3.1.1 - intl: ^0.18.1 + intl: ^0.19.0 dev_dependencies: integration_test: @@ -43,7 +43,7 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^2.0.0 + flutter_lints: ^3.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/bluetooth_low_energy_windows/example/windows/flutter/CMakeLists.txt b/bluetooth_low_energy_windows/example/windows/flutter/CMakeLists.txt index 930d207..903f489 100644 --- a/bluetooth_low_energy_windows/example/windows/flutter/CMakeLists.txt +++ b/bluetooth_low_energy_windows/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/bluetooth_low_energy_windows/example/windows/flutter/generated_plugin_registrant.cc b/bluetooth_low_energy_windows/example/windows/flutter/generated_plugin_registrant.cc index 8b6d468..d051da9 100644 --- a/bluetooth_low_energy_windows/example/windows/flutter/generated_plugin_registrant.cc +++ b/bluetooth_low_energy_windows/example/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,9 @@ #include "generated_plugin_registrant.h" +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + BluetoothLowEnergyWindowsCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("BluetoothLowEnergyWindowsCApi")); } diff --git a/bluetooth_low_energy_windows/example/windows/flutter/generated_plugins.cmake b/bluetooth_low_energy_windows/example/windows/flutter/generated_plugins.cmake index b93c4c3..4b19aa9 100644 --- a/bluetooth_low_energy_windows/example/windows/flutter/generated_plugins.cmake +++ b/bluetooth_low_energy_windows/example/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + bluetooth_low_energy_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/bluetooth_low_energy_windows/lib/bluetooth_low_energy_windows.dart b/bluetooth_low_energy_windows/lib/bluetooth_low_energy_windows.dart index 73567ef..9a987e1 100644 --- a/bluetooth_low_energy_windows/lib/bluetooth_low_energy_windows.dart +++ b/bluetooth_low_energy_windows/lib/bluetooth_low_energy_windows.dart @@ -1,9 +1,9 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'src/my_bluetooth_low_energy.dart'; +import 'src/my_central_manager.dart'; abstract class BluetoothLowEnergyWindows { static void registerWith() { - BluetoothLowEnergy.instance = MyBluetoothLowEnergy(); + CentralManager.instance = MyCentralManager(); } } diff --git a/bluetooth_low_energy_windows/lib/src/my_api.dart b/bluetooth_low_energy_windows/lib/src/my_api.dart new file mode 100644 index 0000000..1c13485 --- /dev/null +++ b/bluetooth_low_energy_windows/lib/src/my_api.dart @@ -0,0 +1,254 @@ +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +import 'my_api.g.dart'; +import 'my_gatt_characteristic2.dart'; +import 'my_gatt_descriptor2.dart'; +import 'my_gatt_service2.dart'; + +export 'my_api.g.dart'; + +// ToObject +extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { + BluetoothLowEnergyState toState() { + switch (this) { + case MyBluetoothLowEnergyStateArgs.unknown: + return BluetoothLowEnergyState.unknown; + case MyBluetoothLowEnergyStateArgs.disabled: + return BluetoothLowEnergyState.unsupported; + case MyBluetoothLowEnergyStateArgs.off: + return BluetoothLowEnergyState.poweredOff; + case MyBluetoothLowEnergyStateArgs.on: + return BluetoothLowEnergyState.poweredOn; + } + } +} + +extension MyGattCharacteristicPropertyArgsX + on MyGattCharacteristicPropertyArgs { + GattCharacteristicProperty toProperty() { + return GattCharacteristicProperty.values[index]; + } +} + +extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs { + ManufacturerSpecificData toManufacturerSpecificData() { + final id = idArgs; + final data = dataArgs; + return ManufacturerSpecificData( + id: id, + data: data, + ); + } +} + +extension MyAdvertisementArgsX on MyAdvertisementArgs { + Advertisement toAdvertisement() { + final name = nameArgs; + final serviceUUIDs = + serviceUUIDsArgs.cast().map((args) => args.toUUID()).toList(); + final serviceData = serviceDataArgs.cast().map( + (uuidArgs, dataArgs) { + final uuid = uuidArgs.toUUID(); + final data = dataArgs; + return MapEntry(uuid, data); + }, + ); + final manufacturerSpecificData = + manufacturerSpecificDataArgs?.toManufacturerSpecificData(); + return Advertisement( + name: name, + serviceUUIDs: serviceUUIDs, + serviceData: serviceData, + manufacturerSpecificData: manufacturerSpecificData, + ); + } +} + +extension MyCentralArgsX on MyCentralArgs { + MyCentral toCentral() { + final uuid = addressArgs.toUUID(); + return MyCentral( + uuid: uuid, + ); + } +} + +extension MyPeripheralArgsX on MyPeripheralArgs { + MyPeripheral toPeripheral() { + final uuid = addressArgs.toUUID(); + return MyPeripheral( + uuid: uuid, + ); + } +} + +extension MyGattDescriptorArgsX on MyGattDescriptorArgs { + MyGattDescriptor2 toDescriptor2(MyPeripheral peripheral) { + final handle = handleArgs; + final uuid = uuidArgs.toUUID(); + return MyGattDescriptor2( + peripheral: peripheral, + handle: handle, + uuid: uuid, + ); + } +} + +extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs { + MyGattCharacteristic2 toCharacteristic2(MyPeripheral peripheral) { + final handle = handleArgs; + final uuid = uuidArgs.toUUID(); + final properties = propertyNumbersArgs.cast().map( + (args) { + final propertyArgs = MyGattCharacteristicPropertyArgs.values[args]; + return propertyArgs.toProperty(); + }, + ).toList(); + final descriptors = descriptorsArgs + .cast() + .map((args) => args.toDescriptor2(peripheral)) + .toList(); + return MyGattCharacteristic2( + peripheral: peripheral, + handle: handle, + uuid: uuid, + properties: properties, + descriptors: descriptors, + ); + } +} + +extension MyGattServiceArgsX on MyGattServiceArgs { + MyGattService2 toService2(MyPeripheral peripheral) { + final handle = handleArgs; + final uuid = uuidArgs.toUUID(); + final characteristics = characteristicsArgs + .cast() + .map((args) => args.toCharacteristic2(peripheral)) + .toList(); + return MyGattService2( + peripheral: peripheral, + handle: handle, + uuid: uuid, + characteristics: characteristics, + ); + } +} + +extension MyAddressArgsX on int { + UUID toUUID() { + final node = (this & 0xFFFFFFFFFFFF).toRadixString(16).padLeft(12, '0'); + return UUID.fromString('00000000-0000-0000-0000-$node'); + } +} + +extension MyUuidArgsX on String { + UUID toUUID() { + return UUID.fromString(this); + } +} + +// ToArgs +extension GattCharacteristicPropertyX on GattCharacteristicProperty { + MyGattCharacteristicPropertyArgs toArgs() { + return MyGattCharacteristicPropertyArgs.values[index]; + } +} + +extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType { + MyGattCharacteristicWriteTypeArgs toArgs() { + return MyGattCharacteristicWriteTypeArgs.values[index]; + } +} + +extension ManufacturerSpecificDataX on ManufacturerSpecificData { + MyManufacturerSpecificDataArgs toArgs() { + final idArgs = id; + final dataArgs = data; + return MyManufacturerSpecificDataArgs( + idArgs: idArgs, + dataArgs: dataArgs, + ); + } +} + +extension AdvertisementX on Advertisement { + MyAdvertisementArgs toArgs() { + final nameArgs = name; + final serviceUUIDsArgs = serviceUUIDs.map((uuid) => uuid.toArgs()).toList(); + final serviceDataArgs = serviceData.map((uuid, data) { + final uuidArgs = uuid.toArgs(); + final dataArgs = data; + return MapEntry(uuidArgs, dataArgs); + }); + final manufacturerSpecificDataArgs = manufacturerSpecificData?.toArgs(); + return MyAdvertisementArgs( + nameArgs: nameArgs, + serviceUUIDsArgs: serviceUUIDsArgs, + serviceDataArgs: serviceDataArgs, + manufacturerSpecificDataArgs: manufacturerSpecificDataArgs, + ); + } +} + +extension MyGattDescriptor2X on MyGattDescriptor2 { + MyGattDescriptorArgs toArgs() { + final handleArgs = handle; + final uuidArgs = uuid.toArgs(); + final valueArgs = value; + return MyGattDescriptorArgs( + handleArgs: handleArgs, + uuidArgs: uuidArgs, + valueArgs: valueArgs, + ); + } +} + +extension MyGattCharacteristic2X on MyGattCharacteristic2 { + MyGattCharacteristicArgs toArgs() { + final handleArgs = handle; + final uuidArgs = uuid.toArgs(); + final propertyNumbersArgs = + properties.map((property) => property.toArgs().index).toList(); + final descriptorsArgs = + descriptors.map((descriptor) => descriptor.toArgs()).toList(); + return MyGattCharacteristicArgs( + handleArgs: handleArgs, + uuidArgs: uuidArgs, + propertyNumbersArgs: propertyNumbersArgs, + descriptorsArgs: descriptorsArgs, + ); + } +} + +extension MyGattService2X on MyGattService2 { + MyGattServiceArgs toArgs() { + final handleArgs = handle; + final uuidArgs = uuid.toArgs(); + final characteristicsArgs = characteristics + .map((characteristic) => characteristic.toArgs()) + .toList(); + return MyGattServiceArgs( + handleArgs: handleArgs, + uuidArgs: uuidArgs, + characteristicsArgs: characteristicsArgs, + ); + } +} + +extension UuidX on UUID { + String toArgs() { + return toString(); + } + + int toAddressArgs() { + final node = toString().split('-').last; + final address = int.parse( + node, + radix: 16, + ); + return address; + } +} diff --git a/bluetooth_low_energy_windows/lib/src/my_api.g.dart b/bluetooth_low_energy_windows/lib/src/my_api.g.dart new file mode 100644 index 0000000..3b26fa6 --- /dev/null +++ b/bluetooth_low_energy_windows/lib/src/my_api.g.dart @@ -0,0 +1,1179 @@ +// Autogenerated from Pigeon (v15.0.2), 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 + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + +enum MyBluetoothLowEnergyStateArgs { + unknown, + disabled, + off, + on, +} + +enum MyGattCharacteristicPropertyArgs { + read, + write, + writeWithoutResponse, + notify, + indicate, +} + +enum MyGattCharacteristicWriteTypeArgs { + withResponse, + withoutResponse, +} + +enum MyGattCharacteristicNotifyStateArgs { + none, + notify, + indicate, +} + +class MyManufacturerSpecificDataArgs { + MyManufacturerSpecificDataArgs({ + required this.idArgs, + required this.dataArgs, + }); + + int idArgs; + + Uint8List dataArgs; + + Object encode() { + return [ + idArgs, + dataArgs, + ]; + } + + static MyManufacturerSpecificDataArgs decode(Object result) { + result as List; + return MyManufacturerSpecificDataArgs( + idArgs: result[0]! as int, + dataArgs: result[1]! as Uint8List, + ); + } +} + +class MyAdvertisementArgs { + MyAdvertisementArgs({ + this.nameArgs, + required this.serviceUUIDsArgs, + required this.serviceDataArgs, + this.manufacturerSpecificDataArgs, + }); + + String? nameArgs; + + List serviceUUIDsArgs; + + Map serviceDataArgs; + + MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs; + + Object encode() { + return [ + nameArgs, + serviceUUIDsArgs, + serviceDataArgs, + manufacturerSpecificDataArgs?.encode(), + ]; + } + + static MyAdvertisementArgs decode(Object result) { + result as List; + return MyAdvertisementArgs( + nameArgs: result[0] as String?, + serviceUUIDsArgs: (result[1] as List?)!.cast(), + serviceDataArgs: (result[2] as Map?)!.cast(), + manufacturerSpecificDataArgs: result[3] != null + ? MyManufacturerSpecificDataArgs.decode(result[3]! as List) + : null, + ); + } +} + +class MyCentralArgs { + MyCentralArgs({ + required this.addressArgs, + }); + + int addressArgs; + + Object encode() { + return [ + addressArgs, + ]; + } + + static MyCentralArgs decode(Object result) { + result as List; + return MyCentralArgs( + addressArgs: result[0]! as int, + ); + } +} + +class MyPeripheralArgs { + MyPeripheralArgs({ + required this.addressArgs, + }); + + int addressArgs; + + Object encode() { + return [ + addressArgs, + ]; + } + + static MyPeripheralArgs decode(Object result) { + result as List; + return MyPeripheralArgs( + addressArgs: result[0]! as int, + ); + } +} + +class MyGattDescriptorArgs { + MyGattDescriptorArgs({ + required this.handleArgs, + required this.uuidArgs, + this.valueArgs, + }); + + int handleArgs; + + String uuidArgs; + + Uint8List? valueArgs; + + Object encode() { + return [ + handleArgs, + uuidArgs, + valueArgs, + ]; + } + + static MyGattDescriptorArgs decode(Object result) { + result as List; + return MyGattDescriptorArgs( + handleArgs: result[0]! as int, + uuidArgs: result[1]! as String, + valueArgs: result[2] as Uint8List?, + ); + } +} + +class MyGattCharacteristicArgs { + MyGattCharacteristicArgs({ + required this.handleArgs, + required this.uuidArgs, + required this.propertyNumbersArgs, + required this.descriptorsArgs, + }); + + int handleArgs; + + String uuidArgs; + + List propertyNumbersArgs; + + List descriptorsArgs; + + Object encode() { + return [ + handleArgs, + uuidArgs, + propertyNumbersArgs, + descriptorsArgs, + ]; + } + + static MyGattCharacteristicArgs decode(Object result) { + result as List; + return MyGattCharacteristicArgs( + handleArgs: result[0]! as int, + uuidArgs: result[1]! as String, + propertyNumbersArgs: (result[2] as List?)!.cast(), + descriptorsArgs: (result[3] as List?)!.cast(), + ); + } +} + +class MyGattServiceArgs { + MyGattServiceArgs({ + required this.handleArgs, + required this.uuidArgs, + required this.characteristicsArgs, + }); + + int handleArgs; + + String uuidArgs; + + List characteristicsArgs; + + Object encode() { + return [ + handleArgs, + uuidArgs, + characteristicsArgs, + ]; + } + + static MyGattServiceArgs decode(Object result) { + result as List; + return MyGattServiceArgs( + handleArgs: result[0]! as int, + uuidArgs: result[1]! as String, + characteristicsArgs: (result[2] as List?)!.cast(), + ); + } +} + +class _MyCentralManagerHostApiCodec extends StandardMessageCodec { + const _MyCentralManagerHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is MyGattCharacteristicArgs) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is MyGattDescriptorArgs) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MyGattServiceArgs) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return MyGattCharacteristicArgs.decode(readValue(buffer)!); + case 129: + return MyGattDescriptorArgs.decode(readValue(buffer)!); + case 130: + return MyGattServiceArgs.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class MyCentralManagerHostApi { + /// Constructor for [MyCentralManagerHostApi]. 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. + MyCentralManagerHostApi({BinaryMessenger? binaryMessenger}) + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _MyCentralManagerHostApiCodec(); + + Future setUp() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.setUp'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future startDiscovery() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.startDiscovery'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future stopDiscovery() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.stopDiscovery'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future connect(int addressArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.connect'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future disconnect(int addressArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.disconnect'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future> discoverServices(int addressArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverServices'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + 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 (__pigeon_replyList[0] as List?)!.cast(); + } + } + + Future> discoverCharacteristics(int addressArgs, int handleArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverCharacteristics'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + 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 (__pigeon_replyList[0] as List?)!.cast(); + } + } + + Future> discoverDescriptors(int addressArgs, int handleArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverDescriptors'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + 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 (__pigeon_replyList[0] as List?)!.cast(); + } + } + + Future readCharacteristic(int addressArgs, int handleArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.readCharacteristic'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + 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 (__pigeon_replyList[0] as Uint8List?)!; + } + } + + Future writeCharacteristic(int addressArgs, int handleArgs, Uint8List valueArgs, int typeNumberArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.writeCharacteristic'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs, valueArgs, typeNumberArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future setCharacteristicNotifyState(int addressArgs, int handleArgs, int stateNumberArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.setCharacteristicNotifyState'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs, stateNumberArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future readDescriptor(int addressArgs, int handleArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.readDescriptor'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + 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 (__pigeon_replyList[0] as Uint8List?)!; + } + } + + Future writeDescriptor(int addressArgs, int handleArgs, Uint8List valueArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.writeDescriptor'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs, valueArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } +} + +class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { + const _MyCentralManagerFlutterApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is MyAdvertisementArgs) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is MyManufacturerSpecificDataArgs) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MyPeripheralArgs) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return MyAdvertisementArgs.decode(readValue(buffer)!); + case 129: + return MyManufacturerSpecificDataArgs.decode(readValue(buffer)!); + case 130: + return MyPeripheralArgs.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class MyCentralManagerFlutterApi { + static const MessageCodec pigeonChannelCodec = _MyCentralManagerFlutterApiCodec(); + + void onStateChanged(int stateNumberArgs); + + void onDiscovered(MyPeripheralArgs peripheralArgs, int rssiArgs, MyAdvertisementArgs advertisementArgs); + + void onConnectionStateChanged(int addressArgs, bool stateArgs); + + void onCharacteristicNotified(int addressArgs, int handleArgs, Uint8List valueArgs); + + static void setup(MyCentralManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onStateChanged', 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.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onStateChanged was null.'); + final List args = (message as List?)!; + final int? arg_stateNumberArgs = (args[0] as int?); + assert(arg_stateNumberArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onStateChanged was null, expected non-null int.'); + try { + api.onStateChanged(arg_stateNumberArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onDiscovered', 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.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onDiscovered was null.'); + final List args = (message as List?)!; + final MyPeripheralArgs? arg_peripheralArgs = (args[0] as MyPeripheralArgs?); + assert(arg_peripheralArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onDiscovered was null, expected non-null MyPeripheralArgs.'); + final int? arg_rssiArgs = (args[1] as int?); + assert(arg_rssiArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onDiscovered was null, expected non-null int.'); + final MyAdvertisementArgs? arg_advertisementArgs = (args[2] as MyAdvertisementArgs?); + assert(arg_advertisementArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onDiscovered was null, expected non-null MyAdvertisementArgs.'); + try { + api.onDiscovered(arg_peripheralArgs!, arg_rssiArgs!, arg_advertisementArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onConnectionStateChanged', 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.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onConnectionStateChanged was null.'); + final List args = (message as List?)!; + final int? arg_addressArgs = (args[0] as int?); + assert(arg_addressArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onConnectionStateChanged was null, expected non-null int.'); + final bool? arg_stateArgs = (args[1] as bool?); + assert(arg_stateArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onConnectionStateChanged was null, expected non-null bool.'); + try { + api.onConnectionStateChanged(arg_addressArgs!, arg_stateArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onCharacteristicNotified', 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.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onCharacteristicNotified was null.'); + final List args = (message as List?)!; + final int? arg_addressArgs = (args[0] as int?); + assert(arg_addressArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onCharacteristicNotified was null, expected non-null int.'); + final int? arg_handleArgs = (args[1] as int?); + assert(arg_handleArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onCharacteristicNotified was null, expected non-null int.'); + final Uint8List? arg_valueArgs = (args[2] as Uint8List?); + assert(arg_valueArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onCharacteristicNotified was null, expected non-null Uint8List.'); + try { + api.onCharacteristicNotified(arg_addressArgs!, arg_handleArgs!, arg_valueArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } +} + +class _MyPeripheralManagerHostApiCodec extends StandardMessageCodec { + const _MyPeripheralManagerHostApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is MyAdvertisementArgs) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is MyGattCharacteristicArgs) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MyGattDescriptorArgs) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is MyGattServiceArgs) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is MyManufacturerSpecificDataArgs) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return MyAdvertisementArgs.decode(readValue(buffer)!); + case 129: + return MyGattCharacteristicArgs.decode(readValue(buffer)!); + case 130: + return MyGattDescriptorArgs.decode(readValue(buffer)!); + case 131: + return MyGattServiceArgs.decode(readValue(buffer)!); + case 132: + return MyManufacturerSpecificDataArgs.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class MyPeripheralManagerHostApi { + /// Constructor for [MyPeripheralManagerHostApi]. 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. + MyPeripheralManagerHostApi({BinaryMessenger? binaryMessenger}) + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerHostApiCodec(); + + Future setUp() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.setUp'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future addService(MyGattServiceArgs serviceArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.addService'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([serviceArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future removeService(int handleArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.removeService'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([handleArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future clearServices() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.clearServices'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future startAdvertising(MyAdvertisementArgs advertisementArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.startAdvertising'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([advertisementArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future stopAdvertising() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.stopAdvertising'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future sendReadCharacteristicReply(int addressArgs, int handleArgs, bool statusArgs, Uint8List valueArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.sendReadCharacteristicReply'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs, statusArgs, valueArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future sendWriteCharacteristicReply(int addressArgs, int handleArgs, bool statusArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.sendWriteCharacteristicReply'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs, statusArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + Future notifyCharacteristic(int addressArgs, int handleArgs, Uint8List valueArgs) async { + const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.notifyCharacteristic'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs, valueArgs]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } +} + +class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { + const _MyPeripheralManagerFlutterApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is MyCentralArgs) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return MyCentralArgs.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class MyPeripheralManagerFlutterApi { + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerFlutterApiCodec(); + + void onStateChanged(int stateNumberArgs); + + void onReadCharacteristicCommandReceived(MyCentralArgs centralArgs, int handleArgs); + + void onWriteCharacteristicCommandReceived(MyCentralArgs centralArgs, int handleArgs, Uint8List valueArgs); + + void onCharacteristicNotifyStateChanged(MyCentralArgs centralArgs, int handleArgs, bool stateArgs); + + static void setup(MyPeripheralManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onStateChanged', 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.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onStateChanged was null.'); + final List args = (message as List?)!; + final int? arg_stateNumberArgs = (args[0] as int?); + assert(arg_stateNumberArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onStateChanged was null, expected non-null int.'); + try { + api.onStateChanged(arg_stateNumberArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived', 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.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null.'); + final List args = (message as List?)!; + final MyCentralArgs? arg_centralArgs = (args[0] as MyCentralArgs?); + assert(arg_centralArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null, expected non-null MyCentralArgs.'); + final int? arg_handleArgs = (args[1] as int?); + assert(arg_handleArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived was null, expected non-null int.'); + try { + api.onReadCharacteristicCommandReceived(arg_centralArgs!, arg_handleArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived', 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.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null.'); + final List args = (message as List?)!; + final MyCentralArgs? arg_centralArgs = (args[0] as MyCentralArgs?); + assert(arg_centralArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null MyCentralArgs.'); + final int? arg_handleArgs = (args[1] as int?); + assert(arg_handleArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null int.'); + final Uint8List? arg_valueArgs = (args[2] as Uint8List?); + assert(arg_valueArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived was null, expected non-null Uint8List.'); + try { + api.onWriteCharacteristicCommandReceived(arg_centralArgs!, arg_handleArgs!, arg_valueArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged', 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.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null.'); + final List args = (message as List?)!; + final MyCentralArgs? arg_centralArgs = (args[0] as MyCentralArgs?); + assert(arg_centralArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null, expected non-null MyCentralArgs.'); + final int? arg_handleArgs = (args[1] as int?); + assert(arg_handleArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null, expected non-null int.'); + final bool? arg_stateArgs = (args[2] as bool?); + assert(arg_stateArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null, expected non-null bool.'); + try { + api.onCharacteristicNotifyStateChanged(arg_centralArgs!, arg_handleArgs!, arg_stateArgs!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } +} diff --git a/bluetooth_low_energy_windows/lib/src/my_bluetooth_low_energy.dart b/bluetooth_low_energy_windows/lib/src/my_bluetooth_low_energy.dart deleted file mode 100644 index 48b0455..0000000 --- a/bluetooth_low_energy_windows/lib/src/my_bluetooth_low_energy.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; - -class MyBluetoothLowEnergy extends BluetoothLowEnergy { - @override - // TODO: implement centralManager - CentralManager get centralManager => throw UnimplementedError(); - - @override - // TODO: implement peripheralManager - PeripheralManager get peripheralManager => throw UnimplementedError(); -} diff --git a/bluetooth_low_energy_windows/lib/src/my_central_manager.dart b/bluetooth_low_energy_windows/lib/src/my_central_manager.dart new file mode 100644 index 0000000..b0a9cc4 --- /dev/null +++ b/bluetooth_low_energy_windows/lib/src/my_central_manager.dart @@ -0,0 +1,317 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:flutter/foundation.dart'; + +import 'my_api.dart'; +import 'my_gatt_characteristic2.dart'; +import 'my_gatt_descriptor2.dart'; + +class MyCentralManager extends CentralManager + implements MyCentralManagerFlutterApi { + final MyCentralManagerHostApi _api; + final StreamController + _stateChangedController; + final StreamController _discoveredController; + final StreamController + _connectionStateChangedController; + final StreamController + _characteristicNotifiedController; + + final Map _peripherals; + final Map> _characteristics; + + BluetoothLowEnergyState _state; + + MyCentralManager() + : _api = MyCentralManagerHostApi(), + _stateChangedController = StreamController.broadcast(), + _discoveredController = StreamController.broadcast(), + _connectionStateChangedController = StreamController.broadcast(), + _characteristicNotifiedController = StreamController.broadcast(), + _peripherals = {}, + _characteristics = {}, + _state = BluetoothLowEnergyState.unknown; + + @override + Stream get stateChanged => + _stateChangedController.stream; + @override + Stream get discovered => _discoveredController.stream; + @override + Stream get connectionStateChanged => + _connectionStateChangedController.stream; + @override + Stream get characteristicNotified => + _characteristicNotifiedController.stream; + + @override + Future setUp() async { + logger.info('setUp'); + await _api.setUp(); + MyCentralManagerFlutterApi.setup(this); + } + + @override + Future getState() { + logger.info('getState'); + return Future.value(_state); + } + + @override + Future startDiscovery() async { + logger.info('startDiscovery'); + await _api.startDiscovery(); + } + + @override + Future stopDiscovery() async { + logger.info('stopDiscovery'); + await _api.stopDiscovery(); + } + + @override + Future connect(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { + throw TypeError(); + } + final addressArgs = peripheral.uuid.toAddressArgs(); + logger.info('connect: $addressArgs'); + await _api.connect(addressArgs); + } + + @override + Future disconnect(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { + throw TypeError(); + } + final addressArgs = peripheral.uuid.toAddressArgs(); + logger.info('disconnect: $addressArgs'); + await _api.disconnect(addressArgs); + } + + @override + Future readRSSI(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { + throw TypeError(); + } + final addressArgs = peripheral.uuid.toAddressArgs(); + logger.info('readRSSI: $addressArgs'); + // TODO: Windows doesn't support read RSSI after connected. + throw UnimplementedError(); + } + + @override + Future> discoverGATT(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { + throw TypeError(); + } + // 发现 GATT 服务 + final addressArgs = peripheral.uuid.toAddressArgs(); + logger.info('discoverServices: $addressArgs'); + final servicesArgs = await _api + .discoverServices(addressArgs) + .then((args) => args.cast()); + for (var serviceArgs in servicesArgs) { + // 发现 GATT 特征值 + final handleArgs = serviceArgs.handleArgs; + logger.info('discoverCharacteristics: $addressArgs.$handleArgs'); + final characteristicsArgs = await _api + .discoverCharacteristics(addressArgs, handleArgs) + .then((args) => args.cast()); + for (var characteristicArgs in characteristicsArgs) { + // 发现 GATT 描述值 + final handleArgs = characteristicArgs.handleArgs; + logger.info('discoverDescriptors: $addressArgs.$handleArgs'); + final descriptorsArgs = await _api + .discoverDescriptors(addressArgs, handleArgs) + .then((args) => args.cast()); + characteristicArgs.descriptorsArgs = descriptorsArgs; + } + serviceArgs.characteristicsArgs = characteristicsArgs; + } + final services = + servicesArgs.map((args) => args.toService2(peripheral)).toList(); + final characteristics = + services.expand((service) => service.characteristics).toList(); + _characteristics[addressArgs] = { + for (var characteristic in characteristics) + characteristic.handle: characteristic + }; + return services; + } + + @override + Future readCharacteristic( + GattCharacteristic characteristic, + ) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final peripheral = characteristic.peripheral; + final addressArgs = peripheral.uuid.toAddressArgs(); + final handleArgs = characteristic.handle; + logger.info('readCharacteristic: $addressArgs.$handleArgs'); + final value = await _api.readCharacteristic(addressArgs, handleArgs); + return value; + } + + @override + Future writeCharacteristic( + GattCharacteristic characteristic, { + required Uint8List value, + required GattCharacteristicWriteType type, + }) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final peripheral = characteristic.peripheral; + final addressArgs = peripheral.uuid.toAddressArgs(); + final handleArgs = characteristic.handle; + final trimmedValueArgs = value.trimGATT(); + final typeArgs = type.toArgs(); + final typeNumberArgs = typeArgs.index; + logger.info( + 'writeCharacteristic: $addressArgs.$handleArgs - $trimmedValueArgs, $typeArgs'); + await _api.writeCharacteristic( + addressArgs, + handleArgs, + trimmedValueArgs, + typeNumberArgs, + ); + } + + @override + Future setCharacteristicNotifyState( + GattCharacteristic characteristic, { + required bool state, + }) async { + if (characteristic is! MyGattCharacteristic2) { + throw TypeError(); + } + final peripheral = characteristic.peripheral; + final addressArgs = peripheral.uuid.toAddressArgs(); + final handleArgs = characteristic.handle; + final stateArgs = state + ? characteristic.properties.contains(GattCharacteristicProperty.notify) + ? MyGattCharacteristicNotifyStateArgs.notify + : MyGattCharacteristicNotifyStateArgs.indicate + : MyGattCharacteristicNotifyStateArgs.none; + final stateNumberArgs = stateArgs.index; + logger.info( + 'setCharacteristicNotifyState: $addressArgs.$handleArgs - $stateArgs'); + await _api.setCharacteristicNotifyState( + addressArgs, + handleArgs, + stateNumberArgs, + ); + } + + @override + Future readDescriptor(GattDescriptor descriptor) async { + if (descriptor is! MyGattDescriptor2) { + throw TypeError(); + } + final peripheral = descriptor.peripheral; + final addressArgs = peripheral.uuid.toAddressArgs(); + final handleArgs = descriptor.handle; + logger.info('readDescriptor: $addressArgs.$handleArgs'); + final value = await _api.readDescriptor(addressArgs, handleArgs); + return value; + } + + @override + Future writeDescriptor( + GattDescriptor descriptor, { + required Uint8List value, + }) async { + if (descriptor is! MyGattDescriptor2) { + throw TypeError(); + } + final peripheral = descriptor.peripheral; + final addressArgs = peripheral.uuid.toAddressArgs(); + final handleArgs = descriptor.handle; + final trimmedValueArgs = value.trimGATT(); + logger + .info('writeDescriptor: $addressArgs.$handleArgs - $trimmedValueArgs'); + await _api.writeDescriptor(addressArgs, handleArgs, trimmedValueArgs); + } + + @override + void onStateChanged(int stateNumberArgs) { + final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; + logger.info('onStateChanged: $stateArgs'); + final state = stateArgs.toState(); + if (_state == state) { + return; + } + _state = state; + final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); + _stateChangedController.add(eventArgs); + } + + @override + void onDiscovered( + MyPeripheralArgs peripheralArgs, + int rssiArgs, + MyAdvertisementArgs advertisementArgs, + ) { + final addressArgs = peripheralArgs.addressArgs; + logger.info('onDiscovered: $addressArgs - $rssiArgs, $advertisementArgs'); + final peripheral = _peripherals.putIfAbsent( + peripheralArgs.addressArgs, + () => peripheralArgs.toPeripheral(), + ); + final rssi = rssiArgs; + final advertisement = advertisementArgs.toAdvertisement(); + final eventArgs = DiscoveredEventArgs( + peripheral, + rssi, + advertisement, + ); + _discoveredController.add(eventArgs); + } + + @override + void onConnectionStateChanged(int addressArgs, bool stateArgs) { + logger.info('onConnectionStateChanged: $addressArgs - $stateArgs'); + final peripheral = _peripherals[addressArgs]; + if (peripheral == null) { + return; + } + final state = stateArgs; + final eventArgs = ConnectionStateChangedEventArgs(peripheral, state); + _connectionStateChangedController.add(eventArgs); + if (!state) { + _characteristics.remove(addressArgs); + } + } + + @override + void onCharacteristicNotified( + int addressArgs, + int handleArgs, + Uint8List valueArgs, + ) { + logger.info( + 'onCharacteristicNotified: $addressArgs.$handleArgs - $valueArgs'); + final characteristic = _retrieveCharacteristic(addressArgs, handleArgs); + if (characteristic == null) { + return; + } + final value = valueArgs; + final eventArgs = GattCharacteristicNotifiedEventArgs( + characteristic, + value, + ); + _characteristicNotifiedController.add(eventArgs); + } + + GattCharacteristic? _retrieveCharacteristic(int addressArgs, int handleArgs) { + final characteristics = _characteristics[addressArgs]; + if (characteristics == null) { + return null; + } + return characteristics[handleArgs]; + } +} diff --git a/bluetooth_low_energy_windows/lib/src/my_gatt_characteristic2.dart b/bluetooth_low_energy_windows/lib/src/my_gatt_characteristic2.dart new file mode 100644 index 0000000..00e3c1a --- /dev/null +++ b/bluetooth_low_energy_windows/lib/src/my_gatt_characteristic2.dart @@ -0,0 +1,30 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +import 'my_gatt_descriptor2.dart'; + +class MyGattCharacteristic2 extends MyGattCharacteristic { + final MyPeripheral peripheral; + final int handle; + + MyGattCharacteristic2({ + required this.peripheral, + required this.handle, + required super.uuid, + required super.properties, + required List descriptors, + }) : super(descriptors: descriptors); + + @override + List get descriptors => + super.descriptors.cast(); + + @override + int get hashCode => Object.hash(peripheral, handle); + + @override + bool operator ==(Object other) { + return other is MyGattCharacteristic2 && + other.peripheral == peripheral && + other.handle == handle; + } +} diff --git a/bluetooth_low_energy_windows/lib/src/my_gatt_descriptor2.dart b/bluetooth_low_energy_windows/lib/src/my_gatt_descriptor2.dart new file mode 100644 index 0000000..16ec0af --- /dev/null +++ b/bluetooth_low_energy_windows/lib/src/my_gatt_descriptor2.dart @@ -0,0 +1,22 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +class MyGattDescriptor2 extends MyGattDescriptor { + final MyPeripheral peripheral; + final int handle; + + MyGattDescriptor2({ + required this.peripheral, + required this.handle, + required super.uuid, + }); + + @override + int get hashCode => Object.hash(peripheral, handle); + + @override + bool operator ==(Object other) { + return other is MyGattDescriptor2 && + other.peripheral == peripheral && + other.handle == handle; + } +} diff --git a/bluetooth_low_energy_windows/lib/src/my_gatt_service2.dart b/bluetooth_low_energy_windows/lib/src/my_gatt_service2.dart new file mode 100644 index 0000000..2ac169b --- /dev/null +++ b/bluetooth_low_energy_windows/lib/src/my_gatt_service2.dart @@ -0,0 +1,29 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +import 'my_gatt_characteristic2.dart'; + +class MyGattService2 extends MyGattService { + final MyPeripheral peripheral; + final int handle; + + MyGattService2({ + required this.peripheral, + required this.handle, + required super.uuid, + required List characteristics, + }) : super(characteristics: characteristics); + + @override + List get characteristics => + super.characteristics.cast(); + + @override + int get hashCode => Object.hash(peripheral, handle); + + @override + bool operator ==(Object other) { + return other is MyGattService2 && + other.peripheral == peripheral && + other.handle == handle; + } +} diff --git a/bluetooth_low_energy_windows/my_api.dart b/bluetooth_low_energy_windows/my_api.dart new file mode 100644 index 0000000..a80a0df --- /dev/null +++ b/bluetooth_low_energy_windows/my_api.dart @@ -0,0 +1,220 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/my_api.g.dart', + dartOptions: DartOptions(), + dartPackageName: 'bluetooth_low_energy_windows', + cppHeaderOut: 'windows/my_api.g.h', + cppSourceOut: 'windows/my_api.g.cpp', + cppOptions: CppOptions( + namespace: 'bluetooth_low_energy_windows', + ), + ), +) +enum MyBluetoothLowEnergyStateArgs { + unknown, + disabled, + off, + on, +} + +enum MyGattCharacteristicPropertyArgs { + read, + write, + writeWithoutResponse, + notify, + indicate, +} + +enum MyGattCharacteristicWriteTypeArgs { + withResponse, + withoutResponse, +} + +enum MyGattCharacteristicNotifyStateArgs { + none, + notify, + indicate, +} + +class MyManufacturerSpecificDataArgs { + final int idArgs; + final Uint8List dataArgs; + + MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs); +} + +class MyAdvertisementArgs { + final String? nameArgs; + final List serviceUUIDsArgs; + final Map serviceDataArgs; + final MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs; + + MyAdvertisementArgs( + this.nameArgs, + this.serviceUUIDsArgs, + this.serviceDataArgs, + this.manufacturerSpecificDataArgs, + ); +} + +class MyCentralArgs { + final int addressArgs; + + MyCentralArgs(this.addressArgs); +} + +class MyPeripheralArgs { + final int addressArgs; + + MyPeripheralArgs(this.addressArgs); +} + +class MyGattDescriptorArgs { + final int handleArgs; + final String uuidArgs; + final Uint8List? valueArgs; + + MyGattDescriptorArgs( + this.handleArgs, + this.uuidArgs, + this.valueArgs, + ); +} + +class MyGattCharacteristicArgs { + final int handleArgs; + final String uuidArgs; + final List propertyNumbersArgs; + final List descriptorsArgs; + + MyGattCharacteristicArgs( + this.handleArgs, + this.uuidArgs, + this.propertyNumbersArgs, + this.descriptorsArgs, + ); +} + +class MyGattServiceArgs { + final int handleArgs; + final String uuidArgs; + final List characteristicsArgs; + + MyGattServiceArgs( + this.handleArgs, + this.uuidArgs, + this.characteristicsArgs, + ); +} + +@HostApi() +abstract class MyCentralManagerHostApi { + @async + void setUp(); + void startDiscovery(); + void stopDiscovery(); + @async + void connect(int addressArgs); + void disconnect(int addressArgs); + @async + List discoverServices(int addressArgs); + @async + List discoverCharacteristics( + int addressArgs, + int handleArgs, + ); + @async + List discoverDescriptors( + int addressArgs, + int handleArgs, + ); + @async + Uint8List readCharacteristic(int addressArgs, int handleArgs); + @async + void writeCharacteristic( + int addressArgs, + int handleArgs, + Uint8List valueArgs, + int typeNumberArgs, + ); + @async + void setCharacteristicNotifyState( + int addressArgs, + int handleArgs, + int stateNumberArgs, + ); + @async + Uint8List readDescriptor(int addressArgs, int handleArgs); + @async + void writeDescriptor(int addressArgs, int handleArgs, Uint8List valueArgs); +} + +@FlutterApi() +abstract class MyCentralManagerFlutterApi { + void onStateChanged(int stateNumberArgs); + void onDiscovered( + MyPeripheralArgs peripheralArgs, + int rssiArgs, + MyAdvertisementArgs advertisementArgs, + ); + void onConnectionStateChanged( + int addressArgs, + bool stateArgs, + ); + void onCharacteristicNotified( + int addressArgs, + int handleArgs, + Uint8List valueArgs, + ); +} + +@HostApi() +abstract class MyPeripheralManagerHostApi { + @async + void setUp(); + @async + void addService(MyGattServiceArgs serviceArgs); + void removeService(int handleArgs); + void clearServices(); + @async + void startAdvertising(MyAdvertisementArgs advertisementArgs); + void stopAdvertising(); + void sendReadCharacteristicReply( + int addressArgs, + int handleArgs, + bool statusArgs, + Uint8List valueArgs, + ); + void sendWriteCharacteristicReply( + int addressArgs, + int handleArgs, + bool statusArgs, + ); + @async + void notifyCharacteristic( + int addressArgs, + int handleArgs, + Uint8List valueArgs, + ); +} + +@FlutterApi() +abstract class MyPeripheralManagerFlutterApi { + void onStateChanged(int stateNumberArgs); + void onReadCharacteristicCommandReceived( + MyCentralArgs centralArgs, + int handleArgs, + ); + void onWriteCharacteristicCommandReceived( + MyCentralArgs centralArgs, + int handleArgs, + Uint8List valueArgs, + ); + void onCharacteristicNotifyStateChanged( + MyCentralArgs centralArgs, + int handleArgs, + bool stateArgs, + ); +} diff --git a/bluetooth_low_energy_windows/pubspec.yaml b/bluetooth_low_energy_windows/pubspec.yaml index 3839ea2..cf145e8 100644 --- a/bluetooth_low_energy_windows/pubspec.yaml +++ b/bluetooth_low_energy_windows/pubspec.yaml @@ -1,6 +1,6 @@ name: bluetooth_low_energy_windows description: Windows implementation of the bluetooth_low_energy plugin. -version: 3.0.0 +version: 5.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy environment: @@ -10,17 +10,18 @@ environment: dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^3.0.0 - win32: ^5.0.6 + bluetooth_low_energy_platform_interface: ^5.0.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.0 + flutter_lints: ^3.0.0 + pigeon: ^15.0.2 flutter: plugin: implements: bluetooth_low_energy platforms: windows: + pluginClass: BluetoothLowEnergyWindowsCApi dartPluginClass: BluetoothLowEnergyWindows diff --git a/bluetooth_low_energy_windows/windows/.gitignore b/bluetooth_low_energy_windows/windows/.gitignore new file mode 100644 index 0000000..b3eb2be --- /dev/null +++ b/bluetooth_low_energy_windows/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/bluetooth_low_energy_windows/windows/CMakeLists.txt b/bluetooth_low_energy_windows/windows/CMakeLists.txt new file mode 100644 index 0000000..3589bcd --- /dev/null +++ b/bluetooth_low_energy_windows/windows/CMakeLists.txt @@ -0,0 +1,135 @@ +# The Flutter tooling requires that developers have a version of Visual Studio +# installed that includes CMake 3.14 or later. You should not increase this +# version, as doing so will cause the plugin to fail to compile for some +# customers of the plugin. +cmake_minimum_required(VERSION 3.14) + +# Project-level configuration. +set(PROJECT_NAME "bluetooth_low_energy_windows") +project(${PROJECT_NAME} LANGUAGES CXX) + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "bluetooth_low_energy_windows_plugin") + +# NUGET: Microsoft.Windows.CppWinRT +set(CPPWINRT_VERSION "2.0.230706.1") + +find_program(NUGET nuget) +if(NOT NUGET) + message(FATAL_ERROR "Nuget.exe not found") +endif() + +execute_process(COMMAND + ${NUGET} install Microsoft.Windows.CppWinRT -Version ${CPPWINRT_VERSION} -OutputDirectory packages + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if(NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}") +endif() + +set(CPPWINRT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/bin/cppwinrt.exe) +execute_process(COMMAND + ${CPPWINRT} -input sdk -output include + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if(NOT ret EQUAL 0) + message(FATAL_ERROR "Failed to run cppwinrt.exe") +endif() + +include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/include) + +# Any new source files that you add to the plugin should be added here. +list(APPEND PLUGIN_SOURCES + "bluetooth_low_energy_windows.cpp" + "bluetooth_low_energy_windows.h" + "my_api.g.cpp" + "my_api.g.h" + "my_exception.cpp" + "my_exception.h" + "my_central_manager.cpp" + "my_central_manager.h" +) + +# Define the plugin library target. Its name must not be changed (see comment +# on PLUGIN_NAME above). +add_library(${PLUGIN_NAME} SHARED + "include/bluetooth_low_energy_windows/bluetooth_low_energy_windows_c_api.h" + "bluetooth_low_energy_windows_c_api.cpp" + ${PLUGIN_SOURCES} +) + +# Apply a standard set of build settings that are configured in the +# application-level CMakeLists.txt. This can be removed for plugins that want +# full control over build settings. +apply_standard_settings(${PLUGIN_NAME}) + +# Symbols are hidden by default to reduce the chance of accidental conflicts +# between plugins. This should not be removed; any symbols that should be +# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro. +set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) + +# Enable compiler support for coroutines +target_compile_features(${PLUGIN_NAME} PRIVATE cxx_std_20) +target_compile_options(${PLUGIN_NAME} PRIVATE /await) + +# Source include directories and library dependencies. Add any plugin-specific +# dependencies here. +target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) + +# List of absolute paths to libraries that should be bundled with the plugin. +# This list could contain prebuilt libraries, or libraries created by an +# external build triggered from this build file. +set(bluetooth_low_energy_windows_bundled_libraries + "" + PARENT_SCOPE +) + +# === Tests === +# These unit tests can be run from a terminal after building the example, or +# from Visual Studio after opening the generated solution file. + +# Only enable test builds when building the example (which sets this variable) +# so that plugin clients aren't building the tests. +if (${include_${PROJECT_NAME}_tests}) +set(TEST_RUNNER "${PROJECT_NAME}_test") +enable_testing() + +# Add the Google Test dependency. +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/release-1.11.0.zip +) +# Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +# Disable install commands for gtest so it doesn't end up in the bundle. +set(INSTALL_GTEST OFF CACHE BOOL "Disable installation of googletest" FORCE) +FetchContent_MakeAvailable(googletest) + +# The plugin's C API is not very useful for unit testing, so build the sources +# directly into the test binary rather than using the DLL. +add_executable(${TEST_RUNNER} + test/bluetooth_low_energy_windows_test.cpp + ${PLUGIN_SOURCES} +) +apply_standard_settings(${TEST_RUNNER}) +target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin) +target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) +# flutter_wrapper_plugin has link dependencies on the Flutter DLL. +add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${FLUTTER_LIBRARY}" $ +) + +# Enable automatic test discovery. +include(GoogleTest) +gtest_discover_tests(${TEST_RUNNER}) +endif() diff --git a/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.cpp b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.cpp new file mode 100644 index 0000000..a294aa4 --- /dev/null +++ b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.cpp @@ -0,0 +1,24 @@ +// This must be included before many other Windows headers. +#include + +#include "bluetooth_low_energy_windows.h" + +namespace bluetooth_low_energy_windows { + // static + void BluetoothLowEnergyWindows::RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar) { + auto messenger = registrar->messenger(); + auto central_manager = std::make_unique(messenger); + MyCentralManagerHostApi::SetUp(messenger, central_manager.get()); + + auto plugin = std::make_unique(std::move(central_manager)); + registrar->AddPlugin(std::move(plugin)); + } + + BluetoothLowEnergyWindows::BluetoothLowEnergyWindows(std::unique_ptr central_manager) + { + m_central_manager = std::move(central_manager); + } + + BluetoothLowEnergyWindows::~BluetoothLowEnergyWindows() {} + +} // namespace bluetooth_low_energy_windows diff --git a/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.h b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.h new file mode 100644 index 0000000..42b9e1c --- /dev/null +++ b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.h @@ -0,0 +1,26 @@ +#ifndef FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_H_ +#define FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_H_ + +#include + +#include "my_central_manager.h" + +namespace bluetooth_low_energy_windows { + class BluetoothLowEnergyWindows : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + BluetoothLowEnergyWindows(std::unique_ptr central_manager); + + virtual ~BluetoothLowEnergyWindows(); + + // Disallow copy and assign. + BluetoothLowEnergyWindows(const BluetoothLowEnergyWindows&) = delete; + BluetoothLowEnergyWindows& operator=(const BluetoothLowEnergyWindows&) = delete; + private: + std::unique_ptr m_central_manager; + }; + +} // namespace bluetooth_low_energy_windows + +#endif // FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_H_ diff --git a/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_c_api.cpp b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_c_api.cpp new file mode 100644 index 0000000..7b0a9cf --- /dev/null +++ b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_c_api.cpp @@ -0,0 +1,12 @@ +#include "include/bluetooth_low_energy_windows/bluetooth_low_energy_windows_c_api.h" + +#include + +#include "bluetooth_low_energy_windows.h" + +void BluetoothLowEnergyWindowsCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + bluetooth_low_energy_windows::BluetoothLowEnergyWindows::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/bluetooth_low_energy_windows/windows/include/bluetooth_low_energy_windows/bluetooth_low_energy_windows_c_api.h b/bluetooth_low_energy_windows/windows/include/bluetooth_low_energy_windows/bluetooth_low_energy_windows_c_api.h new file mode 100644 index 0000000..fd67e78 --- /dev/null +++ b/bluetooth_low_energy_windows/windows/include/bluetooth_low_energy_windows/bluetooth_low_energy_windows_c_api.h @@ -0,0 +1,23 @@ +#ifndef FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_C_API_H_ +#define FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_C_API_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void BluetoothLowEnergyWindowsCApiRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_C_API_H_ diff --git a/bluetooth_low_energy_windows/windows/my_api.g.cpp b/bluetooth_low_energy_windows/windows/my_api.g.cpp new file mode 100644 index 0000000..81f8ac5 --- /dev/null +++ b/bluetooth_low_energy_windows/windows/my_api.g.cpp @@ -0,0 +1,1551 @@ +// Autogenerated from Pigeon (v15.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#undef _HAS_EXCEPTIONS + +#include "my_api.g.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace bluetooth_low_energy_windows { +using flutter::BasicMessageChannel; +using flutter::CustomEncodableValue; +using flutter::EncodableList; +using flutter::EncodableMap; +using flutter::EncodableValue; + +FlutterError CreateConnectionError(const std::string channel_name) { + return FlutterError( + "channel-error", + "Unable to establish connection on channel: '" + channel_name + "'.", + EncodableValue("")); +} + +// MyManufacturerSpecificDataArgs + +MyManufacturerSpecificDataArgs::MyManufacturerSpecificDataArgs( + int64_t id_args, + const std::vector& data_args) + : id_args_(id_args), + data_args_(data_args) {} + +int64_t MyManufacturerSpecificDataArgs::id_args() const { + return id_args_; +} + +void MyManufacturerSpecificDataArgs::set_id_args(int64_t value_arg) { + id_args_ = value_arg; +} + + +const std::vector& MyManufacturerSpecificDataArgs::data_args() const { + return data_args_; +} + +void MyManufacturerSpecificDataArgs::set_data_args(const std::vector& value_arg) { + data_args_ = value_arg; +} + + +EncodableList MyManufacturerSpecificDataArgs::ToEncodableList() const { + EncodableList list; + list.reserve(2); + list.push_back(EncodableValue(id_args_)); + list.push_back(EncodableValue(data_args_)); + return list; +} + +MyManufacturerSpecificDataArgs MyManufacturerSpecificDataArgs::FromEncodableList(const EncodableList& list) { + MyManufacturerSpecificDataArgs decoded( + list[0].LongValue(), + std::get>(list[1])); + return decoded; +} + +// MyAdvertisementArgs + +MyAdvertisementArgs::MyAdvertisementArgs( + const EncodableList& service_u_u_i_ds_args, + const EncodableMap& service_data_args) + : service_u_u_i_ds_args_(service_u_u_i_ds_args), + service_data_args_(service_data_args) {} + +MyAdvertisementArgs::MyAdvertisementArgs( + const std::string* name_args, + const EncodableList& service_u_u_i_ds_args, + const EncodableMap& service_data_args, + const MyManufacturerSpecificDataArgs* manufacturer_specific_data_args) + : name_args_(name_args ? std::optional(*name_args) : std::nullopt), + service_u_u_i_ds_args_(service_u_u_i_ds_args), + service_data_args_(service_data_args), + manufacturer_specific_data_args_(manufacturer_specific_data_args ? std::optional(*manufacturer_specific_data_args) : std::nullopt) {} + +const std::string* MyAdvertisementArgs::name_args() const { + return name_args_ ? &(*name_args_) : nullptr; +} + +void MyAdvertisementArgs::set_name_args(const std::string_view* value_arg) { + name_args_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void MyAdvertisementArgs::set_name_args(std::string_view value_arg) { + name_args_ = value_arg; +} + + +const EncodableList& MyAdvertisementArgs::service_u_u_i_ds_args() const { + return service_u_u_i_ds_args_; +} + +void MyAdvertisementArgs::set_service_u_u_i_ds_args(const EncodableList& value_arg) { + service_u_u_i_ds_args_ = value_arg; +} + + +const EncodableMap& MyAdvertisementArgs::service_data_args() const { + return service_data_args_; +} + +void MyAdvertisementArgs::set_service_data_args(const EncodableMap& value_arg) { + service_data_args_ = value_arg; +} + + +const MyManufacturerSpecificDataArgs* MyAdvertisementArgs::manufacturer_specific_data_args() const { + return manufacturer_specific_data_args_ ? &(*manufacturer_specific_data_args_) : nullptr; +} + +void MyAdvertisementArgs::set_manufacturer_specific_data_args(const MyManufacturerSpecificDataArgs* value_arg) { + manufacturer_specific_data_args_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void MyAdvertisementArgs::set_manufacturer_specific_data_args(const MyManufacturerSpecificDataArgs& value_arg) { + manufacturer_specific_data_args_ = value_arg; +} + + +EncodableList MyAdvertisementArgs::ToEncodableList() const { + EncodableList list; + list.reserve(4); + list.push_back(name_args_ ? EncodableValue(*name_args_) : EncodableValue()); + list.push_back(EncodableValue(service_u_u_i_ds_args_)); + list.push_back(EncodableValue(service_data_args_)); + list.push_back(manufacturer_specific_data_args_ ? EncodableValue(manufacturer_specific_data_args_->ToEncodableList()) : EncodableValue()); + return list; +} + +MyAdvertisementArgs MyAdvertisementArgs::FromEncodableList(const EncodableList& list) { + MyAdvertisementArgs decoded( + std::get(list[1]), + std::get(list[2])); + auto& encodable_name_args = list[0]; + if (!encodable_name_args.IsNull()) { + decoded.set_name_args(std::get(encodable_name_args)); + } + auto& encodable_manufacturer_specific_data_args = list[3]; + if (!encodable_manufacturer_specific_data_args.IsNull()) { + decoded.set_manufacturer_specific_data_args(MyManufacturerSpecificDataArgs::FromEncodableList(std::get(encodable_manufacturer_specific_data_args))); + } + return decoded; +} + +// MyCentralArgs + +MyCentralArgs::MyCentralArgs(int64_t address_args) + : address_args_(address_args) {} + +int64_t MyCentralArgs::address_args() const { + return address_args_; +} + +void MyCentralArgs::set_address_args(int64_t value_arg) { + address_args_ = value_arg; +} + + +EncodableList MyCentralArgs::ToEncodableList() const { + EncodableList list; + list.reserve(1); + list.push_back(EncodableValue(address_args_)); + return list; +} + +MyCentralArgs MyCentralArgs::FromEncodableList(const EncodableList& list) { + MyCentralArgs decoded( + list[0].LongValue()); + return decoded; +} + +// MyPeripheralArgs + +MyPeripheralArgs::MyPeripheralArgs(int64_t address_args) + : address_args_(address_args) {} + +int64_t MyPeripheralArgs::address_args() const { + return address_args_; +} + +void MyPeripheralArgs::set_address_args(int64_t value_arg) { + address_args_ = value_arg; +} + + +EncodableList MyPeripheralArgs::ToEncodableList() const { + EncodableList list; + list.reserve(1); + list.push_back(EncodableValue(address_args_)); + return list; +} + +MyPeripheralArgs MyPeripheralArgs::FromEncodableList(const EncodableList& list) { + MyPeripheralArgs decoded( + list[0].LongValue()); + return decoded; +} + +// MyGattDescriptorArgs + +MyGattDescriptorArgs::MyGattDescriptorArgs( + int64_t handle_args, + const std::string& uuid_args) + : handle_args_(handle_args), + uuid_args_(uuid_args) {} + +MyGattDescriptorArgs::MyGattDescriptorArgs( + int64_t handle_args, + const std::string& uuid_args, + const std::vector* value_args) + : handle_args_(handle_args), + uuid_args_(uuid_args), + value_args_(value_args ? std::optional>(*value_args) : std::nullopt) {} + +int64_t MyGattDescriptorArgs::handle_args() const { + return handle_args_; +} + +void MyGattDescriptorArgs::set_handle_args(int64_t value_arg) { + handle_args_ = value_arg; +} + + +const std::string& MyGattDescriptorArgs::uuid_args() const { + return uuid_args_; +} + +void MyGattDescriptorArgs::set_uuid_args(std::string_view value_arg) { + uuid_args_ = value_arg; +} + + +const std::vector* MyGattDescriptorArgs::value_args() const { + return value_args_ ? &(*value_args_) : nullptr; +} + +void MyGattDescriptorArgs::set_value_args(const std::vector* value_arg) { + value_args_ = value_arg ? std::optional>(*value_arg) : std::nullopt; +} + +void MyGattDescriptorArgs::set_value_args(const std::vector& value_arg) { + value_args_ = value_arg; +} + + +EncodableList MyGattDescriptorArgs::ToEncodableList() const { + EncodableList list; + list.reserve(3); + list.push_back(EncodableValue(handle_args_)); + list.push_back(EncodableValue(uuid_args_)); + list.push_back(value_args_ ? EncodableValue(*value_args_) : EncodableValue()); + return list; +} + +MyGattDescriptorArgs MyGattDescriptorArgs::FromEncodableList(const EncodableList& list) { + MyGattDescriptorArgs decoded( + list[0].LongValue(), + std::get(list[1])); + auto& encodable_value_args = list[2]; + if (!encodable_value_args.IsNull()) { + decoded.set_value_args(std::get>(encodable_value_args)); + } + return decoded; +} + +// MyGattCharacteristicArgs + +MyGattCharacteristicArgs::MyGattCharacteristicArgs( + int64_t handle_args, + const std::string& uuid_args, + const EncodableList& property_numbers_args, + const EncodableList& descriptors_args) + : handle_args_(handle_args), + uuid_args_(uuid_args), + property_numbers_args_(property_numbers_args), + descriptors_args_(descriptors_args) {} + +int64_t MyGattCharacteristicArgs::handle_args() const { + return handle_args_; +} + +void MyGattCharacteristicArgs::set_handle_args(int64_t value_arg) { + handle_args_ = value_arg; +} + + +const std::string& MyGattCharacteristicArgs::uuid_args() const { + return uuid_args_; +} + +void MyGattCharacteristicArgs::set_uuid_args(std::string_view value_arg) { + uuid_args_ = value_arg; +} + + +const EncodableList& MyGattCharacteristicArgs::property_numbers_args() const { + return property_numbers_args_; +} + +void MyGattCharacteristicArgs::set_property_numbers_args(const EncodableList& value_arg) { + property_numbers_args_ = value_arg; +} + + +const EncodableList& MyGattCharacteristicArgs::descriptors_args() const { + return descriptors_args_; +} + +void MyGattCharacteristicArgs::set_descriptors_args(const EncodableList& value_arg) { + descriptors_args_ = value_arg; +} + + +EncodableList MyGattCharacteristicArgs::ToEncodableList() const { + EncodableList list; + list.reserve(4); + list.push_back(EncodableValue(handle_args_)); + list.push_back(EncodableValue(uuid_args_)); + list.push_back(EncodableValue(property_numbers_args_)); + list.push_back(EncodableValue(descriptors_args_)); + return list; +} + +MyGattCharacteristicArgs MyGattCharacteristicArgs::FromEncodableList(const EncodableList& list) { + MyGattCharacteristicArgs decoded( + list[0].LongValue(), + std::get(list[1]), + std::get(list[2]), + std::get(list[3])); + return decoded; +} + +// MyGattServiceArgs + +MyGattServiceArgs::MyGattServiceArgs( + int64_t handle_args, + const std::string& uuid_args, + const EncodableList& characteristics_args) + : handle_args_(handle_args), + uuid_args_(uuid_args), + characteristics_args_(characteristics_args) {} + +int64_t MyGattServiceArgs::handle_args() const { + return handle_args_; +} + +void MyGattServiceArgs::set_handle_args(int64_t value_arg) { + handle_args_ = value_arg; +} + + +const std::string& MyGattServiceArgs::uuid_args() const { + return uuid_args_; +} + +void MyGattServiceArgs::set_uuid_args(std::string_view value_arg) { + uuid_args_ = value_arg; +} + + +const EncodableList& MyGattServiceArgs::characteristics_args() const { + return characteristics_args_; +} + +void MyGattServiceArgs::set_characteristics_args(const EncodableList& value_arg) { + characteristics_args_ = value_arg; +} + + +EncodableList MyGattServiceArgs::ToEncodableList() const { + EncodableList list; + list.reserve(3); + list.push_back(EncodableValue(handle_args_)); + list.push_back(EncodableValue(uuid_args_)); + list.push_back(EncodableValue(characteristics_args_)); + return list; +} + +MyGattServiceArgs MyGattServiceArgs::FromEncodableList(const EncodableList& list) { + MyGattServiceArgs decoded( + list[0].LongValue(), + std::get(list[1]), + std::get(list[2])); + return decoded; +} + + +MyCentralManagerHostApiCodecSerializer::MyCentralManagerHostApiCodecSerializer() {} + +EncodableValue MyCentralManagerHostApiCodecSerializer::ReadValueOfType( + uint8_t type, + flutter::ByteStreamReader* stream) const { + switch (type) { + case 128: + return CustomEncodableValue(MyGattCharacteristicArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 129: + return CustomEncodableValue(MyGattDescriptorArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 130: + return CustomEncodableValue(MyGattServiceArgs::FromEncodableList(std::get(ReadValue(stream)))); + default: + return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + } +} + +void MyCentralManagerHostApiCodecSerializer::WriteValue( + const EncodableValue& value, + flutter::ByteStreamWriter* stream) const { + if (const CustomEncodableValue* custom_value = std::get_if(&value)) { + if (custom_value->type() == typeid(MyGattCharacteristicArgs)) { + stream->WriteByte(128); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyGattDescriptorArgs)) { + stream->WriteByte(129); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyGattServiceArgs)) { + stream->WriteByte(130); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + } + flutter::StandardCodecSerializer::WriteValue(value, stream); +} + +/// The codec used by MyCentralManagerHostApi. +const flutter::StandardMessageCodec& MyCentralManagerHostApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&MyCentralManagerHostApiCodecSerializer::GetInstance()); +} + +// Sets up an instance of `MyCentralManagerHostApi` to handle messages through the `binary_messenger`. +void MyCentralManagerHostApi::SetUp( + flutter::BinaryMessenger* binary_messenger, + MyCentralManagerHostApi* api) { + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.setUp", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + api->SetUp([reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.startDiscovery", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->StartDiscovery(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.stopDiscovery", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->StopDiscovery(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.connect", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + api->Connect(address_args_arg, [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.disconnect", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + std::optional output = api->Disconnect(address_args_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverServices", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + api->DiscoverServices(address_args_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())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverCharacteristics", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + const auto& encodable_handle_args_arg = args.at(1); + if (encodable_handle_args_arg.IsNull()) { + reply(WrapError("handle_args_arg unexpectedly null.")); + return; + } + const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + api->DiscoverCharacteristics(address_args_arg, handle_args_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())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverDescriptors", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + const auto& encodable_handle_args_arg = args.at(1); + if (encodable_handle_args_arg.IsNull()) { + reply(WrapError("handle_args_arg unexpectedly null.")); + return; + } + const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + api->DiscoverDescriptors(address_args_arg, handle_args_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())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.readCharacteristic", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + const auto& encodable_handle_args_arg = args.at(1); + if (encodable_handle_args_arg.IsNull()) { + reply(WrapError("handle_args_arg unexpectedly null.")); + return; + } + const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + api->ReadCharacteristic(address_args_arg, handle_args_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())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.writeCharacteristic", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + const auto& encodable_handle_args_arg = args.at(1); + if (encodable_handle_args_arg.IsNull()) { + reply(WrapError("handle_args_arg unexpectedly null.")); + return; + } + const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + const auto& encodable_value_args_arg = args.at(2); + if (encodable_value_args_arg.IsNull()) { + reply(WrapError("value_args_arg unexpectedly null.")); + return; + } + const auto& value_args_arg = std::get>(encodable_value_args_arg); + const auto& encodable_type_number_args_arg = args.at(3); + if (encodable_type_number_args_arg.IsNull()) { + reply(WrapError("type_number_args_arg unexpectedly null.")); + return; + } + const int64_t type_number_args_arg = encodable_type_number_args_arg.LongValue(); + api->WriteCharacteristic(address_args_arg, handle_args_arg, value_args_arg, type_number_args_arg, [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.setCharacteristicNotifyState", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + const auto& encodable_handle_args_arg = args.at(1); + if (encodable_handle_args_arg.IsNull()) { + reply(WrapError("handle_args_arg unexpectedly null.")); + return; + } + const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + const auto& encodable_state_number_args_arg = args.at(2); + if (encodable_state_number_args_arg.IsNull()) { + reply(WrapError("state_number_args_arg unexpectedly null.")); + return; + } + const int64_t state_number_args_arg = encodable_state_number_args_arg.LongValue(); + api->SetCharacteristicNotifyState(address_args_arg, handle_args_arg, state_number_args_arg, [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.readDescriptor", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + const auto& encodable_handle_args_arg = args.at(1); + if (encodable_handle_args_arg.IsNull()) { + reply(WrapError("handle_args_arg unexpectedly null.")); + return; + } + const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + api->ReadDescriptor(address_args_arg, handle_args_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())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.writeDescriptor", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + const auto& encodable_handle_args_arg = args.at(1); + if (encodable_handle_args_arg.IsNull()) { + reply(WrapError("handle_args_arg unexpectedly null.")); + return; + } + const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + const auto& encodable_value_args_arg = args.at(2); + if (encodable_value_args_arg.IsNull()) { + reply(WrapError("value_args_arg unexpectedly null.")); + return; + } + const auto& value_args_arg = std::get>(encodable_value_args_arg); + api->WriteDescriptor(address_args_arg, handle_args_arg, value_args_arg, [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } +} + +EncodableValue MyCentralManagerHostApi::WrapError(std::string_view error_message) { + return EncodableValue(EncodableList{ + EncodableValue(std::string(error_message)), + EncodableValue("Error"), + EncodableValue() + }); +} + +EncodableValue MyCentralManagerHostApi::WrapError(const FlutterError& error) { + return EncodableValue(EncodableList{ + EncodableValue(error.code()), + EncodableValue(error.message()), + error.details() + }); +} + + +MyCentralManagerFlutterApiCodecSerializer::MyCentralManagerFlutterApiCodecSerializer() {} + +EncodableValue MyCentralManagerFlutterApiCodecSerializer::ReadValueOfType( + uint8_t type, + flutter::ByteStreamReader* stream) const { + switch (type) { + case 128: + return CustomEncodableValue(MyAdvertisementArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 129: + return CustomEncodableValue(MyManufacturerSpecificDataArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 130: + return CustomEncodableValue(MyPeripheralArgs::FromEncodableList(std::get(ReadValue(stream)))); + default: + return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + } +} + +void MyCentralManagerFlutterApiCodecSerializer::WriteValue( + const EncodableValue& value, + flutter::ByteStreamWriter* stream) const { + if (const CustomEncodableValue* custom_value = std::get_if(&value)) { + if (custom_value->type() == typeid(MyAdvertisementArgs)) { + stream->WriteByte(128); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyManufacturerSpecificDataArgs)) { + stream->WriteByte(129); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyPeripheralArgs)) { + stream->WriteByte(130); + 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++. +MyCentralManagerFlutterApi::MyCentralManagerFlutterApi(flutter::BinaryMessenger* binary_messenger) + : binary_messenger_(binary_messenger) {} + +const flutter::StandardMessageCodec& MyCentralManagerFlutterApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&MyCentralManagerFlutterApiCodecSerializer::GetInstance()); +} + +void MyCentralManagerFlutterApi::OnStateChanged( + int64_t state_number_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onStateChanged"; + auto channel = std::make_unique>(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + EncodableValue(state_number_args_arg), + }); + channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + } else { + on_success(); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + +void MyCentralManagerFlutterApi::OnDiscovered( + const MyPeripheralArgs& peripheral_args_arg, + int64_t rssi_args_arg, + const MyAdvertisementArgs& advertisement_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onDiscovered"; + auto channel = std::make_unique>(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + CustomEncodableValue(peripheral_args_arg), + EncodableValue(rssi_args_arg), + CustomEncodableValue(advertisement_args_arg), + }); + channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + } else { + on_success(); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + +void MyCentralManagerFlutterApi::OnConnectionStateChanged( + int64_t address_args_arg, + bool state_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onConnectionStateChanged"; + auto channel = std::make_unique>(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + EncodableValue(address_args_arg), + EncodableValue(state_args_arg), + }); + channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + } else { + on_success(); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + +void MyCentralManagerFlutterApi::OnCharacteristicNotified( + int64_t address_args_arg, + int64_t handle_args_arg, + const std::vector& value_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterApi.onCharacteristicNotified"; + auto channel = std::make_unique>(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + EncodableValue(address_args_arg), + EncodableValue(handle_args_arg), + EncodableValue(value_args_arg), + }); + channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + } else { + on_success(); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + + +MyPeripheralManagerHostApiCodecSerializer::MyPeripheralManagerHostApiCodecSerializer() {} + +EncodableValue MyPeripheralManagerHostApiCodecSerializer::ReadValueOfType( + uint8_t type, + flutter::ByteStreamReader* stream) const { + switch (type) { + case 128: + return CustomEncodableValue(MyAdvertisementArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 129: + return CustomEncodableValue(MyGattCharacteristicArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 130: + return CustomEncodableValue(MyGattDescriptorArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 131: + return CustomEncodableValue(MyGattServiceArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 132: + return CustomEncodableValue(MyManufacturerSpecificDataArgs::FromEncodableList(std::get(ReadValue(stream)))); + default: + return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + } +} + +void MyPeripheralManagerHostApiCodecSerializer::WriteValue( + const EncodableValue& value, + flutter::ByteStreamWriter* stream) const { + if (const CustomEncodableValue* custom_value = std::get_if(&value)) { + if (custom_value->type() == typeid(MyAdvertisementArgs)) { + stream->WriteByte(128); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyGattCharacteristicArgs)) { + stream->WriteByte(129); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyGattDescriptorArgs)) { + stream->WriteByte(130); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyGattServiceArgs)) { + stream->WriteByte(131); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyManufacturerSpecificDataArgs)) { + stream->WriteByte(132); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + } + flutter::StandardCodecSerializer::WriteValue(value, stream); +} + +/// The codec used by MyPeripheralManagerHostApi. +const flutter::StandardMessageCodec& MyPeripheralManagerHostApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&MyPeripheralManagerHostApiCodecSerializer::GetInstance()); +} + +// Sets up an instance of `MyPeripheralManagerHostApi` to handle messages through the `binary_messenger`. +void MyPeripheralManagerHostApi::SetUp( + flutter::BinaryMessenger* binary_messenger, + MyPeripheralManagerHostApi* api) { + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.setUp", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + api->SetUp([reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.addService", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_service_args_arg = args.at(0); + if (encodable_service_args_arg.IsNull()) { + reply(WrapError("service_args_arg unexpectedly null.")); + return; + } + const auto& service_args_arg = std::any_cast(std::get(encodable_service_args_arg)); + api->AddService(service_args_arg, [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.removeService", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_handle_args_arg = args.at(0); + if (encodable_handle_args_arg.IsNull()) { + reply(WrapError("handle_args_arg unexpectedly null.")); + return; + } + const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + std::optional output = api->RemoveService(handle_args_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.clearServices", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->ClearServices(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.startAdvertising", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_advertisement_args_arg = args.at(0); + if (encodable_advertisement_args_arg.IsNull()) { + reply(WrapError("advertisement_args_arg unexpectedly null.")); + return; + } + const auto& advertisement_args_arg = std::any_cast(std::get(encodable_advertisement_args_arg)); + api->StartAdvertising(advertisement_args_arg, [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.stopAdvertising", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + std::optional output = api->StopAdvertising(); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.sendReadCharacteristicReply", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + const auto& encodable_handle_args_arg = args.at(1); + if (encodable_handle_args_arg.IsNull()) { + reply(WrapError("handle_args_arg unexpectedly null.")); + return; + } + const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + const auto& encodable_status_args_arg = args.at(2); + if (encodable_status_args_arg.IsNull()) { + reply(WrapError("status_args_arg unexpectedly null.")); + return; + } + const auto& status_args_arg = std::get(encodable_status_args_arg); + const auto& encodable_value_args_arg = args.at(3); + if (encodable_value_args_arg.IsNull()) { + reply(WrapError("value_args_arg unexpectedly null.")); + return; + } + const auto& value_args_arg = std::get>(encodable_value_args_arg); + std::optional output = api->SendReadCharacteristicReply(address_args_arg, handle_args_arg, status_args_arg, value_args_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.sendWriteCharacteristicReply", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + const auto& encodable_handle_args_arg = args.at(1); + if (encodable_handle_args_arg.IsNull()) { + reply(WrapError("handle_args_arg unexpectedly null.")); + return; + } + const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + const auto& encodable_status_args_arg = args.at(2); + if (encodable_status_args_arg.IsNull()) { + reply(WrapError("status_args_arg unexpectedly null.")); + return; + } + const auto& status_args_arg = std::get(encodable_status_args_arg); + std::optional output = api->SendWriteCharacteristicReply(address_args_arg, handle_args_arg, status_args_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.notifyCharacteristic", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_address_args_arg = args.at(0); + if (encodable_address_args_arg.IsNull()) { + reply(WrapError("address_args_arg unexpectedly null.")); + return; + } + const int64_t address_args_arg = encodable_address_args_arg.LongValue(); + const auto& encodable_handle_args_arg = args.at(1); + if (encodable_handle_args_arg.IsNull()) { + reply(WrapError("handle_args_arg unexpectedly null.")); + return; + } + const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + const auto& encodable_value_args_arg = args.at(2); + if (encodable_value_args_arg.IsNull()) { + reply(WrapError("value_args_arg unexpectedly null.")); + return; + } + const auto& value_args_arg = std::get>(encodable_value_args_arg); + api->NotifyCharacteristic(address_args_arg, handle_args_arg, value_args_arg, [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); + }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } +} + +EncodableValue MyPeripheralManagerHostApi::WrapError(std::string_view error_message) { + return EncodableValue(EncodableList{ + EncodableValue(std::string(error_message)), + EncodableValue("Error"), + EncodableValue() + }); +} + +EncodableValue MyPeripheralManagerHostApi::WrapError(const FlutterError& error) { + return EncodableValue(EncodableList{ + EncodableValue(error.code()), + EncodableValue(error.message()), + error.details() + }); +} + + +MyPeripheralManagerFlutterApiCodecSerializer::MyPeripheralManagerFlutterApiCodecSerializer() {} + +EncodableValue MyPeripheralManagerFlutterApiCodecSerializer::ReadValueOfType( + uint8_t type, + flutter::ByteStreamReader* stream) const { + switch (type) { + case 128: + return CustomEncodableValue(MyCentralArgs::FromEncodableList(std::get(ReadValue(stream)))); + default: + return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + } +} + +void MyPeripheralManagerFlutterApiCodecSerializer::WriteValue( + const EncodableValue& value, + flutter::ByteStreamWriter* stream) const { + if (const CustomEncodableValue* custom_value = std::get_if(&value)) { + if (custom_value->type() == typeid(MyCentralArgs)) { + 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++. +MyPeripheralManagerFlutterApi::MyPeripheralManagerFlutterApi(flutter::BinaryMessenger* binary_messenger) + : binary_messenger_(binary_messenger) {} + +const flutter::StandardMessageCodec& MyPeripheralManagerFlutterApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&MyPeripheralManagerFlutterApiCodecSerializer::GetInstance()); +} + +void MyPeripheralManagerFlutterApi::OnStateChanged( + int64_t state_number_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onStateChanged"; + auto channel = std::make_unique>(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + EncodableValue(state_number_args_arg), + }); + channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + } else { + on_success(); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + +void MyPeripheralManagerFlutterApi::OnReadCharacteristicCommandReceived( + const MyCentralArgs& central_args_arg, + int64_t handle_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived"; + auto channel = std::make_unique>(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + CustomEncodableValue(central_args_arg), + EncodableValue(handle_args_arg), + }); + channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + } else { + on_success(); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + +void MyPeripheralManagerFlutterApi::OnWriteCharacteristicCommandReceived( + const MyCentralArgs& central_args_arg, + int64_t handle_args_arg, + const std::vector& value_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived"; + auto channel = std::make_unique>(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + CustomEncodableValue(central_args_arg), + EncodableValue(handle_args_arg), + EncodableValue(value_args_arg), + }); + channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + } else { + on_success(); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + +void MyPeripheralManagerFlutterApi::OnCharacteristicNotifyStateChanged( + const MyCentralArgs& central_args_arg, + int64_t handle_args_arg, + bool state_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged"; + auto channel = std::make_unique>(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + CustomEncodableValue(central_args_arg), + EncodableValue(handle_args_arg), + EncodableValue(state_args_arg), + }); + channel->Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + } else { + on_success(); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); +} + +} // namespace bluetooth_low_energy_windows diff --git a/bluetooth_low_energy_windows/windows/my_api.g.h b/bluetooth_low_energy_windows/windows/my_api.g.h new file mode 100644 index 0000000..b2a7d5f --- /dev/null +++ b/bluetooth_low_energy_windows/windows/my_api.g.h @@ -0,0 +1,588 @@ +// Autogenerated from Pigeon (v15.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#ifndef PIGEON_MY_API_G_H_ +#define PIGEON_MY_API_G_H_ +#include +#include +#include +#include + +#include +#include +#include + +namespace bluetooth_low_energy_windows { + + +// Generated class from Pigeon. + +class FlutterError { + public: + explicit FlutterError(const std::string& code) + : code_(code) {} + explicit FlutterError(const std::string& code, const std::string& message) + : code_(code), message_(message) {} + explicit FlutterError(const std::string& code, const std::string& message, const flutter::EncodableValue& details) + : code_(code), message_(message), details_(details) {} + + const std::string& code() const { return code_; } + const std::string& message() const { return message_; } + const flutter::EncodableValue& details() const { return details_; } + + private: + std::string code_; + std::string message_; + flutter::EncodableValue details_; +}; + +template class ErrorOr { + public: + ErrorOr(const T& rhs) : v_(rhs) {} + ErrorOr(const T&& rhs) : v_(std::move(rhs)) {} + ErrorOr(const FlutterError& rhs) : v_(rhs) {} + ErrorOr(const FlutterError&& rhs) : v_(std::move(rhs)) {} + + bool has_error() const { return std::holds_alternative(v_); } + const T& value() const { return std::get(v_); }; + const FlutterError& error() const { return std::get(v_); }; + + private: + friend class MyCentralManagerHostApi; + friend class MyCentralManagerFlutterApi; + friend class MyPeripheralManagerHostApi; + friend class MyPeripheralManagerFlutterApi; + ErrorOr() = default; + T TakeValue() && { return std::get(std::move(v_)); } + + std::variant v_; +}; + + +enum class MyBluetoothLowEnergyStateArgs { + unknown = 0, + disabled = 1, + off = 2, + on = 3 +}; + +enum class MyGattCharacteristicPropertyArgs { + read = 0, + write = 1, + writeWithoutResponse = 2, + notify = 3, + indicate = 4 +}; + +enum class MyGattCharacteristicWriteTypeArgs { + withResponse = 0, + withoutResponse = 1 +}; + +enum class MyGattCharacteristicNotifyStateArgs { + none = 0, + notify = 1, + indicate = 2 +}; + +// Generated class from Pigeon that represents data sent in messages. +class MyManufacturerSpecificDataArgs { + public: + // Constructs an object setting all fields. + explicit MyManufacturerSpecificDataArgs( + int64_t id_args, + const std::vector& data_args); + + int64_t id_args() const; + void set_id_args(int64_t value_arg); + + const std::vector& data_args() const; + void set_data_args(const std::vector& value_arg); + + + private: + static MyManufacturerSpecificDataArgs FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class MyAdvertisementArgs; + friend class MyCentralManagerHostApi; + friend class MyCentralManagerHostApiCodecSerializer; + friend class MyCentralManagerFlutterApi; + friend class MyCentralManagerFlutterApiCodecSerializer; + friend class MyPeripheralManagerHostApi; + friend class MyPeripheralManagerHostApiCodecSerializer; + friend class MyPeripheralManagerFlutterApi; + friend class MyPeripheralManagerFlutterApiCodecSerializer; + int64_t id_args_; + std::vector data_args_; + +}; + + +// Generated class from Pigeon that represents data sent in messages. +class MyAdvertisementArgs { + public: + // Constructs an object setting all non-nullable fields. + explicit MyAdvertisementArgs( + const flutter::EncodableList& service_u_u_i_ds_args, + const flutter::EncodableMap& service_data_args); + + // Constructs an object setting all fields. + explicit MyAdvertisementArgs( + const std::string* name_args, + const flutter::EncodableList& service_u_u_i_ds_args, + const flutter::EncodableMap& service_data_args, + const MyManufacturerSpecificDataArgs* manufacturer_specific_data_args); + + const std::string* name_args() const; + void set_name_args(const std::string_view* value_arg); + void set_name_args(std::string_view value_arg); + + const flutter::EncodableList& service_u_u_i_ds_args() const; + void set_service_u_u_i_ds_args(const flutter::EncodableList& value_arg); + + const flutter::EncodableMap& service_data_args() const; + void set_service_data_args(const flutter::EncodableMap& value_arg); + + const MyManufacturerSpecificDataArgs* manufacturer_specific_data_args() const; + void set_manufacturer_specific_data_args(const MyManufacturerSpecificDataArgs* value_arg); + void set_manufacturer_specific_data_args(const MyManufacturerSpecificDataArgs& value_arg); + + + private: + static MyAdvertisementArgs FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class MyCentralManagerHostApi; + friend class MyCentralManagerHostApiCodecSerializer; + friend class MyCentralManagerFlutterApi; + friend class MyCentralManagerFlutterApiCodecSerializer; + friend class MyPeripheralManagerHostApi; + friend class MyPeripheralManagerHostApiCodecSerializer; + friend class MyPeripheralManagerFlutterApi; + friend class MyPeripheralManagerFlutterApiCodecSerializer; + std::optional name_args_; + flutter::EncodableList service_u_u_i_ds_args_; + flutter::EncodableMap service_data_args_; + std::optional manufacturer_specific_data_args_; + +}; + + +// Generated class from Pigeon that represents data sent in messages. +class MyCentralArgs { + public: + // Constructs an object setting all fields. + explicit MyCentralArgs(int64_t address_args); + + int64_t address_args() const; + void set_address_args(int64_t value_arg); + + + private: + static MyCentralArgs FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class MyCentralManagerHostApi; + friend class MyCentralManagerHostApiCodecSerializer; + friend class MyCentralManagerFlutterApi; + friend class MyCentralManagerFlutterApiCodecSerializer; + friend class MyPeripheralManagerHostApi; + friend class MyPeripheralManagerHostApiCodecSerializer; + friend class MyPeripheralManagerFlutterApi; + friend class MyPeripheralManagerFlutterApiCodecSerializer; + int64_t address_args_; + +}; + + +// Generated class from Pigeon that represents data sent in messages. +class MyPeripheralArgs { + public: + // Constructs an object setting all fields. + explicit MyPeripheralArgs(int64_t address_args); + + int64_t address_args() const; + void set_address_args(int64_t value_arg); + + + private: + static MyPeripheralArgs FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class MyCentralManagerHostApi; + friend class MyCentralManagerHostApiCodecSerializer; + friend class MyCentralManagerFlutterApi; + friend class MyCentralManagerFlutterApiCodecSerializer; + friend class MyPeripheralManagerHostApi; + friend class MyPeripheralManagerHostApiCodecSerializer; + friend class MyPeripheralManagerFlutterApi; + friend class MyPeripheralManagerFlutterApiCodecSerializer; + int64_t address_args_; + +}; + + +// Generated class from Pigeon that represents data sent in messages. +class MyGattDescriptorArgs { + public: + // Constructs an object setting all non-nullable fields. + explicit MyGattDescriptorArgs( + int64_t handle_args, + const std::string& uuid_args); + + // Constructs an object setting all fields. + explicit MyGattDescriptorArgs( + int64_t handle_args, + const std::string& uuid_args, + const std::vector* value_args); + + int64_t handle_args() const; + void set_handle_args(int64_t value_arg); + + const std::string& uuid_args() const; + void set_uuid_args(std::string_view value_arg); + + const std::vector* value_args() const; + void set_value_args(const std::vector* value_arg); + void set_value_args(const std::vector& value_arg); + + + private: + static MyGattDescriptorArgs FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class MyCentralManagerHostApi; + friend class MyCentralManagerHostApiCodecSerializer; + friend class MyCentralManagerFlutterApi; + friend class MyCentralManagerFlutterApiCodecSerializer; + friend class MyPeripheralManagerHostApi; + friend class MyPeripheralManagerHostApiCodecSerializer; + friend class MyPeripheralManagerFlutterApi; + friend class MyPeripheralManagerFlutterApiCodecSerializer; + int64_t handle_args_; + std::string uuid_args_; + std::optional> value_args_; + +}; + + +// Generated class from Pigeon that represents data sent in messages. +class MyGattCharacteristicArgs { + public: + // Constructs an object setting all fields. + explicit MyGattCharacteristicArgs( + int64_t handle_args, + const std::string& uuid_args, + const flutter::EncodableList& property_numbers_args, + const flutter::EncodableList& descriptors_args); + + int64_t handle_args() const; + void set_handle_args(int64_t value_arg); + + const std::string& uuid_args() const; + void set_uuid_args(std::string_view value_arg); + + const flutter::EncodableList& property_numbers_args() const; + void set_property_numbers_args(const flutter::EncodableList& value_arg); + + const flutter::EncodableList& descriptors_args() const; + void set_descriptors_args(const flutter::EncodableList& value_arg); + + + private: + static MyGattCharacteristicArgs FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class MyCentralManagerHostApi; + friend class MyCentralManagerHostApiCodecSerializer; + friend class MyCentralManagerFlutterApi; + friend class MyCentralManagerFlutterApiCodecSerializer; + friend class MyPeripheralManagerHostApi; + friend class MyPeripheralManagerHostApiCodecSerializer; + friend class MyPeripheralManagerFlutterApi; + friend class MyPeripheralManagerFlutterApiCodecSerializer; + int64_t handle_args_; + std::string uuid_args_; + flutter::EncodableList property_numbers_args_; + flutter::EncodableList descriptors_args_; + +}; + + +// Generated class from Pigeon that represents data sent in messages. +class MyGattServiceArgs { + public: + // Constructs an object setting all fields. + explicit MyGattServiceArgs( + int64_t handle_args, + const std::string& uuid_args, + const flutter::EncodableList& characteristics_args); + + int64_t handle_args() const; + void set_handle_args(int64_t value_arg); + + const std::string& uuid_args() const; + void set_uuid_args(std::string_view value_arg); + + const flutter::EncodableList& characteristics_args() const; + void set_characteristics_args(const flutter::EncodableList& value_arg); + + + private: + static MyGattServiceArgs FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class MyCentralManagerHostApi; + friend class MyCentralManagerHostApiCodecSerializer; + friend class MyCentralManagerFlutterApi; + friend class MyCentralManagerFlutterApiCodecSerializer; + friend class MyPeripheralManagerHostApi; + friend class MyPeripheralManagerHostApiCodecSerializer; + friend class MyPeripheralManagerFlutterApi; + friend class MyPeripheralManagerFlutterApiCodecSerializer; + int64_t handle_args_; + std::string uuid_args_; + flutter::EncodableList characteristics_args_; + +}; + +class MyCentralManagerHostApiCodecSerializer : public flutter::StandardCodecSerializer { + public: + MyCentralManagerHostApiCodecSerializer(); + inline static MyCentralManagerHostApiCodecSerializer& GetInstance() { + static MyCentralManagerHostApiCodecSerializer 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; + +}; + +// Generated interface from Pigeon that represents a handler of messages from Flutter. +class MyCentralManagerHostApi { + public: + MyCentralManagerHostApi(const MyCentralManagerHostApi&) = delete; + MyCentralManagerHostApi& operator=(const MyCentralManagerHostApi&) = delete; + virtual ~MyCentralManagerHostApi() {} + virtual void SetUp(std::function reply)> result) = 0; + virtual std::optional StartDiscovery() = 0; + virtual std::optional StopDiscovery() = 0; + virtual void Connect( + int64_t address_args, + std::function reply)> result) = 0; + virtual std::optional Disconnect(int64_t address_args) = 0; + virtual void DiscoverServices( + int64_t address_args, + std::function reply)> result) = 0; + virtual void DiscoverCharacteristics( + int64_t address_args, + int64_t handle_args, + std::function reply)> result) = 0; + virtual void DiscoverDescriptors( + int64_t address_args, + int64_t handle_args, + std::function reply)> result) = 0; + virtual void ReadCharacteristic( + int64_t address_args, + int64_t handle_args, + std::function> reply)> result) = 0; + virtual void WriteCharacteristic( + int64_t address_args, + int64_t handle_args, + const std::vector& value_args, + int64_t type_number_args, + std::function reply)> result) = 0; + virtual void SetCharacteristicNotifyState( + int64_t address_args, + int64_t handle_args, + int64_t state_number_args, + std::function reply)> result) = 0; + virtual void ReadDescriptor( + int64_t address_args, + int64_t handle_args, + std::function> reply)> result) = 0; + virtual void WriteDescriptor( + int64_t address_args, + int64_t handle_args, + const std::vector& value_args, + std::function reply)> result) = 0; + + // The codec used by MyCentralManagerHostApi. + static const flutter::StandardMessageCodec& GetCodec(); + // Sets up an instance of `MyCentralManagerHostApi` to handle messages through the `binary_messenger`. + static void SetUp( + flutter::BinaryMessenger* binary_messenger, + MyCentralManagerHostApi* api); + static flutter::EncodableValue WrapError(std::string_view error_message); + static flutter::EncodableValue WrapError(const FlutterError& error); + + protected: + MyCentralManagerHostApi() = default; + +}; +class MyCentralManagerFlutterApiCodecSerializer : public flutter::StandardCodecSerializer { + public: + MyCentralManagerFlutterApiCodecSerializer(); + inline static MyCentralManagerFlutterApiCodecSerializer& GetInstance() { + static MyCentralManagerFlutterApiCodecSerializer 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; + +}; + +// Generated class from Pigeon that represents Flutter messages that can be called from C++. +class MyCentralManagerFlutterApi { + public: + MyCentralManagerFlutterApi(flutter::BinaryMessenger* binary_messenger); + static const flutter::StandardMessageCodec& GetCodec(); + void OnStateChanged( + int64_t state_number_args, + std::function&& on_success, + std::function&& on_error); + void OnDiscovered( + const MyPeripheralArgs& peripheral_args, + int64_t rssi_args, + const MyAdvertisementArgs& advertisement_args, + std::function&& on_success, + std::function&& on_error); + void OnConnectionStateChanged( + int64_t address_args, + bool state_args, + std::function&& on_success, + std::function&& on_error); + void OnCharacteristicNotified( + int64_t address_args, + int64_t handle_args, + const std::vector& value_args, + std::function&& on_success, + std::function&& on_error); + + private: + flutter::BinaryMessenger* binary_messenger_; +}; + +class MyPeripheralManagerHostApiCodecSerializer : public flutter::StandardCodecSerializer { + public: + MyPeripheralManagerHostApiCodecSerializer(); + inline static MyPeripheralManagerHostApiCodecSerializer& GetInstance() { + static MyPeripheralManagerHostApiCodecSerializer 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; + +}; + +// Generated interface from Pigeon that represents a handler of messages from Flutter. +class MyPeripheralManagerHostApi { + public: + MyPeripheralManagerHostApi(const MyPeripheralManagerHostApi&) = delete; + MyPeripheralManagerHostApi& operator=(const MyPeripheralManagerHostApi&) = delete; + virtual ~MyPeripheralManagerHostApi() {} + virtual void SetUp(std::function reply)> result) = 0; + virtual void AddService( + const MyGattServiceArgs& service_args, + std::function reply)> result) = 0; + virtual std::optional RemoveService(int64_t handle_args) = 0; + virtual std::optional ClearServices() = 0; + virtual void StartAdvertising( + const MyAdvertisementArgs& advertisement_args, + std::function reply)> result) = 0; + virtual std::optional StopAdvertising() = 0; + virtual std::optional SendReadCharacteristicReply( + int64_t address_args, + int64_t handle_args, + bool status_args, + const std::vector& value_args) = 0; + virtual std::optional SendWriteCharacteristicReply( + int64_t address_args, + int64_t handle_args, + bool status_args) = 0; + virtual void NotifyCharacteristic( + int64_t address_args, + int64_t handle_args, + const std::vector& value_args, + std::function reply)> result) = 0; + + // The codec used by MyPeripheralManagerHostApi. + static const flutter::StandardMessageCodec& GetCodec(); + // Sets up an instance of `MyPeripheralManagerHostApi` to handle messages through the `binary_messenger`. + static void SetUp( + flutter::BinaryMessenger* binary_messenger, + MyPeripheralManagerHostApi* api); + static flutter::EncodableValue WrapError(std::string_view error_message); + static flutter::EncodableValue WrapError(const FlutterError& error); + + protected: + MyPeripheralManagerHostApi() = default; + +}; +class MyPeripheralManagerFlutterApiCodecSerializer : public flutter::StandardCodecSerializer { + public: + MyPeripheralManagerFlutterApiCodecSerializer(); + inline static MyPeripheralManagerFlutterApiCodecSerializer& GetInstance() { + static MyPeripheralManagerFlutterApiCodecSerializer 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; + +}; + +// Generated class from Pigeon that represents Flutter messages that can be called from C++. +class MyPeripheralManagerFlutterApi { + public: + MyPeripheralManagerFlutterApi(flutter::BinaryMessenger* binary_messenger); + static const flutter::StandardMessageCodec& GetCodec(); + void OnStateChanged( + int64_t state_number_args, + std::function&& on_success, + std::function&& on_error); + void OnReadCharacteristicCommandReceived( + const MyCentralArgs& central_args, + int64_t handle_args, + std::function&& on_success, + std::function&& on_error); + void OnWriteCharacteristicCommandReceived( + const MyCentralArgs& central_args, + int64_t handle_args, + const std::vector& value_args, + std::function&& on_success, + std::function&& on_error); + void OnCharacteristicNotifyStateChanged( + const MyCentralArgs& central_args, + int64_t handle_args, + bool state_args, + std::function&& on_success, + std::function&& on_error); + + private: + flutter::BinaryMessenger* binary_messenger_; +}; + +} // namespace bluetooth_low_energy_windows +#endif // PIGEON_MY_API_G_H_ diff --git a/bluetooth_low_energy_windows/windows/my_central_manager.cpp b/bluetooth_low_energy_windows/windows/my_central_manager.cpp new file mode 100644 index 0000000..94b3a57 --- /dev/null +++ b/bluetooth_low_energy_windows/windows/my_central_manager.cpp @@ -0,0 +1,871 @@ +// This must be included before many other Windows headers. +#include + +#include +#include + +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/Windows.Security.Cryptography.h" +#include "winrt/Windows.Storage.Streams.h" + +#include "my_central_manager.h" +#include "my_exception.h" + +namespace bluetooth_low_energy_windows +{ + MyCentralManager::MyCentralManager(flutter::BinaryMessenger* messenger) + { + m_api = MyCentralManagerFlutterApi(messenger); + m_watcher = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher(); + } + + MyCentralManager::~MyCentralManager() + { + } + + void MyCentralManager::SetUp(std::functionreply)> result) + { + m_set_up(std::move(result)); + } + + std::optional MyCentralManager::StartDiscovery() + { + m_watcher->Start(); + return std::nullopt; + } + + std::optional MyCentralManager::StopDiscovery() + { + m_watcher->Stop(); + return std::nullopt; + } + + void MyCentralManager::Connect(int64_t address_args, std::functionreply)> result) + { + m_connect(address_args, std::move(result)); + } + + std::optional MyCentralManager::Disconnect(int64_t address_args) + { + try + { + m_clear_device(address_args); + m_api->OnConnectionStateChanged(address_args, false, [] {}, [](auto error) {}); + return std::nullopt; + } + catch (const std::exception& ex) + { + const auto code = "std::exception"; + const auto message = ex.what(); + return FlutterError(code, message); + } + catch (...) { + const auto code = "unhandled exception"; + const auto message = "Disconnect failed with unhandled exception."; + return FlutterError(code, message); + } + } + + void MyCentralManager::DiscoverServices(int64_t address_args, std::functionreply)> result) + { + m_discover_services(address_args, std::move(result)); + } + + void MyCentralManager::DiscoverCharacteristics(int64_t address_args, int64_t handle_args, std::functionreply)> result) + { + m_discover_characteristics(address_args, handle_args, std::move(result)); + } + + void MyCentralManager::DiscoverDescriptors(int64_t address_args, int64_t handle_args, std::functionreply)> result) + { + m_discover_descriptors(address_args, handle_args, std::move(result)); + } + + void MyCentralManager::ReadCharacteristic(int64_t address_args, int64_t handle_args, std::function>reply)> result) + { + m_read_characteristic(address_args, handle_args, std::move(result)); + } + + void MyCentralManager::WriteCharacteristic(int64_t address_args, int64_t handle_args, const std::vector& value_args, int64_t type_number_args, std::functionreply)> result) + { + m_write_characteristic(address_args, handle_args, value_args, type_number_args, std::move(result)); + } + + void MyCentralManager::SetCharacteristicNotifyState(int64_t address_args, int64_t handle_args, int64_t state_number_args, std::functionreply)> result) + { + m_set_characteristic_notify_state(address_args, handle_args, state_number_args, std::move(result)); + } + + void MyCentralManager::ReadDescriptor(int64_t address_args, int64_t handle_args, std::function>reply)> result) + { + m_read_descriptor(address_args, handle_args, std::move(result)); + } + + void MyCentralManager::WriteDescriptor(int64_t address_args, int64_t handle_args, const std::vector& value_args, std::functionreply)> result) + { + m_write_descriptor(address_args, handle_args, value_args, std::move(result)); + } + + winrt::fire_and_forget MyCentralManager::m_set_up(std::functionreply)> result) + { + try + { + m_clear_state(); + // 获取状态 + if (!m_radio_state_changed_revoker) { + m_adapter = co_await winrt::Windows::Devices::Bluetooth::BluetoothAdapter::GetDefaultAsync(); + m_radio = co_await m_adapter->GetRadioAsync(); + m_radio_state_changed_revoker = m_radio->StateChanged(winrt::auto_revoke, [this](winrt::Windows::Devices::Radios::Radio radio, auto obj) + { + m_on_state_changed(); + }); + } + m_on_state_changed(); + if (!m_watcher_received_revoker) + { + m_watcher_received_revoker = m_watcher->Received(winrt::auto_revoke, [this](winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher watcher, winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementReceivedEventArgs event_args) + { + const auto type = event_args.AdvertisementType(); + // TODO: 支持扫描响应和扫描扩展 + if (type == winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementType::ScanResponse || + type == winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementType::Extended) + { + return; + } + const auto address = event_args.BluetoothAddress(); + const auto peripheral_args = m_address_to_peripheral_args(address); + const auto rssi = event_args.RawSignalStrengthInDBm(); + const auto rssi_args = static_cast(rssi); + const auto advertisement = event_args.Advertisement(); + const auto advertisement_args = m_advertisement_to_args(advertisement); + m_api->OnDiscovered(peripheral_args, rssi_args, advertisement_args, [] {}, [](auto error) {}); + }); + } + result(std::nullopt); + } + catch (const winrt::hresult_error& ex) { + const auto code = "winrt::hresult_error"; + const auto winrt_message = ex.message(); + const auto message = winrt::to_string(winrt_message); + const auto error = FlutterError(code, message); + result(error); + } + catch (const std::exception& ex) + { + const auto code = "std::exception"; + const auto message = ex.what(); + const auto error = FlutterError(code, message); + result(error); + } + catch (...) { + const auto code = "unhandled exception"; + const auto message = "Set up failed with unhandled exception."; + const auto error = FlutterError(code, message); + result(error); + } + } + + winrt::fire_and_forget MyCentralManager::m_connect(int64_t address_args, std::function reply)> result) + { + try + { + const auto address = static_cast(address_args); + const auto& device = co_await winrt::Windows::Devices::Bluetooth::BluetoothLEDevice::FromBluetoothAddressAsync(address); + // 通过单独调用此方法创建 BluetoothLEDevice 对象不(一定)会启动连接。 若要启动连接,请将 + // GattSession.MaintainConnection 设置为 true,或在 BluetoothLEDevice 上调用未缓存 + // 的服务发现方法,或对设备执行读/写操作。 + // 参考:https://learn.microsoft.com/zh-cn/windows/uwp/devices-sensors/gatt-client#connecting-to-the-device + const auto& r = co_await device.GetGattServicesAsync(winrt::Windows::Devices::Bluetooth::BluetoothCacheMode::Uncached); + const auto r_status = r.Status(); + if (r_status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) + { + const auto r_status_code = static_cast(r_status); + const auto message = "Connect failed with status: " + std::to_string(r_status_code); + throw MyException(message); + } + m_devices[address_args] = device; + m_api->OnConnectionStateChanged(address_args, true, [] {}, [](auto error) {}); + m_device_connection_status_changed_revokers[address_args] = device.ConnectionStatusChanged(winrt::auto_revoke, [this](winrt::Windows::Devices::Bluetooth::BluetoothLEDevice device, auto obj) + { + const auto address = device.BluetoothAddress(); + const auto status = device.ConnectionStatus(); + const auto address_args = static_cast(address); + const auto state_args = status == winrt::Windows::Devices::Bluetooth::BluetoothConnectionStatus::Connected; + m_api->OnConnectionStateChanged(address_args, state_args, [] {}, [](auto error) {}); + if (status == winrt::Windows::Devices::Bluetooth::BluetoothConnectionStatus::Disconnected) + { + m_clear_device(address_args); + } + }); + result(std::nullopt); + } + catch (const winrt::hresult_error& ex) { + const auto code = "winrt::hresult_error"; + const auto winrt_message = ex.message(); + const auto message = winrt::to_string(winrt_message); + const auto error = FlutterError(code, message); + result(error); + } + catch (const std::exception& ex) + { + const auto code = "std::exception"; + const auto message = ex.what(); + const auto error = FlutterError(code, message); + result(error); + } + catch (...) { + const auto code = "unhandled exception"; + const auto message = "Connect failed with unhandled exception."; + const auto error = FlutterError(code, message); + result(error); + } + } + + winrt::fire_and_forget MyCentralManager::m_discover_services(int64_t address_args, std::functionreply)> result) + { + try + { + const auto& device = m_devices[address_args]; + const auto& r = co_await device->GetGattServicesAsync(winrt::Windows::Devices::Bluetooth::BluetoothCacheMode::Uncached); + const auto status = r.Status(); + if (status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) + { + const auto status_code = static_cast(status); + const auto message = "Discover services failed with status: " + std::to_string(status_code); + throw MyException(message); + } + // 检查设备是否已经释放 + const auto disposed = !m_devices[address_args]; + if (disposed) + { + const auto message = "Discover services failed as the device has disposed"; + throw MyException(message); + } + const auto& services_view = r.Services(); + auto& services = m_services[address_args]; + auto services_args = flutter::EncodableList(); + for (const auto& service : services_view) { + const auto& service_args_value = m_service_to_args(service); + const auto service_args = flutter::CustomEncodableValue(service_args_value); + const auto service_handle_args = service_args_value.handle_args(); + services[service_handle_args] = service; + services_args.emplace_back(service_args); + } + result(services_args); + } + catch (const winrt::hresult_error& ex) { + const auto code = "winrt::hresult_error"; + const auto winrt_message = ex.message(); + const auto message = winrt::to_string(winrt_message); + const auto error = FlutterError(code, message); + result(error); + } + catch (const std::exception& ex) + { + const auto code = "std::exception"; + const auto message = ex.what(); + const auto error = FlutterError(code, message); + result(error); + } + catch (...) { + const auto code = "unhandled exception"; + const auto message = "Discover services failed with unhandled exception."; + const auto error = FlutterError(code, message); + result(error); + } + } + + winrt::fire_and_forget MyCentralManager::m_discover_characteristics(int64_t address_args, int64_t handle_args, std::functionreply)> result) + { + try + { + const auto& service = m_retrieve_service(address_args, handle_args); + const auto& r = co_await service->GetCharacteristicsAsync(); + const auto status = r.Status(); + if (status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) + { + const auto status_code = static_cast(status); + const auto message = "Discover characteristics failed with status: " + std::to_string(status_code); + throw MyException(message); + } + // 检查设备是否已经释放 + const auto disposed = !m_devices[address_args]; + if (disposed) + { + const auto message = "Discover characteristics failed as the device has disposed"; + throw MyException(message); + } + const auto& characteristics_view = r.Characteristics(); + auto& characteristics = m_characteristics[address_args]; + auto characteristics_args = flutter::EncodableList(); + auto& revokers = m_characteristic_value_changed_revokers[address_args]; + for (const auto& characteristic : characteristics_view) { + const auto& characteristic_args_value = m_characteristic_to_args(characteristic); + const auto characteristic_args = flutter::CustomEncodableValue(characteristic_args_value); + const auto characteristic_handle_args = characteristic_args_value.handle_args(); + characteristics[characteristic_handle_args] = characteristic; + revokers[characteristic_handle_args] = characteristic.ValueChanged(winrt::auto_revoke, [this, address_args](const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristic& characteristic, const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattValueChangedEventArgs& event_args) + { + const auto handle = characteristic.AttributeHandle(); + const auto handle_args = static_cast(handle); + const auto& value = event_args.CharacteristicValue(); + const auto& begin = value.data(); + const auto& end = begin + value.Length(); + const auto value_args = std::vector(begin, end); + m_api->OnCharacteristicNotified(address_args, handle_args, value_args, []() {}, [](const auto& error) {}); + }); + characteristics_args.emplace_back(characteristic_args); + } + result(characteristics_args); + } + catch (const winrt::hresult_error& ex) { + const auto code = "winrt::hresult_error"; + const auto winrt_message = ex.message(); + const auto message = winrt::to_string(winrt_message); + const auto error = FlutterError(code, message); + result(error); + } + catch (const std::exception& ex) + { + const auto code = "std::exception"; + const auto message = ex.what(); + const auto error = FlutterError(code, message); + result(error); + } + catch (...) { + const auto code = "unhandled exception"; + const auto message = "Discover characteristics failed with unhandled exception."; + const auto error = FlutterError(code, message); + result(error); + } + } + + winrt::fire_and_forget MyCentralManager::m_discover_descriptors(int64_t address_args, int64_t handle_args, std::functionreply)> result) + { + try + { + const auto& characteristic = m_retrieve_characteristic(address_args, handle_args); + const auto& r = co_await characteristic->GetDescriptorsAsync(); + const auto status = r.Status(); + if (status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) + { + const auto status_code = static_cast(status); + const auto message = "Discover descriptors failed with status: " + std::to_string(status_code); + throw MyException(message); + } + // 检查设备是否已经释放 + const auto disposed = !m_devices[address_args]; + if (disposed) + { + const auto message = "Discover descriptors failed as the device has disposed"; + throw MyException(message); + } + const auto& descriptors_view = r.Descriptors(); + auto& descriptors = m_descriptors[address_args]; + auto descriptors_args = flutter::EncodableList(); + for (const auto& descriptor : descriptors_view) { + const auto& descriptor_args_value = m_descriptor_to_args(descriptor); + const auto descriptor_args = flutter::CustomEncodableValue(descriptor_args_value); + const auto descriptor_handle_args = descriptor_args_value.handle_args(); + descriptors[descriptor_handle_args] = descriptor; + descriptors_args.emplace_back(descriptor_args); + } + result(descriptors_args); + } + catch (const winrt::hresult_error& ex) { + const auto code = "winrt::hresult_error"; + const auto winrt_message = ex.message(); + const auto message = winrt::to_string(winrt_message); + const auto error = FlutterError(code, message); + result(error); + } + catch (const std::exception& ex) + { + const auto code = "std::exception"; + const auto message = ex.what(); + const auto error = FlutterError(code, message); + result(error); + } + catch (...) { + const auto code = "unhandled exception"; + const auto message = "Discover descriptors failed with unhandled exception."; + const auto error = FlutterError(code, message); + result(error); + } + } + + winrt::fire_and_forget MyCentralManager::m_read_characteristic(int64_t address_args, int64_t handle_args, std::function>reply)> result) + { + try + { + const auto& characteristic = m_retrieve_characteristic(address_args, handle_args); + const auto& r = co_await characteristic->ReadValueAsync(); + const auto status = r.Status(); + if (status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) + { + const auto status_code = static_cast(status); + const auto message = "Read characteristic failed with status: " + std::to_string(status_code); + throw MyException(message); + } + const auto& value = r.Value(); + const auto& begin = value.data(); + const auto& end = begin + value.Length(); + const auto value_args = std::vector(begin, end); + result(value_args); + } + catch (const winrt::hresult_error& ex) { + const auto code = "winrt::hresult_error"; + const auto winrt_message = ex.message(); + const auto message = winrt::to_string(winrt_message); + const auto error = FlutterError(code, message); + result(error); + } + catch (const std::exception& ex) + { + const auto code = "std::exception"; + const auto message = ex.what(); + const auto error = FlutterError(code, message); + result(error); + } + catch (...) { + const auto code = "unhandled exception"; + const auto message = "Read characteristic failed with unhandled exception."; + const auto error = FlutterError(code, message); + result(error); + } + } + + winrt::fire_and_forget MyCentralManager::m_write_characteristic(int64_t address_args, int64_t handle_args, const std::vector& value_args, int64_t type_number_args, std::functionreply)> result) + { + try + { + const auto& characteristic = m_retrieve_characteristic(address_args, handle_args); + const auto value = winrt::Windows::Security::Cryptography::CryptographicBuffer::CreateFromByteArray(value_args); + const auto option = m_write_type_number_args_to_write_option(type_number_args); + const auto& status = co_await characteristic->WriteValueAsync(value, option); + if (status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) + { + const auto status_code = static_cast(status); + const auto message = "Write characteristic failed with status: " + std::to_string(status_code); + throw MyException(message); + } + result(std::nullopt); + } + catch (const winrt::hresult_error& ex) { + const auto code = "winrt::hresult_error"; + const auto winrt_message = ex.message(); + const auto message = winrt::to_string(winrt_message); + const auto error = FlutterError(code, message); + result(error); + } + catch (const std::exception& ex) + { + const auto code = "std::exception"; + const auto message = ex.what(); + const auto error = FlutterError(code, message); + result(error); + } + catch (...) { + const auto code = "unhandled exception"; + const auto message = "Write characteristic failed with unhandled exception."; + const auto error = FlutterError(code, message); + result(error); + } + } + + winrt::fire_and_forget MyCentralManager::m_set_characteristic_notify_state(int64_t address_args, int64_t handle_args, int64_t state_number_args, std::functionreply)> result) + { + try + { + const auto& characteristic = m_retrieve_characteristic(address_args, handle_args); + const auto value = m_notify_state_number_args_to_cccd_value(state_number_args); + const auto& status = co_await characteristic->WriteClientCharacteristicConfigurationDescriptorAsync(value); + if (status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) + { + const auto status_code = static_cast(status); + const auto message = "Notify characteristic failed with status: " + std::to_string(status_code); + throw MyException(message); + } + result(std::nullopt); + } + catch (const winrt::hresult_error& ex) { + const auto code = "winrt::hresult_error"; + const auto winrt_message = ex.message(); + const auto message = winrt::to_string(winrt_message); + const auto error = FlutterError(code, message); + result(error); + } + catch (const std::exception& ex) + { + const auto code = "std::exception"; + const auto message = ex.what(); + const auto error = FlutterError(code, message); + result(error); + } + catch (...) { + const auto code = "unhandled exception"; + const auto message = "Notify characteristic failed with unhandled exception."; + const auto error = FlutterError(code, message); + result(error); + } + } + + winrt::fire_and_forget MyCentralManager::m_read_descriptor(int64_t address_args, int64_t handle_args, std::function>reply)> result) + { + try + { + const auto& descriptor = m_retrieve_descriptor(address_args, handle_args); + const auto& r = co_await descriptor->ReadValueAsync(); + const auto status = r.Status(); + if (status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) + { + const auto status_code = static_cast(status); + const auto message = "Read descriptor failed with status: " + std::to_string(status_code); + throw MyException(message); + } + const auto& value = r.Value(); + const auto& begin = value.data(); + const auto& end = begin + value.Length(); + const auto value_args = std::vector(begin, end); + result(value_args); + } + catch (const winrt::hresult_error& ex) { + const auto code = "winrt::hresult_error"; + const auto winrt_message = ex.message(); + const auto message = winrt::to_string(winrt_message); + const auto error = FlutterError(code, message); + result(error); + } + catch (const std::exception& ex) + { + const auto code = "std::exception"; + const auto message = ex.what(); + const auto error = FlutterError(code, message); + result(error); + } + catch (...) { + const auto code = "unhandled exception"; + const auto message = "Read descriptor failed with unhandled exception."; + const auto error = FlutterError(code, message); + result(error); + } + } + + winrt::fire_and_forget MyCentralManager::m_write_descriptor(int64_t address_args, int64_t handle_args, const std::vector& value_args, std::functionreply)> result) + { + try + { + const auto& descriptor = m_retrieve_descriptor(address_args, handle_args); + const auto value = winrt::Windows::Security::Cryptography::CryptographicBuffer::CreateFromByteArray(value_args); + const auto& status = co_await descriptor->WriteValueAsync(value); + if (status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) + { + const auto status_code = static_cast(status); + const auto message = "Write characteristic failed with status: " + std::to_string(status_code); + throw MyException(message); + } + result(std::nullopt); + } + catch (const winrt::hresult_error& ex) { + const auto code = "winrt::hresult_error"; + const auto winrt_message = ex.message(); + const auto message = winrt::to_string(winrt_message); + const auto error = FlutterError(code, message); + result(error); + } + catch (const std::exception& ex) + { + const auto code = "std::exception"; + const auto message = ex.what(); + const auto error = FlutterError(code, message); + result(error); + } + catch (...) { + const auto code = "unhandled exception"; + const auto message = "Write descriptor failed with unhandled exception."; + const auto error = FlutterError(code, message); + result(error); + } + } + + void MyCentralManager::m_clear_state() + { + // 停止扫描 + const auto status = m_watcher->Status(); + if (status == winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcherStatus::Started) + { + m_watcher->Stop(); + } + // 断开连接 + auto addresses = std::list(); + const auto begin = m_devices.begin(); + const auto end = m_devices.end(); + std::transform( + begin, + end, + std::back_inserter(addresses), + [](const auto& device) + { + return device.first; + }); + for (const auto& address : addresses) { + const auto address_args = static_cast(address); + m_clear_device(address_args); + } + } + + void MyCentralManager::m_clear_device(int64_t address_args) + { + // 通过释放连接实例,触发断开连接 + m_device_connection_status_changed_revokers.erase(address_args); + m_characteristic_value_changed_revokers.erase(address_args); + m_devices.erase(address_args); + m_services.erase(address_args); + m_characteristics.erase(address_args); + m_descriptors.erase(address_args); + } + + void MyCentralManager::m_on_state_changed() + { + auto radio_state = m_radio->State(); + const auto state_args = m_radio_state_to_args(radio_state); + const auto state_number_args = static_cast(state_args); + m_api->OnStateChanged(state_number_args, [] {}, [](auto error) {}); + } + + MyBluetoothLowEnergyStateArgs MyCentralManager::m_radio_state_to_args(winrt::Windows::Devices::Radios::RadioState state) + { + switch (state) + { + case winrt::Windows::Devices::Radios::RadioState::Unknown: + return MyBluetoothLowEnergyStateArgs::unknown; + case winrt::Windows::Devices::Radios::RadioState::Disabled: + return MyBluetoothLowEnergyStateArgs::disabled; + case winrt::Windows::Devices::Radios::RadioState::Off: + return MyBluetoothLowEnergyStateArgs::off; + case winrt::Windows::Devices::Radios::RadioState::On: + return MyBluetoothLowEnergyStateArgs::on; + default: + return MyBluetoothLowEnergyStateArgs::unknown; + } + } + + MyAdvertisementArgs MyCentralManager::m_advertisement_to_args(const winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisement& advertisement) + { + const auto name = advertisement.LocalName(); + const auto name_args = to_string(name); + const auto& service_uuids = advertisement.ServiceUuids(); + auto service_uuids_args = flutter::EncodableList(); + for (const auto& uuid : service_uuids) { + const auto uuid_args = m_uuid_to_args(uuid); + service_uuids_args.emplace_back(uuid_args); + } + const auto& data_sections = advertisement.DataSections(); + auto service_data_args = flutter::EncodableMap(); + for (const auto& data_section : data_sections) + { + const auto section_type = data_section.DataType(); + const auto& section_buffer = data_section.Data(); + const auto section_data_length = section_buffer.Length(); + const auto type16 = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementDataTypes::ServiceData16BitUuids(); + const auto type32 = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementDataTypes::ServiceData32BitUuids(); + const auto type128 = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementDataTypes::ServiceData128BitUuids(); + if (section_type == type16 && section_data_length > 2) + { + const auto& section_data = section_buffer.data(); + auto data1 = uint16_t(); + std::memcpy(&data1, section_data, 2Ui64); + const auto uuid_args = std::format("{:04X}", data1); + const auto data_args = std::vector(section_data + 2, section_data + section_data_length); + service_data_args[uuid_args] = data_args; + } + else if (section_type == type32 && section_data_length > 4) + { + const auto& section_data = section_buffer.data(); + auto data1 = uint32_t(); + std::memcpy(&data1, section_data, 4Ui64); + const auto uuid_args = std::format("{:08X}", data1); + const auto data_args = std::vector(section_data + 4, section_data + section_data_length); + service_data_args[uuid_args] = data_args; + } + else if (section_type == type128 && section_data_length > 16) + { + const auto& section_data = section_buffer.data(); + auto data1 = uint32_t(); + std::memcpy(&data1, section_data, 4Ui64); + auto data2 = uint16_t(); + std::memcpy(&data2, section_data + 4, 2Ui64); + auto data3 = uint16_t(); + std::memcpy(&data3, section_data + 6, 2Ui64); + auto data4 = std::array(); + std::memcpy(&data4, section_data + 8, 8Ui64); + const auto uuid = winrt::guid(data1, data2, data3, data4); + const auto uuid_args = m_uuid_to_args(uuid); + const auto data_args = std::vector(section_data + 16, section_data + section_data_length); + service_data_args[uuid_args] = data_args; + } + } + const auto& manufacturer_data = advertisement.ManufacturerData(); + const auto manufacturer_data_size = manufacturer_data.Size(); + if (manufacturer_data_size > 0) + { + const auto& last_manufacturer_data = manufacturer_data.GetAt(manufacturer_data_size - 1); + const auto id = last_manufacturer_data.CompanyId(); + const auto id_args = static_cast(id); + const auto& data = last_manufacturer_data.Data(); + const auto begin = data.data(); + const auto end = begin + data.Length(); + const auto data_args = std::vector(begin, end); + const auto manufacturer_specific_data_args = MyManufacturerSpecificDataArgs(id_args, data_args); + return MyAdvertisementArgs(&name_args, service_uuids_args, service_data_args, &manufacturer_specific_data_args); + } + else + { + return MyAdvertisementArgs(&name_args, service_uuids_args, service_data_args, nullptr); + } + } + + MyPeripheralArgs MyCentralManager::m_address_to_peripheral_args(uint64_t address) + { + const auto address_args = static_cast(address); + return MyPeripheralArgs(address_args); + } + + MyGattServiceArgs MyCentralManager::m_service_to_args(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDeviceService& service) + { + const auto handle = service.AttributeHandle(); + const auto handle_args = static_cast(handle); + const auto uuid = service.Uuid(); + const auto uuid_args = m_uuid_to_args(uuid); + const auto characteristics_args = flutter::EncodableList(); + return MyGattServiceArgs(handle_args, uuid_args, characteristics_args); + } + + MyGattCharacteristicArgs MyCentralManager::m_characteristic_to_args(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristic& characteristic) + { + const auto handle = characteristic.AttributeHandle(); + const auto handle_args = static_cast(handle); + const auto& uuid = characteristic.Uuid(); + const auto uuid_args = m_uuid_to_args(uuid); + const auto properties = characteristic.CharacteristicProperties(); + const auto property_numbers_args = m_characteristic_properties_to_args(properties); + const auto descriptors_args = flutter::EncodableList(); + return MyGattCharacteristicArgs(handle_args, uuid_args, property_numbers_args, descriptors_args); + } + + flutter::EncodableList MyCentralManager::m_characteristic_properties_to_args(winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties properties) + { + const auto readable = static_cast(properties & winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Read); + const auto writable = static_cast(properties & winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Write); + const auto writableWithoutResponse = static_cast(properties & winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::WriteWithoutResponse); + const auto notifiable = static_cast(properties & winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Notify); + const auto indicatable = static_cast(properties & winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Indicate); + auto property_numbers_args = flutter::EncodableList(); + if (readable) + { + const auto property_number_args = static_cast(MyGattCharacteristicPropertyArgs::read); + property_numbers_args.emplace_back(property_number_args); + } + if (writable) + { + const auto property_number_args = static_cast(MyGattCharacteristicPropertyArgs::write); + property_numbers_args.emplace_back(property_number_args); + } + if (writableWithoutResponse) + { + const auto property_number_args = static_cast(MyGattCharacteristicPropertyArgs::writeWithoutResponse); + property_numbers_args.emplace_back(property_number_args); + } + if (notifiable) + { + const auto property_number_args = static_cast(MyGattCharacteristicPropertyArgs::notify); + property_numbers_args.emplace_back(property_number_args); + } + if (indicatable) + { + const auto property_number_args = static_cast(MyGattCharacteristicPropertyArgs::indicate); + property_numbers_args.emplace_back(property_number_args); + } + return property_numbers_args; + } + + MyGattDescriptorArgs MyCentralManager::m_descriptor_to_args(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDescriptor& descriptor) + { + const auto handle = descriptor.AttributeHandle(); + const auto handle_args = static_cast(handle); + const auto& uuid = descriptor.Uuid(); + const auto uuid_args = m_uuid_to_args(uuid); + return MyGattDescriptorArgs(handle_args, uuid_args); + } + + std::string MyCentralManager::m_uuid_to_args(const winrt::guid& uuid) + { + //const auto uuid_value = winrt::to_hstring(uuid); + //return winrt::to_string(uuid_value); + return std::format("{}", uuid); + } + + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption MyCentralManager::m_write_type_number_args_to_write_option(int64_t type_number_args) + { + switch (type_number_args) + { + case static_cast(MyGattCharacteristicWriteTypeArgs::withoutResponse): + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption::WriteWithoutResponse; + case static_cast(MyGattCharacteristicWriteTypeArgs::withResponse): + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption::WriteWithResponse; + default: + throw std::bad_cast(); + } + } + + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue MyCentralManager::m_notify_state_number_args_to_cccd_value(int64_t state_number_args) + { + switch (state_number_args) + { + case static_cast(MyGattCharacteristicNotifyStateArgs::none): + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue::None; + case static_cast(MyGattCharacteristicNotifyStateArgs::notify): + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue::Notify; + case static_cast(MyGattCharacteristicNotifyStateArgs::indicate): + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue::Indicate; + default: + throw std::bad_cast(); + } + } + + std::optional MyCentralManager::m_retrieve_service(int64_t address_args, int64_t handle_args) + { + auto& services = m_services[address_args]; + return services[handle_args]; + } + + std::optional MyCentralManager::m_retrieve_characteristic(int64_t address_args, int64_t handle_args) + { + auto& characteristics = m_characteristics[address_args]; + return characteristics[handle_args]; + } + + std::optional MyCentralManager::m_retrieve_descriptor(int64_t address_args, int64_t handle_args) + { + auto& descriptors = m_descriptors[address_args]; + return descriptors[handle_args]; + } +} + +template <> +struct std::formatter : std::formatter +{ + // NOTE: the format function should be a const member function. + // see: https://developercommunity.visualstudio.com/t/standrad-formatters-should-use-const-and/1662387?q=Angular+standalone+%28esproj%29 + // see: https://developercommunity.visualstudio.com/t/Custom-std::formatter-breaks-after-upgra/10515914?space=8&ftype=problem&sort=newest&q=Suggestion&viewtype=solutions + auto format(const winrt::guid& guid, std::format_context& context) const + { + auto formatted = context.out(); + formatted = std::format_to(formatted, "{:08X}-", guid.Data1); + formatted = std::format_to(formatted, "{:04X}-", guid.Data2); + formatted = std::format_to(formatted, "{:04X}-", guid.Data3); + formatted = std::format_to(formatted, "{:02X}{:02X}-", guid.Data4[0], guid.Data4[1]); + formatted = std::format_to(formatted, "{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}", guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); + return formatted; + } +}; \ No newline at end of file diff --git a/bluetooth_low_energy_windows/windows/my_central_manager.h b/bluetooth_low_energy_windows/windows/my_central_manager.h new file mode 100644 index 0000000..52e8d7f --- /dev/null +++ b/bluetooth_low_energy_windows/windows/my_central_manager.h @@ -0,0 +1,82 @@ +#ifndef BLEW_MY_CENTRAL_MANAGER_H_ +#define BLEW_MY_CENTRAL_MANAGER_H_ + +#include "winrt/Windows.Devices.Bluetooth.h" +#include "winrt/Windows.Devices.Bluetooth.Advertisement.h" +#include "winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h" +#include "winrt/Windows.Devices.Radios.h" + +#include "my_api.g.h" + +namespace bluetooth_low_energy_windows +{ + class MyCentralManager : public MyCentralManagerHostApi + { + public: + MyCentralManager(flutter::BinaryMessenger* messenger); + virtual ~MyCentralManager(); + + // Disallow copy and assign. + MyCentralManager(const MyCentralManager&) = delete; + MyCentralManager& operator=(const MyCentralManager&) = delete; + + // 通过 MyCentralManagerHostApi 继承 + void SetUp(std::functionreply)> result) override; + std::optional StartDiscovery() override; + std::optional StopDiscovery() override; + void Connect(int64_t address_args, std::functionreply)> result) override; + std::optional Disconnect(int64_t address_args) override; + void DiscoverServices(int64_t address_args, std::functionreply)> result) override; + void DiscoverCharacteristics(int64_t address_args, int64_t handle_args, std::functionreply)> result) override; + void DiscoverDescriptors(int64_t address_args, int64_t handle_args, std::functionreply)> result) override; + void ReadCharacteristic(int64_t address_args, int64_t handle_args, std::function>reply)> result) override; + void WriteCharacteristic(int64_t address_args, int64_t handle_args, const std::vector& value_args, int64_t type_number_args, std::functionreply)> result) override; + void SetCharacteristicNotifyState(int64_t address_args, int64_t handle_args, int64_t state_number_args, std::functionreply)> result) override; + void ReadDescriptor(int64_t address_args, int64_t handle_args, std::function>reply)> result) override; + void WriteDescriptor(int64_t address_args, int64_t handle_args, const std::vector& value_args, std::functionreply)> result) override; + private: + std::optional m_api; + std::optional m_watcher; + std::optional m_adapter; + std::optional m_radio; + std::map> m_devices; + std::map>> m_services; + std::map>> m_characteristics; + std::map>> m_descriptors; + std::optional m_watcher_received_revoker; + std::optional m_radio_state_changed_revoker; + std::map> m_device_connection_status_changed_revokers; + std::map>> m_characteristic_value_changed_revokers; + + winrt::fire_and_forget m_set_up(std::functionreply)> result); + winrt::fire_and_forget m_connect(int64_t address_args, std::functionreply)> result); + winrt::fire_and_forget m_discover_services(int64_t address_args, std::function reply)> result); + winrt::fire_and_forget m_discover_characteristics(int64_t address_args, int64_t handle_args, std::function reply)> result); + winrt::fire_and_forget m_discover_descriptors(int64_t address_args, int64_t handle_args, std::function reply)> result); + winrt::fire_and_forget m_read_characteristic(int64_t address_args, int64_t handle_args, std::function> reply)> result); + winrt::fire_and_forget m_write_characteristic(int64_t address_args, int64_t handle_args, const std::vector& value_args, int64_t type_number_args, std::function reply)> result); + winrt::fire_and_forget m_set_characteristic_notify_state(int64_t address_args, int64_t handle_args, int64_t state_number_args, std::function reply)> result); + winrt::fire_and_forget m_read_descriptor(int64_t address_args, int64_t handle_args, std::function> reply)> result); + winrt::fire_and_forget m_write_descriptor(int64_t address_args, int64_t handle_args, const std::vector& value_args, std::function reply)> result); + + void m_clear_state(); + void m_clear_device(int64_t address_args); + void m_on_state_changed(); + MyBluetoothLowEnergyStateArgs m_radio_state_to_args(winrt::Windows::Devices::Radios::RadioState state); + MyAdvertisementArgs m_advertisement_to_args(const winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisement& advertisement); + MyPeripheralArgs m_address_to_peripheral_args(uint64_t address); + MyGattServiceArgs m_service_to_args(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDeviceService& service); + MyGattCharacteristicArgs m_characteristic_to_args(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristic& characteristic); + flutter::EncodableList m_characteristic_properties_to_args(winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties properties); + MyGattDescriptorArgs m_descriptor_to_args(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDescriptor& descriptor); + std::string m_uuid_to_args(const winrt::guid& uuid); + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption m_write_type_number_args_to_write_option(int64_t type_number_args); + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue m_notify_state_number_args_to_cccd_value(int64_t state_number_args); + std::optional m_retrieve_service(int64_t address_args, int64_t handle_args); + std::optional m_retrieve_characteristic(int64_t address, int64_t handle_args); + std::optional m_retrieve_descriptor(int64_t address_args, int64_t handle_args); + }; + +} + +#endif // !PIGEON_MY_CENTRAL_MANAGER_API_H_ diff --git a/bluetooth_low_energy_windows/windows/my_exception.cpp b/bluetooth_low_energy_windows/windows/my_exception.cpp new file mode 100644 index 0000000..39882aa --- /dev/null +++ b/bluetooth_low_energy_windows/windows/my_exception.cpp @@ -0,0 +1,9 @@ +#include "my_exception.h" + +namespace bluetooth_low_energy_windows +{ + const char* MyException::what() const noexcept + { + return message.c_str(); + } +} \ No newline at end of file diff --git a/bluetooth_low_energy_windows/windows/my_exception.h b/bluetooth_low_energy_windows/windows/my_exception.h new file mode 100644 index 0000000..d94043b --- /dev/null +++ b/bluetooth_low_energy_windows/windows/my_exception.h @@ -0,0 +1,22 @@ +#ifndef BLEW_MY_EXCEPTION_H_ +#define BLEW_MY_EXCEPTION_H_ + +#include +#include + +namespace bluetooth_low_energy_windows { + class MyException : public std::exception { + public: + MyException(const std::string& message) + : message(message) + { + } + + const char* what() const noexcept override; + private: + std::string message; + }; + +} + +#endif \ No newline at end of file diff --git a/bluetooth_low_energy_windows/windows/test/bluetooth_low_energy_windows_test.cpp b/bluetooth_low_energy_windows/windows/test/bluetooth_low_energy_windows_test.cpp new file mode 100644 index 0000000..439b6e3 --- /dev/null +++ b/bluetooth_low_energy_windows/windows/test/bluetooth_low_energy_windows_test.cpp @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "bluetooth_low_energy_windows.h" + +namespace bluetooth_low_energy_windows { +namespace test { + +namespace { + +using flutter::EncodableMap; +using flutter::EncodableValue; +using flutter::MethodCall; +using flutter::MethodResultFunctions; + +} // namespace + +TEST(BluetoothLowEnergyWindows, GetPlatformVersion) { + BluetoothLowEnergyWindows plugin; + // Save the reply value from the success callback. + std::string result_string; + plugin.HandleMethodCall( + MethodCall("getPlatformVersion", std::make_unique()), + std::make_unique>( + [&result_string](const EncodableValue* result) { + result_string = std::get(*result); + }, + nullptr, nullptr)); + + // Since the exact string varies by host, just ensure that it's a string + // with the expected format. + EXPECT_TRUE(result_string.rfind("Windows ", 0) == 0); +} + +} // namespace test +} // namespace bluetooth_low_energy_windows