From 108b6a804f7a1a03066e5c6bd6e2316c5a2ae895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B8=90=E6=B8=90=E8=A2=AB=E4=BD=A0=E5=90=B8=E5=BC=95?= Date: Tue, 4 Jun 2024 00:44:39 +0800 Subject: [PATCH] 6.0.0 (#74) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 调整接口 * 临时提交 * 重构 Android 平台代码 * 临时提交 * 临时提交 * Android 6.0.0-dev.0 * 临时提交 * 实现 Windows 接口 * windows-6.0.0-dev.0 * Darwin 6.0.0-dev.0 * 临时提交 * 1 * 临时提交 * 调整接口 * windows-6.0.0-dev.1 * 临时提交 * interface-6.0.0-dev.7 * interface-6.0.0-dev.8 * 临时提交 * windows-6.0.0-dev.2 * 删除多余脚本 * interface-6.0.0-dev.9 * 临时提交 * 临时提交 * interface-6.0.0-dev.10 * android-6.0.0-dev.1 * windows-6.0.0-dev.3 * 临时提交 * interface-6.0.0-dev.11 * windows-6.0.0-dev.4 * 更新 pubspec.lock * 1 * interface-6.0.0-dev.12 * interface-6.0.0-dev.13 * interface-6.0.0-dev.14 * 临时提交 * interface-6.0.0-dev.15 * 临时提交 * interface-6.0.0-dev.16 * android-6.0.0-dev.2 * 临时提交 * windows-6.0.0-dev.5 * 临时提交 * 临时提交 * windows-6.0.0-dev.6 * 优化注释和代码样式 * 优化代码 * 临时提交 * 实现 Dart 接口 * darwin-6.0.0-dev.0 * linux-6.0.0-dev.0 * 修复已知问题 * 修复问题 * 6.0.0-dev.0 * 修改包名 * 更新版本 * 移除原生部分 * 临时提交 * 修复问题 * 更新 pigeon 19.0.0 * 更新 README,添加迁移文档 * linux-6.0.0-dev.1 * 解析扫描回复和扩展广播 * 修复 googletest 版本警告问题 * Use centralArgs instead of addressArgs * interface-6.0.0-dev.18 * android-6.0.0-dev.4 * linux-6.0.0-dev.2 * windows-6.0.0-dev.8 * darwin-6.0.0-dev.2 * 6.0.0-dev.1 * Update LICENSE * clang-format * Combine ADV_IND and SCAN_RES * TEMP commit: update exampe * Adjust advertisement combine logic * Implement `MyPeripheralMananger` on Windows * Added NuGet auto download and scan for names on peripheral (#67) * fetch nuget using other technique * move FetchContent to right location in CMakeLists.txt * also added hash for googletest --------- Co-authored-by: Kevin De Keyser * Fix errors. * Check BluetoothAdapter role supported state and implement PeripheralManager on Flutter side. * Sort code * Fix known errors * interface-6.0.0-dev.19 * windows-6.0.0-dev.9 * Optimize example * android-6.0.0-dev.5 * Optimize the Adverrtisement BottomSheet. * linux-6.0.0-dev.3 * Update dependency * Fix example errors. * Temp commit. * darwin-6.0.0-dev.3 * 6.0.0-dev.2 * Update README.md * 6.0.0 * darwin-6.0.0-dev.4 * android-6.0.0-dev.6 * 6.0.0-dev.3 * Update docs. * interface-6.0.0 * android-6.0.0 * darwin-6.0.0 * linux-6.0.0 * windows-6.0.0 * 6.0.0 * Update dependency --------- Co-authored-by: Kevin De Keyser Co-authored-by: Kevin De Keyser --- .gitignore | 1 - .vscode/c_cpp_properties.json | 21 + LICENSE | 2 +- bluetooth_low_energy/.gitignore | 1 - bluetooth_low_energy/.metadata | 6 +- bluetooth_low_energy/CHANGELOG.md | 110 ++ bluetooth_low_energy/LICENSE | 2 +- bluetooth_low_energy/README.md | 106 +- .../doc/migrations/migration-v6.md | 279 +++ bluetooth_low_energy/example/.gitignore | 1 - .../example/android/app/build.gradle | 48 +- .../android/app/src/main/AndroidManifest.xml | 12 + .../MainActivity.kt | 5 + .../MainActivity.kt | 6 - .../example/android/build.gradle | 17 +- .../example/android/gradle.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/settings.gradle | 19 +- .../plugin_integration_test.dart | 9 - .../ios/Flutter/AppFrameworkInfo.plist | 2 +- bluetooth_low_energy/example/ios/Podfile | 2 +- bluetooth_low_energy/example/ios/Podfile.lock | 12 +- .../ios/Runner.xcodeproj/project.pbxproj | 158 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../example/ios/Runner/AppDelegate.swift | 2 +- .../example/ios/Runner/Info.plist | 2 +- bluetooth_low_energy/example/lib/main.dart | 1146 +------------ bluetooth_low_energy/example/lib/models.dart | 1 + .../example/lib/models/log.dart | 10 + .../example/lib/router_config.dart | 85 + .../example/lib/view_models.dart | 6 + .../central_manager_view_model.dart | 76 + .../characteristic_view_model.dart | 145 ++ .../view_models/descriptor_view_model.dart | 10 + .../peripheral_manager_view_model.dart | 174 ++ .../view_models/peripheral_view_model.dart | 88 + .../lib/view_models/service_view_model.dart | 50 + bluetooth_low_energy/example/lib/views.dart | 4 + .../example/lib/views/advertisement_view.dart | 69 + .../lib/views/central_manager_view.dart | 127 ++ .../views/characteristic_tree_node_view.dart | 18 + .../lib/views/characteristic_view.dart | 178 ++ .../lib/views/descriptor_tree_node_view.dart | 18 + .../example/lib/views/home_view.dart | 96 ++ .../example/lib/views/log_view.dart | 43 + .../lib/views/peripheral_manager_view.dart | 79 + .../example/lib/views/peripheral_view.dart | 107 ++ .../lib/views/service_tree_node_view.dart | 18 + bluetooth_low_energy/example/lib/widgets.dart | 1 + .../example/lib/widgets/rssi_indicator.dart | 23 + .../example/linux/CMakeLists.txt | 8 +- .../example/linux/my_application.cc | 20 + .../Flutter/GeneratedPluginRegistrant.swift | 2 +- .../example/macos/Podfile.lock | 6 +- .../macos/Runner.xcodeproj/project.pbxproj | 135 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../macos/Runner/Configs/AppInfo.xcconfig | 4 +- .../example/macos/Runner/Info.plist | 2 - .../macos/RunnerTests/RunnerTests.swift | 2 +- bluetooth_low_energy/example/pubspec.lock | 121 +- bluetooth_low_energy/example/pubspec.yaml | 17 +- .../example/test/widget_test.dart | 21 +- .../example/windows/CMakeLists.txt | 6 + .../flutter/generated_plugin_registrant.cc | 6 +- .../example/windows/runner/Runner.rc | 4 +- .../example/windows/runner/utils.cpp | 4 +- .../lib/bluetooth_low_energy.dart | 15 +- bluetooth_low_energy/pubspec.yaml | 29 +- .../test/bluetooth_low_energy_test.dart | 1 + bluetooth_low_energy_android/.gitignore | 1 - bluetooth_low_energy_android/.metadata | 10 +- bluetooth_low_energy_android/CHANGELOG.md | 57 + bluetooth_low_energy_android/LICENSE | 2 +- .../android/build.gradle | 34 +- .../android/src/main/AndroidManifest.xml | 22 +- .../BluetoothLowEnergyAndroidPlugin.kt} | 14 +- .../bluetooth_low_energy_android/MyAPI.g.kt} | 928 ++++++---- .../bluetooth_low_energy_android/MyAPI.kt | 258 +++ .../MyActivityResultListener.kt | 16 + .../MyAdvertiseCallback.kt | 2 +- .../MyBluetoothGattCallback.kt | 40 +- .../MyBluetoothGattServerCallback.kt | 68 +- .../MyBluetoothLowEnergyManager.kt | 46 + .../MyBroadcastReceiver.kt | 2 +- .../MyCentralManager.kt | 437 +++-- .../MyPeripheralManager.kt | 446 +++++ .../MyRequestPermissionResultListener.kt | 15 + .../MyScanCallback.kt | 2 +- .../bluetooth_low_energy_android/MyApi.kt | 270 --- .../MyBluetoothLowEnergyManager.kt | 106 -- .../MyPeripheralManager.kt | 461 ----- .../MyRequestPermissionResultListener.kt | 18 - .../BluetoothLowEnergyAndroidPluginTest.kt | 10 + .../BluetoothLowEnergyAndroidPluginTest.kt | 27 - .../example/.gitignore | 1 - .../example/README.md | 4 +- .../example/android/app/build.gradle | 48 +- .../android/app/src/main/AndroidManifest.xml | 14 +- .../MainActivity.kt | 5 + .../MainActivity.kt | 6 - .../example/android/build.gradle | 17 +- .../example/android/gradle.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../example/android/settings.gradle | 19 +- .../plugin_integration_test.dart | 9 - .../example/lib/main.dart | 1131 +----------- .../example/lib/models.dart | 1 + .../example/lib/models/log.dart | 10 + .../example/lib/router_config.dart | 85 + .../example/lib/view_models.dart | 6 + .../central_manager_view_model.dart | 74 + .../characteristic_view_model.dart | 145 ++ .../view_models/descriptor_view_model.dart | 10 + .../peripheral_manager_view_model.dart | 170 ++ .../view_models/peripheral_view_model.dart | 83 + .../lib/view_models/service_view_model.dart | 46 + .../example/lib/views.dart | 4 + .../example/lib/views/advertisement_view.dart | 69 + .../lib/views/central_manager_view.dart | 124 ++ .../views/characteristic_tree_node_view.dart | 18 + .../lib/views/characteristic_view.dart | 178 ++ .../lib/views/descriptor_tree_node_view.dart | 18 + .../example/lib/views/home_view.dart | 96 ++ .../example/lib/views/log_view.dart | 43 + .../lib/views/peripheral_manager_view.dart | 76 + .../example/lib/views/peripheral_view.dart | 107 ++ .../lib/views/service_tree_node_view.dart | 18 + .../example/lib/widgets.dart | 1 + .../example/lib/widgets/rssi_indicator.dart | 23 + .../example/pubspec.lock | 101 +- .../example/pubspec.yaml | 21 +- .../example/test/widget_test.dart | 21 +- .../lib/bluetooth_low_energy_android.dart | 6 +- .../lib/src/my_api.dart | 354 ++-- .../lib/src/my_api.g.dart | 1477 ++++++++++------ .../src/{my_central2.dart => my_central.dart} | 4 +- .../lib/src/my_central_manager.dart | 395 +++-- .../lib/src/my_gatt.dart | 95 + .../lib/src/my_gatt_characteristic2.dart | 29 - .../lib/src/my_gatt_descriptor2.dart | 22 - .../lib/src/my_gatt_service2.dart | 28 - .../lib/src/my_peripheral.dart | 11 + .../lib/src/my_peripheral2.dart | 11 - .../lib/src/my_peripheral_manager.dart | 1137 ++++++++---- bluetooth_low_energy_android/my_api.dart | 230 ++- bluetooth_low_energy_android/pubspec.yaml | 30 +- .../bluetooth_low_energy_android_test.dart | 1 + bluetooth_low_energy_darwin/.gitignore | 1 - bluetooth_low_energy_darwin/.metadata | 14 +- bluetooth_low_energy_darwin/CHANGELOG.md | 35 + bluetooth_low_energy_darwin/LICENSE | 2 +- bluetooth_low_energy_darwin/README.md | 18 +- bluetooth_low_energy_darwin/darwin/.gitignore | 38 + .../darwin/Assets/.gitkeep | 0 ...t => BluetoothLowEnergyDarwinPlugin.swift} | 6 +- .../darwin/Classes/MyAPI.g.swift | 1352 +++++++++++++++ .../Classes/{MyApi.swift => MyAPI.swift} | 113 +- .../darwin/Classes/MyApi.g.swift | 1060 ------------ .../darwin/Classes/MyCentralManager.swift | 540 +++--- .../Classes/MyCentralManagerDelegate.swift | 14 +- .../darwin/Classes/MyError.swift | 4 +- .../darwin/Classes/MyPeripheralDelegate.swift | 26 +- .../darwin/Classes/MyPeripheralManager.swift | 484 +++--- .../Classes/MyPeripheralManagerDelegate.swift | 20 +- .../bluetooth_low_energy_darwin.podspec | 16 +- .../example/.gitignore | 1 - bluetooth_low_energy_darwin/example/README.md | 4 +- .../plugin_integration_test.dart | 9 - .../ios/Flutter/AppFrameworkInfo.plist | 2 +- .../example/ios/Podfile | 2 +- .../example/ios/Podfile.lock | 12 +- .../ios/Runner.xcodeproj/project.pbxproj | 198 +-- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../example/ios/Runner/AppDelegate.swift | 2 +- .../example/ios/Runner/Info.plist | 6 +- .../example/ios/RunnerTests/RunnerTests.swift | 4 +- .../example/lib/main.dart | 1131 +----------- .../example/lib/models.dart | 1 + .../example/lib/models/log.dart | 10 + .../example/lib/router_config.dart | 85 + .../example/lib/view_models.dart | 6 + .../central_manager_view_model.dart | 71 + .../characteristic_view_model.dart | 145 ++ .../view_models/descriptor_view_model.dart | 10 + .../peripheral_manager_view_model.dart | 161 ++ .../view_models/peripheral_view_model.dart | 75 + .../lib/view_models/service_view_model.dart | 46 + .../example/lib/views.dart | 4 + .../example/lib/views/advertisement_view.dart | 69 + .../lib/views/central_manager_view.dart | 126 ++ .../views/characteristic_tree_node_view.dart | 18 + .../lib/views/characteristic_view.dart | 178 ++ .../lib/views/descriptor_tree_node_view.dart | 18 + .../example/lib/views/home_view.dart | 96 ++ .../example/lib/views/log_view.dart | 43 + .../lib/views/peripheral_manager_view.dart | 78 + .../example/lib/views/peripheral_view.dart | 107 ++ .../lib/views/service_tree_node_view.dart | 18 + .../example/lib/widgets.dart | 1 + .../example/lib/widgets/rssi_indicator.dart | 23 + .../Flutter/GeneratedPluginRegistrant.swift | 2 +- .../example/macos/Podfile.lock | 6 +- .../macos/Runner.xcodeproj/project.pbxproj | 149 +- .../xcshareddata/xcschemes/Runner.xcscheme | 10 +- .../macos/Runner/Configs/AppInfo.xcconfig | 6 +- .../example/macos/Runner/Info.plist | 2 - .../macos/RunnerTests/RunnerTests.swift | 6 +- .../example/pubspec.lock | 99 +- .../example/pubspec.yaml | 22 +- .../example/test/widget_test.dart | 21 +- .../lib/bluetooth_low_energy_darwin.dart | 6 +- .../lib/src/my_api.dart | 336 ++-- .../lib/src/my_api.g.dart | 766 ++++++--- .../lib/src/my_central_manager.dart | 372 ++-- .../lib/src/my_gatt.dart | 93 + .../lib/src/my_gatt_characteristic2.dart | 28 - .../lib/src/my_gatt_descriptor2.dart | 20 - .../lib/src/my_gatt_service2.dart | 27 - .../lib/src/my_peripheral_manager.dart | 471 ++--- bluetooth_low_energy_darwin/my_api.dart | 192 ++- bluetooth_low_energy_darwin/pubspec.yaml | 33 +- .../bluetooth_low_energy_darwin_test.dart | 1 + bluetooth_low_energy_linux/.gitignore | 1 - bluetooth_low_energy_linux/.metadata | 10 +- bluetooth_low_energy_linux/CHANGELOG.md | 24 + bluetooth_low_energy_linux/LICENSE | 2 +- bluetooth_low_energy_linux/example/.gitignore | 1 - bluetooth_low_energy_linux/example/README.md | 4 +- .../plugin_integration_test.dart | 9 - .../example/lib/main.dart | 800 +-------- .../example/lib/models.dart | 1 + .../example/lib/models/log.dart | 10 + .../example/lib/router_config.dart | 85 + .../example/lib/view_models.dart | 6 + .../central_manager_view_model.dart | 67 + .../characteristic_view_model.dart | 145 ++ .../view_models/descriptor_view_model.dart | 10 + .../peripheral_manager_view_model.dart | 162 ++ .../view_models/peripheral_view_model.dart | 75 + .../lib/view_models/service_view_model.dart | 40 + .../example/lib/views.dart | 4 + .../example/lib/views/advertisement_view.dart | 69 + .../lib/views/central_manager_view.dart | 117 ++ .../views/characteristic_tree_node_view.dart | 18 + .../lib/views/characteristic_view.dart | 178 ++ .../lib/views/descriptor_tree_node_view.dart | 18 + .../example/lib/views/home_view.dart | 96 ++ .../example/lib/views/log_view.dart | 43 + .../lib/views/peripheral_manager_view.dart | 69 + .../example/lib/views/peripheral_view.dart | 107 ++ .../lib/views/service_tree_node_view.dart | 18 + .../example/lib/widgets.dart | 1 + .../example/lib/widgets/rssi_indicator.dart | 23 + .../example/linux/CMakeLists.txt | 12 +- .../example/linux/my_application.cc | 24 +- .../example/pubspec.lock | 105 +- .../example/pubspec.yaml | 21 +- .../example/test/widget_test.dart | 21 +- .../lib/bluetooth_low_energy_linux.dart | 4 +- .../lib/src/my_bluez.dart | 87 +- .../lib/src/my_central_manager.dart | 247 ++- .../lib/src/my_event_args.dart | 8 - .../lib/src/my_gatt.dart | 74 + .../lib/src/my_gatt_characteristic2.dart | 29 - .../lib/src/my_gatt_descriptor2.dart | 22 - .../lib/src/my_gatt_service2.dart | 27 - ...my_peripheral2.dart => my_peripheral.dart} | 4 +- bluetooth_low_energy_linux/pubspec.yaml | 25 +- .../test/bluetooth_low_energy_linux_test.dart | 1 + .../.gitignore | 1 - .../.metadata | 6 +- .../CHANGELOG.md | 188 ++ .../LICENSE | 2 +- .../README.md | 8 +- ...uetooth_low_energy_platform_interface.dart | 37 +- .../lib/src/advertisement.dart | 15 +- .../src/bluetooth_low_energy_event_args.dart | 11 - .../lib/src/bluetooth_low_energy_manager.dart | 49 +- .../lib/src/bluetooth_low_energy_peer.dart | 16 +- .../lib/src/central.dart | 14 +- .../lib/src/central_event_args.dart | 51 - .../lib/src/central_manager.dart | 279 ++- .../lib/src/connection_state.dart | 8 + .../lib/src/event_args.dart | 2 +- .../lib/src/gatt.dart | 360 ++++ .../lib/src/gatt_attribute.dart | 7 - .../lib/src/gatt_characteristic.dart | 31 - .../lib/src/gatt_characteristic_property.dart | 17 - .../src/gatt_characteristic_write_type.dart | 9 - .../lib/src/gatt_descriptor.dart | 18 - .../lib/src/gatt_service.dart | 21 - .../lib/src/manufacturer_specific_data.dart | 2 +- .../lib/src/my_bluetooth_low_energy_peer.dart | 11 - .../lib/src/my_central.dart | 16 - .../lib/src/my_gatt_attribute.dart | 19 - .../lib/src/my_gatt_characteristic.dart | 27 - .../lib/src/my_gatt_descriptor.dart | 18 - .../lib/src/my_gatt_service.dart | 13 - .../lib/src/my_peripheral.dart | 16 - .../lib/src/peripheral.dart | 14 +- .../lib/src/peripheral_event_args.dart | 61 - .../lib/src/peripheral_manager.dart | 308 +++- .../lib/src/uuid.dart | 21 +- .../pubspec.yaml | 21 +- .../test/my_gatt_attribute_test.dart | 46 - .../test/uuid_test.dart | 13 +- bluetooth_low_energy_windows/.gitignore | 1 - bluetooth_low_energy_windows/.metadata | 10 +- bluetooth_low_energy_windows/CHANGELOG.md | 61 + bluetooth_low_energy_windows/LICENSE | 2 +- .../example/.gitignore | 1 - .../example/README.md | 4 +- .../plugin_integration_test.dart | 9 - .../example/lib/main.dart | 796 +-------- .../example/lib/models.dart | 1 + .../example/lib/models/log.dart | 10 + .../example/lib/router_config.dart | 85 + .../example/lib/view_models.dart | 6 + .../central_manager_view_model.dart | 67 + .../characteristic_view_model.dart | 145 ++ .../view_models/descriptor_view_model.dart | 10 + .../peripheral_manager_view_model.dart | 162 ++ .../view_models/peripheral_view_model.dart | 83 + .../lib/view_models/service_view_model.dart | 46 + .../example/lib/views.dart | 4 + .../example/lib/views/advertisement_view.dart | 69 + .../lib/views/central_manager_view.dart | 117 ++ .../views/characteristic_tree_node_view.dart | 18 + .../lib/views/characteristic_view.dart | 178 ++ .../lib/views/descriptor_tree_node_view.dart | 18 + .../example/lib/views/home_view.dart | 96 ++ .../example/lib/views/log_view.dart | 43 + .../lib/views/peripheral_manager_view.dart | 69 + .../example/lib/views/peripheral_view.dart | 107 ++ .../lib/views/service_tree_node_view.dart | 18 + .../example/lib/widgets.dart | 1 + .../example/lib/widgets/rssi_indicator.dart | 23 + .../example/pubspec.lock | 101 +- .../example/pubspec.yaml | 21 +- .../example/test/widget_test.dart | 21 +- .../example/windows/CMakeLists.txt | 12 +- .../flutter/generated_plugin_registrant.cc | 6 +- .../example/windows/runner/Runner.rc | 12 +- .../example/windows/runner/main.cpp | 2 +- .../example/windows/runner/utils.cpp | 4 +- .../lib/bluetooth_low_energy_windows.dart | 6 +- .../lib/src/my_api.dart | 366 ++-- .../lib/src/my_api.g.dart | 1380 ++++++++++----- .../lib/src/my_central.dart | 10 + .../lib/src/my_central_manager.dart | 543 ++++-- .../lib/src/my_gatt.dart | 97 ++ .../lib/src/my_gatt_characteristic2.dart | 30 - .../lib/src/my_gatt_descriptor2.dart | 22 - .../lib/src/my_gatt_service2.dart | 29 - .../lib/src/my_peripheral.dart | 10 + .../lib/src/my_peripheral_manager.dart | 498 ++++++ bluetooth_low_energy_windows/my_api.dart | 283 ++- bluetooth_low_energy_windows/pubspec.yaml | 27 +- .../bluetooth_low_energy_windows_test.dart | 1 + .../windows/CMakeLists.txt | 52 +- .../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_plugin.cpp | 27 + .../bluetooth_low_energy_windows_plugin.h | 30 + ...etooth_low_energy_windows_plugin_c_api.cpp | 12 + ...uetooth_low_energy_windows_plugin_c_api.h} | 8 +- .../windows/my_api.g.cpp | 1521 +++++++++++++---- .../windows/my_api.g.h | 683 ++++++-- .../windows/my_central_manager.cpp | 988 ++++++----- .../windows/my_central_manager.h | 106 +- .../windows/my_exception.cpp | 10 +- .../windows/my_exception.h | 28 +- .../windows/my_format.cpp | 1 + .../windows/my_format.h | 24 + .../windows/my_peripheral_manager.cpp | 887 ++++++++++ .../windows/my_peripheral_manager.h | 90 + ...uetooth_low_energy_windows_plugin_test.cpp | 8 + .../bluetooth_low_energy_windows_test.cpp | 43 - scripts/run_pigeon.sh | 1 - 380 files changed, 23782 insertions(+), 14127 deletions(-) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 bluetooth_low_energy/doc/migrations/migration-v6.md create mode 100644 bluetooth_low_energy/example/android/app/src/main/kotlin/dev/hebei/bluetooth_low_energy_example/MainActivity.kt delete mode 100644 bluetooth_low_energy/example/android/app/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_example/MainActivity.kt create mode 100644 bluetooth_low_energy/example/lib/models.dart create mode 100644 bluetooth_low_energy/example/lib/models/log.dart create mode 100644 bluetooth_low_energy/example/lib/router_config.dart create mode 100644 bluetooth_low_energy/example/lib/view_models.dart create mode 100644 bluetooth_low_energy/example/lib/view_models/central_manager_view_model.dart create mode 100644 bluetooth_low_energy/example/lib/view_models/characteristic_view_model.dart create mode 100644 bluetooth_low_energy/example/lib/view_models/descriptor_view_model.dart create mode 100644 bluetooth_low_energy/example/lib/view_models/peripheral_manager_view_model.dart create mode 100644 bluetooth_low_energy/example/lib/view_models/peripheral_view_model.dart create mode 100644 bluetooth_low_energy/example/lib/view_models/service_view_model.dart create mode 100644 bluetooth_low_energy/example/lib/views.dart create mode 100644 bluetooth_low_energy/example/lib/views/advertisement_view.dart create mode 100644 bluetooth_low_energy/example/lib/views/central_manager_view.dart create mode 100644 bluetooth_low_energy/example/lib/views/characteristic_tree_node_view.dart create mode 100644 bluetooth_low_energy/example/lib/views/characteristic_view.dart create mode 100644 bluetooth_low_energy/example/lib/views/descriptor_tree_node_view.dart create mode 100644 bluetooth_low_energy/example/lib/views/home_view.dart create mode 100644 bluetooth_low_energy/example/lib/views/log_view.dart create mode 100644 bluetooth_low_energy/example/lib/views/peripheral_manager_view.dart create mode 100644 bluetooth_low_energy/example/lib/views/peripheral_view.dart create mode 100644 bluetooth_low_energy/example/lib/views/service_tree_node_view.dart create mode 100644 bluetooth_low_energy/example/lib/widgets.dart create mode 100644 bluetooth_low_energy/example/lib/widgets/rssi_indicator.dart create mode 100644 bluetooth_low_energy/test/bluetooth_low_energy_test.dart rename bluetooth_low_energy_android/android/src/main/kotlin/dev/{yanshouwang/bluetooth_low_energy_android/BluetoothLowEnergyAndroid.kt => hebei/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPlugin.kt} (77%) rename bluetooth_low_energy_android/android/src/main/kotlin/dev/{yanshouwang/bluetooth_low_energy_android/MyApi.g.kt => hebei/bluetooth_low_energy_android/MyAPI.g.kt} (51%) create mode 100644 bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyAPI.kt create mode 100644 bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyActivityResultListener.kt rename bluetooth_low_energy_android/android/src/main/kotlin/dev/{yanshouwang => hebei}/bluetooth_low_energy_android/MyAdvertiseCallback.kt (91%) rename bluetooth_low_energy_android/android/src/main/kotlin/dev/{yanshouwang => hebei}/bluetooth_low_energy_android/MyBluetoothGattCallback.kt (74%) rename bluetooth_low_energy_android/android/src/main/kotlin/dev/{yanshouwang => hebei}/bluetooth_low_energy_android/MyBluetoothGattServerCallback.kt (57%) create mode 100644 bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyBluetoothLowEnergyManager.kt rename bluetooth_low_energy_android/android/src/main/kotlin/dev/{yanshouwang => hebei}/bluetooth_low_energy_android/MyBroadcastReceiver.kt (88%) rename bluetooth_low_energy_android/android/src/main/kotlin/dev/{yanshouwang => hebei}/bluetooth_low_energy_android/MyCentralManager.kt (57%) create mode 100644 bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyPeripheralManager.kt create mode 100644 bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyRequestPermissionResultListener.kt rename bluetooth_low_energy_android/android/src/main/kotlin/dev/{yanshouwang => hebei}/bluetooth_low_energy_android/MyScanCallback.kt (91%) delete mode 100644 bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.kt delete mode 100644 bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothLowEnergyManager.kt delete mode 100644 bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyPeripheralManager.kt delete mode 100644 bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyRequestPermissionResultListener.kt create mode 100644 bluetooth_low_energy_android/android/src/test/kotlin/dev/hebei/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPluginTest.kt delete mode 100644 bluetooth_low_energy_android/android/src/test/kotlin/dev/yanshouwang/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPluginTest.kt create mode 100644 bluetooth_low_energy_android/example/android/app/src/main/kotlin/dev/hebei/bluetooth_low_energy_android_example/MainActivity.kt delete mode 100644 bluetooth_low_energy_android/example/android/app/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_example/MainActivity.kt create mode 100644 bluetooth_low_energy_android/example/lib/models.dart create mode 100644 bluetooth_low_energy_android/example/lib/models/log.dart create mode 100644 bluetooth_low_energy_android/example/lib/router_config.dart create mode 100644 bluetooth_low_energy_android/example/lib/view_models.dart create mode 100644 bluetooth_low_energy_android/example/lib/view_models/central_manager_view_model.dart create mode 100644 bluetooth_low_energy_android/example/lib/view_models/characteristic_view_model.dart create mode 100644 bluetooth_low_energy_android/example/lib/view_models/descriptor_view_model.dart create mode 100644 bluetooth_low_energy_android/example/lib/view_models/peripheral_manager_view_model.dart create mode 100644 bluetooth_low_energy_android/example/lib/view_models/peripheral_view_model.dart create mode 100644 bluetooth_low_energy_android/example/lib/view_models/service_view_model.dart create mode 100644 bluetooth_low_energy_android/example/lib/views.dart create mode 100644 bluetooth_low_energy_android/example/lib/views/advertisement_view.dart create mode 100644 bluetooth_low_energy_android/example/lib/views/central_manager_view.dart create mode 100644 bluetooth_low_energy_android/example/lib/views/characteristic_tree_node_view.dart create mode 100644 bluetooth_low_energy_android/example/lib/views/characteristic_view.dart create mode 100644 bluetooth_low_energy_android/example/lib/views/descriptor_tree_node_view.dart create mode 100644 bluetooth_low_energy_android/example/lib/views/home_view.dart create mode 100644 bluetooth_low_energy_android/example/lib/views/log_view.dart create mode 100644 bluetooth_low_energy_android/example/lib/views/peripheral_manager_view.dart create mode 100644 bluetooth_low_energy_android/example/lib/views/peripheral_view.dart create mode 100644 bluetooth_low_energy_android/example/lib/views/service_tree_node_view.dart create mode 100644 bluetooth_low_energy_android/example/lib/widgets.dart create mode 100644 bluetooth_low_energy_android/example/lib/widgets/rssi_indicator.dart rename bluetooth_low_energy_android/lib/src/{my_central2.dart => my_central.dart} (80%) create mode 100644 bluetooth_low_energy_android/lib/src/my_gatt.dart delete mode 100644 bluetooth_low_energy_android/lib/src/my_gatt_characteristic2.dart delete mode 100644 bluetooth_low_energy_android/lib/src/my_gatt_descriptor2.dart delete mode 100644 bluetooth_low_energy_android/lib/src/my_gatt_service2.dart create mode 100644 bluetooth_low_energy_android/lib/src/my_peripheral.dart delete mode 100644 bluetooth_low_energy_android/lib/src/my_peripheral2.dart create mode 100644 bluetooth_low_energy_android/test/bluetooth_low_energy_android_test.dart create mode 100644 bluetooth_low_energy_darwin/darwin/.gitignore create mode 100644 bluetooth_low_energy_darwin/darwin/Assets/.gitkeep rename bluetooth_low_energy_darwin/darwin/Classes/{BluetoothLowEnergyDarwin.swift => BluetoothLowEnergyDarwinPlugin.swift} (74%) create mode 100644 bluetooth_low_energy_darwin/darwin/Classes/MyAPI.g.swift rename bluetooth_low_energy_darwin/darwin/Classes/{MyApi.swift => MyAPI.swift} (72%) delete mode 100644 bluetooth_low_energy_darwin/darwin/Classes/MyApi.g.swift create mode 100644 bluetooth_low_energy_darwin/example/lib/models.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/models/log.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/router_config.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/view_models.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/view_models/central_manager_view_model.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/view_models/characteristic_view_model.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/view_models/descriptor_view_model.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/view_models/peripheral_manager_view_model.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/view_models/peripheral_view_model.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/view_models/service_view_model.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/views.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/views/advertisement_view.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/views/central_manager_view.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/views/characteristic_tree_node_view.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/views/characteristic_view.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/views/descriptor_tree_node_view.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/views/home_view.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/views/log_view.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/views/peripheral_manager_view.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/views/peripheral_view.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/views/service_tree_node_view.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/widgets.dart create mode 100644 bluetooth_low_energy_darwin/example/lib/widgets/rssi_indicator.dart create mode 100644 bluetooth_low_energy_darwin/lib/src/my_gatt.dart delete mode 100644 bluetooth_low_energy_darwin/lib/src/my_gatt_characteristic2.dart delete mode 100644 bluetooth_low_energy_darwin/lib/src/my_gatt_descriptor2.dart delete mode 100644 bluetooth_low_energy_darwin/lib/src/my_gatt_service2.dart create mode 100644 bluetooth_low_energy_darwin/test/bluetooth_low_energy_darwin_test.dart create mode 100644 bluetooth_low_energy_linux/example/lib/models.dart create mode 100644 bluetooth_low_energy_linux/example/lib/models/log.dart create mode 100644 bluetooth_low_energy_linux/example/lib/router_config.dart create mode 100644 bluetooth_low_energy_linux/example/lib/view_models.dart create mode 100644 bluetooth_low_energy_linux/example/lib/view_models/central_manager_view_model.dart create mode 100644 bluetooth_low_energy_linux/example/lib/view_models/characteristic_view_model.dart create mode 100644 bluetooth_low_energy_linux/example/lib/view_models/descriptor_view_model.dart create mode 100644 bluetooth_low_energy_linux/example/lib/view_models/peripheral_manager_view_model.dart create mode 100644 bluetooth_low_energy_linux/example/lib/view_models/peripheral_view_model.dart create mode 100644 bluetooth_low_energy_linux/example/lib/view_models/service_view_model.dart create mode 100644 bluetooth_low_energy_linux/example/lib/views.dart create mode 100644 bluetooth_low_energy_linux/example/lib/views/advertisement_view.dart create mode 100644 bluetooth_low_energy_linux/example/lib/views/central_manager_view.dart create mode 100644 bluetooth_low_energy_linux/example/lib/views/characteristic_tree_node_view.dart create mode 100644 bluetooth_low_energy_linux/example/lib/views/characteristic_view.dart create mode 100644 bluetooth_low_energy_linux/example/lib/views/descriptor_tree_node_view.dart create mode 100644 bluetooth_low_energy_linux/example/lib/views/home_view.dart create mode 100644 bluetooth_low_energy_linux/example/lib/views/log_view.dart create mode 100644 bluetooth_low_energy_linux/example/lib/views/peripheral_manager_view.dart create mode 100644 bluetooth_low_energy_linux/example/lib/views/peripheral_view.dart create mode 100644 bluetooth_low_energy_linux/example/lib/views/service_tree_node_view.dart create mode 100644 bluetooth_low_energy_linux/example/lib/widgets.dart create mode 100644 bluetooth_low_energy_linux/example/lib/widgets/rssi_indicator.dart delete mode 100644 bluetooth_low_energy_linux/lib/src/my_event_args.dart create mode 100644 bluetooth_low_energy_linux/lib/src/my_gatt.dart delete mode 100644 bluetooth_low_energy_linux/lib/src/my_gatt_characteristic2.dart delete mode 100644 bluetooth_low_energy_linux/lib/src/my_gatt_descriptor2.dart delete mode 100644 bluetooth_low_energy_linux/lib/src/my_gatt_service2.dart rename bluetooth_low_energy_linux/lib/src/{my_peripheral2.dart => my_peripheral.dart} (76%) create mode 100644 bluetooth_low_energy_linux/test/bluetooth_low_energy_linux_test.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_event_args.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/central_event_args.dart create mode 100644 bluetooth_low_energy_platform_interface/lib/src/connection_state.dart create mode 100644 bluetooth_low_energy_platform_interface/lib/src/gatt.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/gatt_attribute.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic_property.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic_write_type.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/gatt_descriptor.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/gatt_service.dart delete 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.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/my_gatt_attribute.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/my_gatt_characteristic.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/my_gatt_descriptor.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/my_gatt_service.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/my_peripheral.dart delete mode 100644 bluetooth_low_energy_platform_interface/lib/src/peripheral_event_args.dart delete mode 100644 bluetooth_low_energy_platform_interface/test/my_gatt_attribute_test.dart create mode 100644 bluetooth_low_energy_windows/example/lib/models.dart create mode 100644 bluetooth_low_energy_windows/example/lib/models/log.dart create mode 100644 bluetooth_low_energy_windows/example/lib/router_config.dart create mode 100644 bluetooth_low_energy_windows/example/lib/view_models.dart create mode 100644 bluetooth_low_energy_windows/example/lib/view_models/central_manager_view_model.dart create mode 100644 bluetooth_low_energy_windows/example/lib/view_models/characteristic_view_model.dart create mode 100644 bluetooth_low_energy_windows/example/lib/view_models/descriptor_view_model.dart create mode 100644 bluetooth_low_energy_windows/example/lib/view_models/peripheral_manager_view_model.dart create mode 100644 bluetooth_low_energy_windows/example/lib/view_models/peripheral_view_model.dart create mode 100644 bluetooth_low_energy_windows/example/lib/view_models/service_view_model.dart create mode 100644 bluetooth_low_energy_windows/example/lib/views.dart create mode 100644 bluetooth_low_energy_windows/example/lib/views/advertisement_view.dart create mode 100644 bluetooth_low_energy_windows/example/lib/views/central_manager_view.dart create mode 100644 bluetooth_low_energy_windows/example/lib/views/characteristic_tree_node_view.dart create mode 100644 bluetooth_low_energy_windows/example/lib/views/characteristic_view.dart create mode 100644 bluetooth_low_energy_windows/example/lib/views/descriptor_tree_node_view.dart create mode 100644 bluetooth_low_energy_windows/example/lib/views/home_view.dart create mode 100644 bluetooth_low_energy_windows/example/lib/views/log_view.dart create mode 100644 bluetooth_low_energy_windows/example/lib/views/peripheral_manager_view.dart create mode 100644 bluetooth_low_energy_windows/example/lib/views/peripheral_view.dart create mode 100644 bluetooth_low_energy_windows/example/lib/views/service_tree_node_view.dart create mode 100644 bluetooth_low_energy_windows/example/lib/widgets.dart create mode 100644 bluetooth_low_energy_windows/example/lib/widgets/rssi_indicator.dart create mode 100644 bluetooth_low_energy_windows/lib/src/my_central.dart create mode 100644 bluetooth_low_energy_windows/lib/src/my_gatt.dart delete mode 100644 bluetooth_low_energy_windows/lib/src/my_gatt_characteristic2.dart delete mode 100644 bluetooth_low_energy_windows/lib/src/my_gatt_descriptor2.dart delete mode 100644 bluetooth_low_energy_windows/lib/src/my_gatt_service2.dart create mode 100644 bluetooth_low_energy_windows/lib/src/my_peripheral.dart create mode 100644 bluetooth_low_energy_windows/lib/src/my_peripheral_manager.dart create mode 100644 bluetooth_low_energy_windows/test/bluetooth_low_energy_windows_test.dart delete mode 100644 bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.cpp delete mode 100644 bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.h delete mode 100644 bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_c_api.cpp create mode 100644 bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_plugin.cpp create mode 100644 bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_plugin.h create mode 100644 bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_plugin_c_api.cpp rename bluetooth_low_energy_windows/windows/include/bluetooth_low_energy_windows/{bluetooth_low_energy_windows_c_api.h => bluetooth_low_energy_windows_plugin_c_api.h} (53%) create mode 100644 bluetooth_low_energy_windows/windows/my_format.cpp create mode 100644 bluetooth_low_energy_windows/windows/my_format.h create mode 100644 bluetooth_low_energy_windows/windows/my_peripheral_manager.cpp create mode 100644 bluetooth_low_energy_windows/windows/my_peripheral_manager.h create mode 100644 bluetooth_low_energy_windows/windows/test/bluetooth_low_energy_windows_plugin_test.cpp delete mode 100644 bluetooth_low_energy_windows/windows/test/bluetooth_low_energy_windows_test.cpp delete mode 100644 scripts/run_pigeon.sh diff --git a/.gitignore b/.gitignore index 5185860..496ee2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -.vscode/ .DS_Store \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..cbb98f9 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "windowsSdkVersion": "10.0.22621.0", + "compilerPath": "cl.exe", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "windows-msvc-arm64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/LICENSE b/LICENSE index 752d28b..3977562 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 yanshouwang +Copyright (c) 2024 hebei.dev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/bluetooth_low_energy/.gitignore b/bluetooth_low_energy/.gitignore index 96486fd..ac5aa98 100644 --- a/bluetooth_low_energy/.gitignore +++ b/bluetooth_low_energy/.gitignore @@ -26,5 +26,4 @@ migrate_working_dir/ /pubspec.lock **/doc/api/ .dart_tool/ -.packages build/ diff --git a/bluetooth_low_energy/.metadata b/bluetooth_low_energy/.metadata index 0885ce9..1437de8 100644 --- a/bluetooth_low_energy/.metadata +++ b/bluetooth_low_energy/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "efbf63d9c66b9f6ec30e9ad4611189aa80003d31" + revision: "5dcb86f68f239346676ceb1ed1ea385bd215fba1" channel: "stable" project_type: plugin @@ -13,8 +13,8 @@ project_type: plugin migration: platforms: - platform: root - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 # User provided section diff --git a/bluetooth_low_energy/CHANGELOG.md b/bluetooth_low_energy/CHANGELOG.md index 127ecf5..06268a6 100644 --- a/bluetooth_low_energy/CHANGELOG.md +++ b/bluetooth_low_energy/CHANGELOG.md @@ -1,3 +1,113 @@ +## 6.0.0 + +* [Add `CentralManager#retrieveConnectedPeripherals` method.](https://github.com/yanshouwang/bluetooth_low_energy/issues/61) +* [Add optional `serviceUUIDs` argument to the `CentralManager#startDiscovery` method.](https://github.com/yanshouwang/bluetooth_low_energy/issues/53) +* [Add `CentralManager#mtuChanged` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/57). +* [Add `PeripheralManager#mtuChanged` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/57). +* Add `BluetoothLowEnergyManager#authorize` method. +* Add `BluetoothLowEnergyManager#showAppSettings` method. +* [Add `CentralManager#requestMTU` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/57). +* [Add `CentralManager#getMaximumWriteLength` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/57). +* Add `PeripheralManager#connectionStateChanged` event. +* [Add `PeripheralManager#characteristicReadRequested` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#characteristicWriteRequested` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#descriptorReadRequested` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#descriptorWriteRequested` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#getMaximumNotifyLength` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/57). +* [Add `PeripheralManager#respondReadRequestWithValue` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#respondReadRequestWithError` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#respondWriteRequest` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#respondWriteRequestWithError` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* Add `ConnectionState` enum. +* Add `GATTService.isPrimary` field. +* Add `GATTService#includedServices` field. +* Add `MutableGATTCharacteristic#permissions` field. +* Add `MutableGATTDescriptor#permissions` field. +* Add `int` type to `UUID#fromAddress`. +* Move `CentralManger.instance` to factory constructor. +* Move `PeripheralManager.instance` to factory constructor. +* Move the type of `Advertisement#manufacturerSpecificData` to `List`. +* Move `BluetoothLowEnergyManager#getState` to `BluetoothLowEnergyManager#state`. +* Move `PeripheralManager#clearServices` to `PeripheralManager#removeAllServices`. +* Remove `BluetoothLowEnergyManager#setUp` method. +* Remove `PeripheralManager#characteristicRead` event. +* Remove `PeripheralManager#characteristicWritten` event. +* Remove `PeripheralManager#readCharacteristic` method. +* Remove `PeripheralManager#writeCharacteristic` method. +* Fix the issue that [`Cannot access value of empty optional`](https://github.com/yanshouwang/bluetooth_low_energy/issues/63). +* Fix known issues. +* Rewrite example with MVVM. + +## 6.0.0-dev.3 + +* Implement `CentralMananger#showAppSettings` on iOS. +* Implement `PeripheralManager#showAppSettings` on iOS. +* Fix known issues. + +## 6.0.0-dev.2 + +* Add `int` type to `UUID#fromAddress`. +* Move the type of `Advertisement#manufacturerSpecificData` to `List`. +* Rewrite example with MVVM. +* Fix known issues. + +## 6.0.0-dev.1 + +* Add `PeripheralManager#respondReadRequestWithValue`. +* Add `PeripheralManager#respondReadRequestWithError`. +* Add `PeripheralManager#respondWriteRequest`. +* Add `PeripheralManager#respondWriteRequestWithError`. +* Remove `PeripheralManager#respondCharacteristicReadRequestWithValue`. +* Remove `PeripheralManager#respondCharacteristicReadRequestWithError`. +* Remove `PeripheralManager#respondCharacteristicWriteRequest`. +* Remove `PeripheralManager#respondCharacteristicWriteRequestWithError`. +* Remove `PeripheralManager#respondDescriptorReadRequestWithValue`. +* Remove `PeripheralManager#respondDescriptorReadRequestWithError`. +* Remove `PeripheralManager#respondDescriptorWriteRequest`. +* Remove `PeripheralManager#respondDescriptorWriteRequestWithError`. + +## 6.0.0-dev.0 + +* [Add `CentralManager#retrieveConnectedPeripherals` method.](https://github.com/yanshouwang/bluetooth_low_energy/issues/61) +* [Add optional `serviceUUIDs` argument to the `CentralManager#startDiscovery` method.](https://github.com/yanshouwang/bluetooth_low_energy/issues/53) +* [Add `CentralManager#mtuChanged` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/57). +* [Add `PeripheralManager#mtuChanged` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/57). +* Add `BluetoothLowEnergyManager#authorize` method. +* Add `BluetoothLowEnergyManager#showAppSettings` method. +* [Add `CentralManager#requestMTU` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/57). +* [Add `CentralManager#getMaximumWriteLength` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/57). +* Add `PeripheralManager#connectionStateChanged` event. +* [Add `PeripheralManager#characteristicReadRequested` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#characteristicWriteRequested` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#descriptorReadRequested` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#descriptorWriteRequested` event](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#getMaximumNotifyLength` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/57). +* [Add `PeripheralManager#respondCharacteristicReadRequestWithValue` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#respondCharacteristicReadRequestWithError` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#respondCharacteristicWriteRequest` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#respondCharacteristicWriteRequestWithError` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#respondDescriptorReadRequestWithValue` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#respondDescriptorReadRequestWithError` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#respondDescriptorWriteRequest` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* [Add `PeripheralManager#respondDescriptorWriteRequestWithError` method](https://github.com/yanshouwang/bluetooth_low_energy/issues/45). +* Add `ConnectionState` enum. +* Add `GATTService.isPrimary` field. +* Add `GATTService#includedServices` field. +* Add `MutableGATTCharacteristic#permissions` field. +* Add `MutableGATTDescriptor#permissions` field. +* Move `BluetoothLowEnergyManager#getState` to `BluetoothLowEnergyManager#state`. +* Move `PeripheralManager#clearServices` to `PeripheralManager#removeAllServices`. +* Move `CentralManger.instance` to factory constructor. +* Move `PeripheralManager.instance` to factory constructor. +* Remove `BluetoothLowEnergyManager#setUp` method. +* Remove `GATTCharacteristicReadEventArgs` class. +* Remove `GATTCharacteristicWrittenEventArgs` class. +* Remove `PeripheralManager#characteristicRead` event. +* Remove `PeripheralManager#characteristicWritten` event. +* Remove `PeripheralManager#readCharacteristic` method. +* Remove `PeripheralManager#writeCharacteristic` method. +* Fix the issue that [`Cannot access value of empty optional`](https://github.com/yanshouwang/bluetooth_low_energy/issues/63). + ## 5.0.7 * `Android` Fix the issue that [Advertisement resolve failed with `NullPointerException`](https://github.com/yanshouwang/bluetooth_low_energy/issues/59) diff --git a/bluetooth_low_energy/LICENSE b/bluetooth_low_energy/LICENSE index 752d28b..3977562 100644 --- a/bluetooth_low_energy/LICENSE +++ b/bluetooth_low_energy/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 yanshouwang +Copyright (c) 2024 hebei.dev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/bluetooth_low_energy/README.md b/bluetooth_low_energy/README.md index 1454609..e4a195a 100644 --- a/bluetooth_low_energy/README.md +++ b/bluetooth_low_energy/README.md @@ -1,61 +1,103 @@ # bluetooth_low_energy -A Flutter plugin for controlling the bluetooth low energy. +A Flutter plugin for controlling the bluetooth low energy, supports central and peripheral roles. -## Features +## CentralManager -### CentralManager +|API|Android|iOS|macOS|Windows|Linux| +|:-|:-:|:-:|:-:|:-:|:-:| +|logLevel|✅|✅|✅|✅|✅| +|state|✅|✅|✅|✅|✅| +|stateChanged|✅|✅|✅|✅|✅| +|authorize|✅||||| +|showAppSettings|✅|✅|||| +|discovered|✅|✅|✅|✅|✅| +|connectionStateChanged|✅|✅|✅|✅|✅| +|mtuChanged|✅|||✅|| +|characteristicNotified|✅|✅|✅|✅|✅| +|startDiscovery|✅|✅|✅|✅|✅| +|stopDiscovery|✅|✅|✅|✅|✅| +|retrieveConnectedPeripherals|✅|✅|✅||✅| +|connect|✅|✅|✅|✅|✅| +|disconnect|✅|✅|✅|✅|✅| +|requestMTU|✅||||| +|getMaximumWriteLength|✅|✅|✅|✅|✅| +|readRSSI|✅|✅|✅||✅| +|readCharacteristic|✅|✅|✅|✅|✅| +|writeCharacteristic|✅|✅|✅|✅|✅| +|setCharacteristicNotifyState|✅|✅|✅|✅|✅| +|readDescriptor|✅|✅|✅|✅|✅| +|writeDescriptor|✅|✅|✅|✅|✅| -- [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] Read RSSI of peripherals. -- [x] Discover GATT. -- [x] Read/Write GATT characteristics. -- [x] Set GATT characteristics notify state. -- [x] Read/Write GATT descriptors. +## PeripheralManager -### PeripheralManager - -- [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] Read/Write(Notify) GATT characteristics. +|API|Android|iOS|macOS|Windows|Linux| +|:-|:-:|:-:|:-:|:-:|:-:| +|logLevel|✅|✅|✅|✅|| +|state|✅|✅|✅|✅|| +|stateChanged|✅|✅|✅|✅|| +|authorize|✅||||| +|showAppSettings|✅|✅|||| +|connectionStateChanged|✅||||| +|mtuChanged|✅|||✅|| +|characteristicReadRequested|✅|✅|✅|✅|| +|characteristicWriteRequested|✅|✅|✅|✅|| +|characteristicNotifyStateChanged|✅|✅|✅|✅|| +|descriptorReadRequested|✅|||✅|| +|descriptorWriteRequested|✅|||✅|| +|addService|✅|✅|✅|✅|| +|removeService|✅|✅|✅|✅|| +|removeAllServices|✅|✅|✅|✅|| +|startAdvertising|✅|✅|✅|✅|| +|stopAdvertising|✅|✅|✅|✅|| +|getMaximumNotifyLength|✅|✅|✅|✅|| +|respondReadRequestWithValue|✅|✅|✅|✅|| +|respondReadRequestWithError|✅|✅|✅|✅|| +|respondWriteRequest|✅|✅|✅|✅|| +|respondWriteRequestWithError|✅|✅|✅|✅|| +|notifyCharacteristic|✅|✅|✅|✅|| ## Getting Started -Add `bluetooth_low_energy` as a [dependency in your pubspec.yaml file](https://flutter.dev/using-packages/). +Add `bluetooth_low_energy` as a [dependency][1] in your pubspec.yaml file. -``` Yaml +``` YAML dependencies: bluetooth_low_energy: ^ ``` -Remember to call `await CentralManager.setUp()` and `await PeripheralManager.setUp()` before use any apis of this plugin. - *Note:* Bluetooth Low Energy doesn't work on emulators, so use physical devices which has bluetooth features for development. ### Android -Make sure you have a `miniSdkVersion` with 21 or higher in your `android/app/build.gradle` file. +Make sure you have a `minSdk` with 21 or higher in your `android/app/build.gradle` file. ### iOS and macOS -According to Apple's [documents](https://developer.apple.com/documentation/corebluetooth/), you must include the [`NSBluetoothAlwaysUsageDescription`](https://developer.apple.com/documentation/bundleresources/information_property_list/nsbluetoothalwaysusagedescription) on or after iOS 13, and include the [`NSBluetoothPeripheralUsageDescription`](https://developer.apple.com/documentation/bundleresources/information_property_list/nsbluetoothperipheralusagedescription) key before iOS 13. +According to the [Apple's documents][2], you must include the [`NSBluetoothAlwaysUsageDescription`][3] on or after iOS 13, and include the [`NSBluetoothPeripheralUsageDescription`][4] key before iOS 13. -*Note:* The `PeripheralManager#startAdvertising` only support `name` and `serviceUUIDs`, see [the startAdvertising document](https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising). +When use bluetooth or other hardwares on macOS, developers need to [configure the app sandbox][5]. -*Note:* When use bluetooth or other hardwares on macOS, developers need to [configure the app sandbox](https://developer.apple.com/documentation/xcode/configuring-the-macos-app-sandbox#Enable-access-to-restricted-resources). +*Note:* The `PeripheralManager#startAdvertising` only support `name` and `serviceUUIDs`, see the [Apple's document][6]. + +### Winodows + +*Note:* The `PeripheralManager#startAdvertising` not support `name`, see the [Microsoft's document][7]. ### Linux -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). +The `PeripheralManager` API is not implemented since the [`bluez`][8] didn't support this feature yet. -### Windows +## Migrations -PeripheralManager is not implemented, it will be implemented in the future. +* [Migrate from 5.x to 6.x][9] -*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. +[1]: https://docs.flutter.dev/packages-and-plugins/using-packages +[2]: https://developer.apple.com/documentation/corebluetooth +[3]: https://developer.apple.com/documentation/bundleresources/information_property_list/nsbluetoothalwaysusagedescription +[4]: https://developer.apple.com/documentation/bundleresources/information_property_list/nsbluetoothperipheralusagedescription +[5]: https://developer.apple.com/documentation/xcode/configuring-the-macos-app-sandbox#Enable-access-to-restricted-resources +[6]: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising +[7]: https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementpublisher.advertisement?view=winrt-22621 +[8]: https://github.com/canonical/bluez.dart +[9]: doc/migrations/migration-v6.md \ No newline at end of file diff --git a/bluetooth_low_energy/doc/migrations/migration-v6.md b/bluetooth_low_energy/doc/migrations/migration-v6.md new file mode 100644 index 0000000..48f29c8 --- /dev/null +++ b/bluetooth_low_energy/doc/migrations/migration-v6.md @@ -0,0 +1,279 @@ +# Migrate form 5.x to 6.x + + +## Capitalization Rules + +* `GattCharacteristicNotifiedEventArgs` -> `GATTCharacteristicNotifiedEventArgs` +* `GattAttribute` -> `GATTAttribute` +* `GattDescriptor` -> `GATTDescriptor` +* `GattCharacteristic` -> `GATTCharacteristic ` +* `GattCharacteristicProperty` -> `GATTCharacteristicProperty` +* `GattCharacteristicWriteType` -> `GATTCharacteristicWriteType` +* `GattService` -> `GATTService` +* `GattCharacteristicNotifyStateChangedEventArgs` -> `GATTCharacteristicNotifyStateChangedEventArgs` + +## CentralManager + +1. Use the factory method to get the instance. + +``` Dart +// 5.x +// final centralManager = CentralManager.instance; +// 6.x +final centralManager = CentralManager(); +``` + +2. The `setUp` method is removed. + +``` Dart +// 5.x +// await centralManager.setUp(); +``` + +3. Authorize manually on Android. + +``` Dart +// The authorize method is no longer called automatically on Android +// after 6.x, listen the stateChanged stream and call the authorize +// method when the state is unauthorized. +final stateChangedSubscription = centralManager.stateChanged.listen((eventArgs) async { + final state = eventArgs.state; + if (Platform.isAndroid && state == BluetoothLowEnergyState.unauthorized) { + await centralManager.authorize(); + } + this.state.value = state; +}); +``` + +4. The type of `manufacturerSpecificData` in `Advertisement` changed from `ManufacturerSpecificData` to `List`. + +``` Dart +// 5.x +final manufacturerSpecificData = advertisement.manufacturerSpecificData; // ManufacturerSpecificData +// 6.0 +final manufacturerSpecificData = advertisement.manufacturerSpecificData; // List +``` + +5. Request MTU manually on Android. + +``` Dart +// The mtu is no longer requested automatically on Android +// after 6.x, call the requestMTU method after connected. +await centralManager.connect(peripheral); +services.value = await centralManager.discoverGATT(peripheral); +if (Platform.isAndroid) { + await centralManager.requestMTU( + peripheral, + mtu: 517, + ); +} +``` + +6. Fragment the value manually when write characteristics. + +``` Dart +// The value is no longer fragmented automatically after +// 6.x, fragment the value with the maximum write length. +final fragmentSize = await centralManager.getMaximumWriteLength( + peripheral, + type: type, +); +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.writeCharacteristic( + peripheral, + characteristic, + value: fragmentedValue, + type: type, + ); + start = end; +} +``` + +## PeripheralManager + + +1. Use the factory method to get the instance. + +``` Dart +// 5.x +// final peripheralManager = PeripheralManager.instance; +// 6.x +final peripheralManager = PeripheralManager(); +``` + +2. The `setUp` method is removed. + +``` Dart +// 5.x +// await peripheralManager.setUp(); +``` + +3. Authorize manually on Android. + +``` Dart +// The authorize method is no longer called automatically on Android +// after 6.x, listen the stateChanged stream and call the authorize +// method when the state is unauthorized. +final stateChangedSubscription = peripheralManager.stateChanged.listen((eventArgs) async { + final state = eventArgs.state; + if (Platform.isAndroid && state == BluetoothLowEnergyState.unauthorized) { + await peripheralManager.authorize(); + } + this.state.value = state; +}); +``` + +4. Use the factory methods to create GATTService and GATTDescriptor. + +``` Dart +// 5.x +// final service = GattService( +// uuid: serviceUUID, +// characteristics: [ +// GattCharacteristic( +// uuid: characteristicUUID, +// properties: [ +// GattCharacteristicProperty.read, +// GattCharacteristicProperty.write, +// GattCharacteristicProperty.writeWithoutResponse, +// GattCharacteristicProperty.notify, +// GattCharacteristicProperty.indicate, +// ], +// value: characteristicValue, +// descriptors: [ +// GattDescriptor( +// uuid: descriptorUUID, +// value: descriptorValue, +// ), +// ], +// ), +// ], +// ); +// 6.x +final service = GATTService( + uuid: serviceUUID, + characteristics: [ + GATTCharacteristic.immutable( + uuid: immutableCharacteristicUUID, + value: immutableCharacteristicValue, + descriptors: [ + GATTDescriptor.immutable( + uuid: immutableDescriptorUUID, + value: immutableDescriptorValue, + ), + ] + ), + GATTCharacteristic.mutable( + uuid: mutableCharacteristicUUID, + properties: [ + GATTCharacteristicProperty.read, + GATTCharacteristicProperty.write, + GATTCharacteristicProperty.writeWithoutResponse, + GATTCharacteristicProperty.notify, + GATTCharacteristicProperty.indicate, + ], + permissions: [ + GATTCharacteristicPermission.read, + GATTCharacteristicPermission.write, + ], + descriptors: [ + // This is not supported on iOS and macOS. + GATTDescriptor.mutable( + uuid: mutableDescriptorUUID, + permissions: [ + GATTCharacteristicPermission.read, + GATTCharacteristicPermission.write, + ], + ), + ] + ), + ], +); +``` + +5. The type of `manufacturerSpecificData` in `Advertisement` changed from `ManufacturerSpecificData` to `List`. + +``` Dart +// 5.x +final advertisement = Advertisement( + manufacturerSpecificData: ManufacturerSpecificData( + id: 0x2e19, + data: Uint8List.fromList([0x01, 0x02, 0x03]), + ), +); +// 6.x +final advertisement = Advertisement( + manufacturerSpecificData: [ + ManufacturerSpecificData( + id: 0x2e19, + data: Uint8List.fromList([0x01, 0x02, 0x03]), + ), + ], +); +``` + +6. Respond read and write requests manually. + +``` Dart +// The read and write requests are no longer responded automatically +// after 6.x, listen the characteristicReadRequested, +// characteristicWriteRequested, descriptorReadRequested and +// descriptorWriteRequested, then respond the requests. + +// 5.x +// final characteristicReadSubscription = PeripheralManager.instance.characteristicRead.listen((eventArgs) async { +// }); +// final characteristicWrittenSubscription = PeripheralManager.instance.characteristicWritten.listen((eventArgs) // async { +// }); + +// 6.x +// These streams are only available when the characteristic is mutable. +final characteristicReadRequestedSubscription = peripheralManager.characteristicReadRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final trimmedValue = value.sublist(offset); + await peripheralManager.respondReadRequestWithValue( + request, + value: trimmedValue, + ); +}); +final characteristicWriteRequestedSubscription = peripheralManager.characteristicWriteRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final value = request.value; + await peripheralManager.respondWriteRequest(request); +}); +``` + +7. Fragment the value manually when notify characteristics. + +``` Dart +// The value is no longer fragmented automatically after +// 6.x, fragment the value with the maximum notify length. +final fragmentSize = await peripheralManager.getMaximumNotifyLength( + peripheral, + type: type, +); +var start = 0; +while (start < value.length) { + final end = start + fragmentSize; + final fragmentedValue = end < value.length + ? value.sublist(start, end) + : value.sublist(start); + await peripheralManager.notifyCharacteristic( + central, + characteristic, + value: value, + ); + start = end; +} +``` diff --git a/bluetooth_low_energy/example/.gitignore b/bluetooth_low_energy/example/.gitignore index 24476c5..29a3a50 100644 --- a/bluetooth_low_energy/example/.gitignore +++ b/bluetooth_low_energy/example/.gitignore @@ -27,7 +27,6 @@ migrate_working_dir/ .dart_tool/ .flutter-plugins .flutter-plugins-dependencies -.packages .pub-cache/ .pub/ /build/ diff --git a/bluetooth_low_energy/example/android/app/build.gradle b/bluetooth_low_energy/example/android/app/build.gradle index c5ff818..c2a2dc6 100644 --- a/bluetooth_low_energy/example/android/app/build.gradle +++ b/bluetooth_low_energy/example/android/app/build.gradle @@ -1,68 +1,58 @@ plugins { id "com.android.application" id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id "dev.flutter.flutter-gradle-plugin" } def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') +def localPropertiesFile = rootProject.file("local.properties") if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> + localPropertiesFile.withReader("UTF-8") { reader -> localProperties.load(reader) } } -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +def flutterVersionCode = localProperties.getProperty("flutter.versionCode") if (flutterVersionCode == null) { - flutterVersionCode = '1' + flutterVersionCode = "1" } -def flutterVersionName = localProperties.getProperty('flutter.versionName') +def flutterVersionName = localProperties.getProperty("flutter.versionName") if (flutterVersionName == null) { - flutterVersionName = '1.0' + flutterVersionName = "1.0" } android { - namespace "dev.yanshouwang.bluetooth_low_energy_example" - compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion + namespace = "dev.hebei.bluetooth_low_energy_example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "dev.yanshouwang.bluetooth_low_energy_example" + applicationId = "dev.hebei.bluetooth_low_energy_example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - // minSdkVersion flutter.minSdkVersion - minSdkVersion 21 - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutterVersionCode.toInteger() + versionName = flutterVersionName } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig = signingConfigs.debug } } } flutter { - source '../..' + source = "../.." } - -dependencies {} diff --git a/bluetooth_low_energy/example/android/app/src/main/AndroidManifest.xml b/bluetooth_low_energy/example/android/app/src/main/AndroidManifest.xml index bfe71e3..89c7199 100644 --- a/bluetooth_low_energy/example/android/app/src/main/AndroidManifest.xml +++ b/bluetooth_low_energy/example/android/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" + android:taskAffinity="" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" @@ -30,4 +31,15 @@ android:name="flutterEmbedding" android:value="2" /> + + + + + + + diff --git a/bluetooth_low_energy/example/android/app/src/main/kotlin/dev/hebei/bluetooth_low_energy_example/MainActivity.kt b/bluetooth_low_energy/example/android/app/src/main/kotlin/dev/hebei/bluetooth_low_energy_example/MainActivity.kt new file mode 100644 index 0000000..3589739 --- /dev/null +++ b/bluetooth_low_energy/example/android/app/src/main/kotlin/dev/hebei/bluetooth_low_energy_example/MainActivity.kt @@ -0,0 +1,5 @@ +package dev.hebei.bluetooth_low_energy_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/bluetooth_low_energy/example/android/app/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_example/MainActivity.kt b/bluetooth_low_energy/example/android/app/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_example/MainActivity.kt deleted file mode 100644 index aa60869..0000000 --- a/bluetooth_low_energy/example/android/app/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_example/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.yanshouwang.bluetooth_low_energy_example - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/bluetooth_low_energy/example/android/build.gradle b/bluetooth_low_energy/example/android/build.gradle index f7eb7f6..d2ffbff 100644 --- a/bluetooth_low_energy/example/android/build.gradle +++ b/bluetooth_low_energy/example/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() @@ -18,12 +5,12 @@ allprojects { } } -rootProject.buildDir = '../build' +rootProject.buildDir = "../build" subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { - project.evaluationDependsOn(':app') + project.evaluationDependsOn(":app") } tasks.register("clean", Delete) { diff --git a/bluetooth_low_energy/example/android/gradle.properties b/bluetooth_low_energy/example/android/gradle.properties index 94adc3a..3b5b324 100644 --- a/bluetooth_low_energy/example/android/gradle.properties +++ b/bluetooth_low_energy/example/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true diff --git a/bluetooth_low_energy/example/android/gradle/wrapper/gradle-wrapper.properties b/bluetooth_low_energy/example/android/gradle/wrapper/gradle-wrapper.properties index 3c472b9..e1ca574 100644 --- a/bluetooth_low_energy/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/bluetooth_low_energy/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/bluetooth_low_energy/example/android/settings.gradle b/bluetooth_low_energy/example/android/settings.gradle index 55c4ca8..536165d 100644 --- a/bluetooth_low_energy/example/android/settings.gradle +++ b/bluetooth_low_energy/example/android/settings.gradle @@ -5,16 +5,21 @@ pluginManagement { def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" return flutterSdkPath - } - settings.ext.flutterSdkPath = flutterSdkPath() + }() - includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - plugins { - id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false + repositories { + google() + mavenCentral() + gradlePluginPortal() } } -include ":app" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} -apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle" +include ":app" diff --git a/bluetooth_low_energy/example/integration_test/plugin_integration_test.dart b/bluetooth_low_energy/example/integration_test/plugin_integration_test.dart index c03e5a8..d53d2ae 100644 --- a/bluetooth_low_energy/example/integration_test/plugin_integration_test.dart +++ b/bluetooth_low_energy/example/integration_test/plugin_integration_test.dart @@ -6,17 +6,8 @@ // For more information about Flutter integration tests, please see // https://docs.flutter.dev/cookbook/testing/integration/introduction -// import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - // testWidgets('getPlatformVersion test', (WidgetTester tester) async { - // final BluetoothLowEnergy plugin = BluetoothLowEnergy(); - // final String? version = await plugin.getPlatformVersion(); - // // The version string depends on the host platform running the test, so - // // just assert that some non-empty string is returned. - // expect(version?.isNotEmpty, true); - // }); } diff --git a/bluetooth_low_energy/example/ios/Flutter/AppFrameworkInfo.plist b/bluetooth_low_energy/example/ios/Flutter/AppFrameworkInfo.plist index 9625e10..7c56964 100644 --- a/bluetooth_low_energy/example/ios/Flutter/AppFrameworkInfo.plist +++ b/bluetooth_low_energy/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/bluetooth_low_energy/example/ios/Podfile b/bluetooth_low_energy/example/ios/Podfile index fdcc671..d97f17e 100644 --- a/bluetooth_low_energy/example/ios/Podfile +++ b/bluetooth_low_energy/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/bluetooth_low_energy/example/ios/Podfile.lock b/bluetooth_low_energy/example/ios/Podfile.lock index 90d7b93..66cdac3 100644 --- a/bluetooth_low_energy/example/ios/Podfile.lock +++ b/bluetooth_low_energy/example/ios/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - bluetooth_low_energy_darwin (2.0.2): + - bluetooth_low_energy_darwin (0.0.1): - Flutter - FlutterMacOS - Flutter (1.0.0) @@ -20,10 +20,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/integration_test/ios" SPEC CHECKSUMS: - bluetooth_low_energy_darwin: e37c1af3337ebc99a60807137dc5e47c6195eac7 - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - integration_test: 13825b8a9334a850581300559b8839134b124670 + bluetooth_low_energy_darwin: 764d8d1ae5abefbcdb839e812b4b25c0061fcf8b + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4 -PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 +PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/bluetooth_low_energy/example/ios/Runner.xcodeproj/project.pbxproj b/bluetooth_low_energy/example/ios/Runner.xcodeproj/project.pbxproj index a833137..81a0eca 100644 --- a/bluetooth_low_energy/example/ios/Runner.xcodeproj/project.pbxproj +++ b/bluetooth_low_energy/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,14 +8,14 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 2AABF0E228395C6F7AA780A3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE7ADD24BADD6FBE9CC26391 /* Pods_Runner.framework */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; - 34E52FEFFBA34148D7F4058F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2353C45A2E0DF37667D194A1 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 80E5EA1F311778FC0849CADE /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A108F55DE130C33FD897F6F /* Pods_RunnerTests.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - F6040B5AA5E5A63D395CB345 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 076A35C4833796B0B42E0237 /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,21 +42,19 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 03284B9B33BEA09EAC1AFD47 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 076A35C4833796B0B42E0237 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2353C45A2E0DF37667D194A1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2E15B810DAA5A0DB3492E697 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 2FE7B38855AAF28688CD47C8 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 3280CE474DAFF9A6D8555987 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 4BF6EEA10984CDD1A14F950F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 54C98B3103488CA87E98A6F4 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 710DD54C8BBDEA22C3F7F293 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 6A108F55DE130C33FD897F6F /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 71D231D0A1BFD0F83AA161B9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 93DA77ACC45858A97F558838 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -64,15 +62,17 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9C6D6BDEFF4878166372D24A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 9A4F151ED2AB361B13D31B8F /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + AC6B889B1461AF17271694EC /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + BE7ADD24BADD6FBE9CC26391 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 6B5302E24483403072236BBB /* Frameworks */ = { + 3B93A9D775DDA5660AD942F8 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F6040B5AA5E5A63D395CB345 /* Pods_RunnerTests.framework in Frameworks */, + 80E5EA1F311778FC0849CADE /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -80,22 +80,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 34E52FEFFBA34148D7F4058F /* Pods_Runner.framework in Frameworks */, + 2AABF0E228395C6F7AA780A3 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0FAACD3CD9E188E482384358 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 2353C45A2E0DF37667D194A1 /* Pods_Runner.framework */, - 076A35C4833796B0B42E0237 /* Pods_RunnerTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -104,17 +95,13 @@ path = RunnerTests; sourceTree = ""; }; - 3C9A3D0714D61C07F02BFBB4 /* Pods */ = { + 600205600FA5A8A2CFBD1657 /* Frameworks */ = { isa = PBXGroup; children = ( - 93DA77ACC45858A97F558838 /* Pods-Runner.debug.xcconfig */, - 4BF6EEA10984CDD1A14F950F /* Pods-Runner.release.xcconfig */, - 03284B9B33BEA09EAC1AFD47 /* Pods-Runner.profile.xcconfig */, - 9C6D6BDEFF4878166372D24A /* Pods-RunnerTests.debug.xcconfig */, - 54C98B3103488CA87E98A6F4 /* Pods-RunnerTests.release.xcconfig */, - 710DD54C8BBDEA22C3F7F293 /* Pods-RunnerTests.profile.xcconfig */, + BE7ADD24BADD6FBE9CC26391 /* Pods_Runner.framework */, + 6A108F55DE130C33FD897F6F /* Pods_RunnerTests.framework */, ); - path = Pods; + name = Frameworks; sourceTree = ""; }; 9740EEB11CF90186004384FC /* Flutter */ = { @@ -135,8 +122,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, - 3C9A3D0714D61C07F02BFBB4 /* Pods */, - 0FAACD3CD9E188E482384358 /* Frameworks */, + F72D04BB94F3E957C55625BE /* Pods */, + 600205600FA5A8A2CFBD1657 /* Frameworks */, ); sourceTree = ""; }; @@ -164,6 +151,19 @@ path = Runner; sourceTree = ""; }; + F72D04BB94F3E957C55625BE /* Pods */ = { + isa = PBXGroup; + children = ( + 71D231D0A1BFD0F83AA161B9 /* Pods-Runner.debug.xcconfig */, + 2E15B810DAA5A0DB3492E697 /* Pods-Runner.release.xcconfig */, + 2FE7B38855AAF28688CD47C8 /* Pods-Runner.profile.xcconfig */, + 9A4F151ED2AB361B13D31B8F /* Pods-RunnerTests.debug.xcconfig */, + 3280CE474DAFF9A6D8555987 /* Pods-RunnerTests.release.xcconfig */, + AC6B889B1461AF17271694EC /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -171,10 +171,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - A3C3FEADF4EBF04832B143F6 /* [CP] Check Pods Manifest.lock */, + BEFD77958F331CBECEF42D43 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, - 6B5302E24483403072236BBB /* Frameworks */, + 3B93A9D775DDA5660AD942F8 /* Frameworks */, ); buildRules = ( ); @@ -190,14 +190,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 67E1A4356061A10A5EC11286 /* [CP] Check Pods Manifest.lock */, + 10E9D52FCC12E90EB7D107E2 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 066C9063BC370AC8304B71D9 /* [CP] Embed Pods Frameworks */, + 2DAAF975682F92424B5A52E3 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -215,7 +215,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { @@ -269,7 +269,29 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 066C9063BC370AC8304B71D9 /* [CP] Embed Pods Frameworks */ = { + 10E9D52FCC12E90EB7D107E2 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 2DAAF975682F92424B5A52E3 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -302,28 +324,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 67E1A4356061A10A5EC11286 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -339,7 +339,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - A3C3FEADF4EBF04832B143F6 /* [CP] Check Pods Manifest.lock */ = { + BEFD77958F331CBECEF42D43 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -415,6 +415,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -444,6 +445,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -452,7 +454,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -468,14 +470,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = T5YVNX2NAC; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -485,14 +487,14 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9C6D6BDEFF4878166372D24A /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 9A4F151ED2AB361B13D31B8F /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -503,14 +505,14 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 54C98B3103488CA87E98A6F4 /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = 3280CE474DAFF9A6D8555987 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -519,14 +521,14 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 710DD54C8BBDEA22C3F7F293 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = AC6B889B1461AF17271694EC /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -537,6 +539,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -566,6 +569,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -580,7 +584,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -592,6 +596,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -621,6 +626,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -629,7 +635,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -647,14 +653,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = T5YVNX2NAC; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -670,14 +676,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = T5YVNX2NAC; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/bluetooth_low_energy/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/bluetooth_low_energy/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 87131a0..8e3ca5d 100644 --- a/bluetooth_low_energy/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/bluetooth_low_energy/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ LSRequiresIPhoneOS NSBluetoothAlwaysUsageDescription - Hello Bluetooth LowEnergy! + Need to use the bluetooth to communicate with peripherals UIApplicationSupportsIndirectInputEvents UILaunchStoryboardName diff --git a/bluetooth_low_energy/example/lib/main.dart b/bluetooth_low_energy/example/lib/main.dart index 4165eb0..a588ab4 100644 --- a/bluetooth_low_energy/example/lib/main.dart +++ b/bluetooth_low_energy/example/lib/main.dart @@ -1,15 +1,10 @@ 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'; -import 'package:convert/convert.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; +import 'package:logging/logging.dart'; -bool get enablePeripheral => !Platform.isLinux && !Platform.isWindows; +import 'router_config.dart'; void main() { runZonedGuarded(onStartUp, onCrashed); @@ -17,13 +12,7 @@ void main() { void onStartUp() async { Logger.root.onRecord.listen(onLogRecord); - // hierarchicalLoggingEnabled = true; - // CentralManager.instance.logLevel = Level.WARNING; - WidgetsFlutterBinding.ensureInitialized(); - await CentralManager.instance.setUp(); - if (enablePeripheral) { - await PeripheralManager.instance.setUp(); - } + hierarchicalLoggingEnabled = true; runApp(const MyApp()); } @@ -44,1136 +33,19 @@ void onLogRecord(LogRecord record) { ); } -class MyApp extends StatefulWidget { +class MyApp extends StatelessWidget { const MyApp({super.key}); - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData.light( - useMaterial3: true, - ).copyWith( + return MaterialApp.router( + routerConfig: routerConfig, + theme: ThemeData.light().copyWith( materialTapTargetSize: MaterialTapTargetSize.padded, ), - home: enablePeripheral ? const HomeView() : const ScannerView(), - routes: { - 'peripheral': (context) { - final route = ModalRoute.of(context); - final eventArgs = route!.settings.arguments as DiscoveredEventArgs; - return PeripheralView( - eventArgs: eventArgs, - ); - }, - }, - ); - } -} - -class HomeView extends StatefulWidget { - 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(); - } -} - -class ScannerView extends StatefulWidget { - const ScannerView({super.key}); - - @override - State createState() => _ScannerViewState(); -} - -class _ScannerViewState extends State { - late final ValueNotifier state; - late final ValueNotifier discovering; - late final ValueNotifier> discoveredEventArgs; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription discoveredSubscription; - - @override - void initState() { - super.initState(); - state = ValueNotifier(BluetoothLowEnergyState.unknown); - discovering = ValueNotifier(false); - discoveredEventArgs = ValueNotifier([]); - stateChangedSubscription = CentralManager.instance.stateChanged.listen( - (eventArgs) { - state.value = eventArgs.state; - }, - ); - discoveredSubscription = CentralManager.instance.discovered.listen( - (eventArgs) { - final items = discoveredEventArgs.value; - final i = items.indexWhere( - (item) => item.peripheral == eventArgs.peripheral, - ); - if (i < 0) { - discoveredEventArgs.value = [...items, eventArgs]; - } else { - items[i] = eventArgs; - discoveredEventArgs.value = [...items]; - } - }, - ); - _initialize(); - } - - void _initialize() async { - state.value = await CentralManager.instance.getState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), - ); - } - - PreferredSizeWidget buildAppBar(BuildContext context) { - return AppBar( - title: const Text('Scanner'), - actions: [ - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: discovering, - builder: (context, discovering, child) { - return TextButton( - onPressed: state == BluetoothLowEnergyState.poweredOn - ? () async { - if (discovering) { - await stopDiscovery(); - } else { - await startDiscovery(); - } - } - : null, - child: Text( - discovering ? 'END' : 'BEGIN', - ), - ); - }, - ); - }, - ), - ], - ); - } - - Future startDiscovery() async { - discoveredEventArgs.value = []; - await CentralManager.instance.startDiscovery(); - discovering.value = true; - } - - Future stopDiscovery() async { - await CentralManager.instance.stopDiscovery(); - discovering.value = false; - } - - Widget buildBody(BuildContext context) { - return ValueListenableBuilder( - valueListenable: discoveredEventArgs, - builder: (context, discoveredEventArgs, child) { - // final items = discoveredEventArgs; - final items = discoveredEventArgs - .where((eventArgs) => eventArgs.advertisement.name != null) - .toList(); - return ListView.separated( - itemBuilder: (context, i) { - final theme = Theme.of(context); - final item = items[i]; - final uuid = item.peripheral.uuid; - final rssi = item.rssi; - final advertisement = item.advertisement; - final name = advertisement.name; - return ListTile( - onTap: () async { - final discovering = this.discovering.value; - if (discovering) { - await stopDiscovery(); - } - if (!mounted) { - throw UnimplementedError(); - } - await Navigator.of(context).pushNamed( - 'peripheral', - arguments: item, - ); - if (discovering) { - await startDiscovery(); - } - }, - onLongPress: () async { - await showModalBottomSheet( - context: context, - builder: (context) { - return BottomSheet( - onClosing: () {}, - clipBehavior: Clip.antiAlias, - builder: (context) { - final manufacturerSpecificData = - advertisement.manufacturerSpecificData; - return ListView.separated( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 40.0, - ), - itemBuilder: (context, i) { - const idWidth = 80.0; - if (i == 0) { - return const Row( - children: [ - SizedBox( - width: idWidth, - child: Text('ID'), - ), - Expanded( - child: Text('DATA'), - ), - ], - ); - } else { - final id = - '0x${manufacturerSpecificData!.id.toRadixString(16).padLeft(4, '0')}'; - final value = - hex.encode(manufacturerSpecificData.data); - return Row( - children: [ - SizedBox( - width: idWidth, - child: Text(id), - ), - Expanded( - child: Text(value), - ), - ], - ); - } - }, - separatorBuilder: (context, i) { - return const Divider(); - }, - itemCount: manufacturerSpecificData == null ? 1 : 2, - ); - }, - ); - }, - ); - }, - title: Text(name ?? 'N/A'), - subtitle: Text( - '$uuid', - style: theme.textTheme.bodySmall, - softWrap: false, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - RssiWidget(rssi), - Text('$rssi'), - ], - ), - ); - }, - separatorBuilder: (context, i) { - return const Divider( - height: 0.0, - ); - }, - itemCount: items.length, - ); - }, - ); - } - - @override - void dispose() { - super.dispose(); - stateChangedSubscription.cancel(); - discoveredSubscription.cancel(); - state.dispose(); - discovering.dispose(); - discoveredEventArgs.dispose(); - } -} - -class PeripheralView extends StatefulWidget { - final DiscoveredEventArgs eventArgs; - - const PeripheralView({ - super.key, - required this.eventArgs, - }); - - @override - State createState() => _PeripheralViewState(); -} - -class _PeripheralViewState extends 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> logs; - late final TextEditingController writeController; - late final StreamSubscription connectionStateChangedSubscription; - late final StreamSubscription characteristicNotifiedSubscription; - - @override - void initState() { - super.initState(); - eventArgs = widget.eventArgs; - connectionState = ValueNotifier(false); - services = ValueNotifier([]); - characteristics = ValueNotifier([]); - service = ValueNotifier(null); - characteristic = ValueNotifier(null); - writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); - logs = ValueNotifier([]); - writeController = TextEditingController(); - connectionStateChangedSubscription = - CentralManager.instance.connectionStateChanged.listen( - (eventArgs) { - if (eventArgs.peripheral != this.eventArgs.peripheral) { - return; - } - final connectionState = eventArgs.connectionState; - this.connectionState.value = connectionState; - if (!connectionState) { - services.value = []; - characteristics.value = []; - service.value = null; - characteristic.value = null; - logs.value = []; - } - }, - ); - characteristicNotifiedSubscription = - CentralManager.instance.characteristicNotified.listen( - (eventArgs) { - // final characteristic = this.characteristic.value; - // if (eventArgs.characteristic != characteristic) { - // return; - // } - const type = LogType.notify; - final log = Log(type, eventArgs.value); - logs.value = [ - ...logs.value, - log, - ]; - }, - ); - } - - @override - Widget build(BuildContext context) { - return PopScope( - onPopInvoked: (didPop) async { - if (connectionState.value) { - final peripheral = eventArgs.peripheral; - await CentralManager.instance.disconnect(peripheral); - } - }, - child: Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), + darkTheme: ThemeData.dark().copyWith( + materialTapTargetSize: MaterialTapTargetSize.padded, ), ); } - - PreferredSizeWidget buildAppBar(BuildContext context) { - final title = eventArgs.advertisement.name ?? ''; - return AppBar( - title: Text(title), - actions: [ - ValueListenableBuilder( - valueListenable: connectionState, - builder: (context, state, child) { - return TextButton( - onPressed: () async { - final peripheral = eventArgs.peripheral; - if (state) { - await CentralManager.instance.disconnect(peripheral); - } else { - await CentralManager.instance.connect(peripheral); - services.value = - await CentralManager.instance.discoverGATT(peripheral); - } - }, - child: Text(state ? 'DISCONNECT' : 'CONNECT'), - ); - }, - ), - ], - ); - } - - Widget buildBody(BuildContext context) { - final theme = Theme.of(context); - return Container( - margin: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ValueListenableBuilder( - valueListenable: services, - builder: (context, services, child) { - final items = services.map((service) { - return DropdownMenuItem( - value: service, - child: Text( - '${service.uuid}', - style: theme.textTheme.bodyMedium, - ), - ); - }).toList(); - return ValueListenableBuilder( - valueListenable: service, - builder: (context, service, child) { - return DropdownButton( - isExpanded: true, - items: items, - hint: const Text('CHOOSE A SERVICE'), - value: service, - onChanged: (service) async { - this.service.value = service; - characteristic.value = null; - if (service == null) { - return; - } - characteristics.value = service.characteristics; - }, - ); - }, - ); - }, - ), - ValueListenableBuilder( - valueListenable: characteristics, - builder: (context, characteristics, child) { - final items = characteristics.map((characteristic) { - return DropdownMenuItem( - value: characteristic, - child: Text( - '${characteristic.uuid}', - style: theme.textTheme.bodyMedium, - ), - ); - }).toList(); - return ValueListenableBuilder( - valueListenable: characteristic, - builder: (context, characteristic, child) { - return DropdownButton( - isExpanded: true, - items: items, - 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; - } - }, - ); - }, - ); - }, - ), - Expanded( - child: 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 = 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, - ); - }, - ), - ), - 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: [ - ElevatedButton( - onPressed: characteristic != null && canNotify - ? () async { - await CentralManager.instance - .setCharacteristicNotifyState( - characteristic, - state: true, - ); - } - : null, - child: const Text('NOTIFY'), - ), - const SizedBox(width: 8.0), - ElevatedButton( - onPressed: characteristic != null && canRead - ? () async { - final value = await CentralManager.instance - .readCharacteristic(characteristic); - const type = LogType.read; - final log = Log(type, value); - logs.value = [...logs.value, log]; - } - : null, - child: const Text('READ'), - ) - ], - ), - ), - 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), - ], - ); - }, - ), - ], - ), - ); - } - - @override - void dispose() { - super.dispose(); - connectionStateChangedSubscription.cancel(); - characteristicNotifiedSubscription.cancel(); - connectionState.dispose(); - services.dispose(); - characteristics.dispose(); - service.dispose(); - characteristic.dispose(); - writeType.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 characteristicReadSubscription; - late final StreamSubscription characteristicWrittenSubscription; - late final StreamSubscription characteristicNotifyStateChangedSubscription; - - @override - void initState() { - super.initState(); - state = ValueNotifier(BluetoothLowEnergyState.unknown); - advertising = ValueNotifier(false); - logs = ValueNotifier([]); - stateChangedSubscription = PeripheralManager.instance.stateChanged.listen( - (eventArgs) { - state.value = eventArgs.state; - }, - ); - characteristicReadSubscription = - PeripheralManager.instance.characteristicRead.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final value = eventArgs.value; - final log = Log( - LogType.read, - value, - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', - ); - logs.value = [ - ...logs.value, - log, - ]; - }, - ); - characteristicWrittenSubscription = - PeripheralManager.instance.characteristicWritten.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final value = eventArgs.value; - final log = Log( - LogType.write, - value, - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', - ); - logs.value = [ - ...logs.value, - log, - ]; - }, - ); - characteristicNotifyStateChangedSubscription = - PeripheralManager.instance.characteristicNotifyStateChanged.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final state = eventArgs.state; - final log = Log( - LogType.notify, - 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 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 - 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.instance.clearServices(); - final batteryService = GattService( - uuid: UUID.short(0x180f), - characteristics: [ - GattCharacteristic( - uuid: UUID.short(0x2A19), - properties: [GattCharacteristicProperty.read], - value: Uint8List.fromList([75]), - descriptors: [], - ), - ], - ); - await PeripheralManager.instance.addService(batteryService); - final elements = List.generate(1000, (i) => i % 256); - final value = Uint8List.fromList(elements); - final service = GattService( - uuid: UUID.short(100), - characteristics: [ - GattCharacteristic( - uuid: UUID.short(200), - properties: [ - GattCharacteristicProperty.read, - ], - value: value, - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(201), - properties: [ - GattCharacteristicProperty.write, - GattCharacteristicProperty.writeWithoutResponse, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(202), - properties: [ - GattCharacteristicProperty.notify, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(203), - properties: [ - GattCharacteristicProperty.indicate, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(204), - properties: [ - GattCharacteristicProperty.read, - GattCharacteristicProperty.write, - GattCharacteristicProperty.writeWithoutResponse, - GattCharacteristicProperty.notify, - GattCharacteristicProperty.indicate, - ], - value: value, - descriptors: [], - ), - ], - ); - await PeripheralManager.instance.addService(service); - final advertisement = Advertisement( - name: 'le12138', - manufacturerSpecificData: ManufacturerSpecificData( - id: 0x2e19, - data: Uint8List.fromList([0x01, 0x02, 0x03]), - ), - ); - await PeripheralManager.instance.startAdvertising(advertisement); - advertising.value = true; - } - - Future stopAdvertising() async { - await PeripheralManager.instance.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(); - characteristicReadSubscription.cancel(); - characteristicWrittenSubscription.cancel(); - characteristicNotifyStateChangedSubscription.cancel(); - state.dispose(); - advertising.dispose(); - logs.dispose(); - } -} - -class Log { - final DateTime time; - final LogType type; - final Uint8List value; - final String? detail; - - Log( - this.type, - this.value, [ - this.detail, - ]) : time = DateTime.now(); - - @override - String toString() { - final type = this.type.toString().split('.').last; - final formatter = DateFormat.Hms(); - final time = formatter.format(this.time); - final message = hex.encode(value); - if (detail == null) { - return '[$type]$time: $message'; - } else { - return '[$type]$time: $message /* $detail */'; - } - } -} - -enum LogType { - read, - write, - notify, - error, -} - -class RssiWidget extends StatelessWidget { - final int rssi; - - const RssiWidget( - this.rssi, { - super.key, - }); - - @override - Widget build(BuildContext context) { - final IconData icon; - if (rssi > -70) { - icon = Icons.wifi_rounded; - } else if (rssi > -100) { - icon = Icons.wifi_2_bar_rounded; - } else { - icon = Icons.wifi_1_bar_rounded; - } - return Icon(icon); - } } diff --git a/bluetooth_low_energy/example/lib/models.dart b/bluetooth_low_energy/example/lib/models.dart new file mode 100644 index 0000000..627f4d2 --- /dev/null +++ b/bluetooth_low_energy/example/lib/models.dart @@ -0,0 +1 @@ +export 'models/log.dart'; diff --git a/bluetooth_low_energy/example/lib/models/log.dart b/bluetooth_low_energy/example/lib/models/log.dart new file mode 100644 index 0000000..2bcfbf7 --- /dev/null +++ b/bluetooth_low_energy/example/lib/models/log.dart @@ -0,0 +1,10 @@ +class Log { + final DateTime time; + final String type; + final String message; + + Log({ + required this.type, + required this.message, + }) : time = DateTime.now(); +} diff --git a/bluetooth_low_energy/example/lib/router_config.dart b/bluetooth_low_energy/example/lib/router_config.dart new file mode 100644 index 0000000..8c6413f --- /dev/null +++ b/bluetooth_low_energy/example/lib/router_config.dart @@ -0,0 +1,85 @@ +import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; +import 'package:clover/clover.dart'; +import 'package:collection/collection.dart'; +import 'package:go_router/go_router.dart'; + +import 'view_models.dart'; +import 'views.dart'; + +final routerConfig = GoRouter( + redirect: (context, state) { + if (state.matchedLocation == '/') { + return '/central'; + } + return null; + }, + routes: [ + StatefulShellRoute( + // builder: (context, state, navigationShell) { + // return HomeView(navigationShell: navigationShell); + // }, + builder: (context, state, navigationShell) => navigationShell, + navigatorContainerBuilder: (context, navigationShell, children) { + final navigators = children.mapIndexed( + (index, element) { + if (index == 0) { + return ViewModelBinding( + viewBuilder: (context) => element, + viewModelBuilder: (context) => CentralManagerViewModel(), + ); + } else { + return element; + } + }, + ).toList(); + return HomeView( + navigationShell: navigationShell, + navigators: navigators, + ); + }, + branches: [ + StatefulShellBranch( + routes: [ + GoRoute( + path: '/central', + builder: (context, state) { + return const CentralManagerView(); + }, + routes: [ + GoRoute( + path: ':uuid', + builder: (context, state) { + final uuidValue = state.pathParameters['uuid']!; + final uuid = UUID.fromString(uuidValue); + final viewModel = + ViewModel.of(context); + final eventArgs = viewModel.discoveries.firstWhere( + (discovery) => discovery.peripheral.uuid == uuid); + return ViewModelBinding( + viewBuilder: (context) => PeripheralView(), + viewModelBuilder: (context) => + PeripheralViewModel(eventArgs), + ); + }, + ), + ], + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/peripheral', + builder: (context, state) { + return ViewModelBinding( + viewBuilder: (context) => const PeripheralManagerView(), + viewModelBuilder: (context) => PeripheralManagerViewModel(), + ); + }, + ), + ], + ), + ], + ), + ], +); diff --git a/bluetooth_low_energy/example/lib/view_models.dart b/bluetooth_low_energy/example/lib/view_models.dart new file mode 100644 index 0000000..6c0c1fc --- /dev/null +++ b/bluetooth_low_energy/example/lib/view_models.dart @@ -0,0 +1,6 @@ +export 'view_models/central_manager_view_model.dart'; +export 'view_models/peripheral_view_model.dart'; +export 'view_models/service_view_model.dart'; +export 'view_models/characteristic_view_model.dart'; +export 'view_models/descriptor_view_model.dart'; +export 'view_models/peripheral_manager_view_model.dart'; diff --git a/bluetooth_low_energy/example/lib/view_models/central_manager_view_model.dart b/bluetooth_low_energy/example/lib/view_models/central_manager_view_model.dart new file mode 100644 index 0000000..c64ce7d --- /dev/null +++ b/bluetooth_low_energy/example/lib/view_models/central_manager_view_model.dart @@ -0,0 +1,76 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; +import 'package:clover/clover.dart'; +import 'package:logging/logging.dart'; + +class CentralManagerViewModel extends ViewModel { + final CentralManager _manager; + final List _discoveries; + bool _discovering; + + late final StreamSubscription _stateChangedSubscription; + late final StreamSubscription _discoveredSubscription; + + CentralManagerViewModel() + : _manager = CentralManager()..logLevel = Level.INFO, + _discoveries = [], + _discovering = false { + _stateChangedSubscription = _manager.stateChanged.listen((eventArgs) async { + if (eventArgs.state == BluetoothLowEnergyState.unauthorized && + Platform.isAndroid) { + await _manager.authorize(); + } + notifyListeners(); + }); + _discoveredSubscription = _manager.discovered.listen((eventArgs) { + final peripheral = eventArgs.peripheral; + final index = _discoveries.indexWhere((i) => i.peripheral == peripheral); + if (index < 0) { + _discoveries.add(eventArgs); + } else { + _discoveries[index] = eventArgs; + } + notifyListeners(); + }); + } + + BluetoothLowEnergyState get state => _manager.state; + bool get discovering => _discovering; + List get discoveries => _discoveries; + + Future showAppSettings() async { + await _manager.showAppSettings(); + } + + Future startDiscovery({ + List? serviceUUIDs, + }) async { + if (_discovering) { + return; + } + _discoveries.clear(); + await _manager.startDiscovery( + serviceUUIDs: serviceUUIDs, + ); + _discovering = true; + notifyListeners(); + } + + Future stopDiscovery() async { + if (!_discovering) { + return; + } + await _manager.stopDiscovery(); + _discovering = false; + notifyListeners(); + } + + @override + void dispose() { + _stateChangedSubscription.cancel(); + _discoveredSubscription.cancel(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy/example/lib/view_models/characteristic_view_model.dart b/bluetooth_low_energy/example/lib/view_models/characteristic_view_model.dart new file mode 100644 index 0000000..a8764f1 --- /dev/null +++ b/bluetooth_low_energy/example/lib/view_models/characteristic_view_model.dart @@ -0,0 +1,145 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; +import 'package:bluetooth_low_energy_example/models.dart'; +import 'package:clover/clover.dart'; + +import 'descriptor_view_model.dart'; + +class CharacteristicViewModel extends ViewModel { + final CentralManager _manager; + final Peripheral _peripheral; + final GATTCharacteristic _characteristic; + final List _descriptorViewModels; + final List _logs; + + GATTCharacteristicWriteType _writeType; + bool _notifyState; + + late final StreamSubscription _characteristicNotifiedSubscription; + + CharacteristicViewModel({ + required CentralManager manager, + required Peripheral peripheral, + required GATTCharacteristic characteristic, + }) : _manager = manager, + _peripheral = peripheral, + _characteristic = characteristic, + _descriptorViewModels = characteristic.descriptors + .map((descriptor) => DescriptorViewModel(descriptor)) + .toList(), + _logs = [], + _writeType = GATTCharacteristicWriteType.withResponse, + _notifyState = false { + if (!canWrite && canWriteWithoutResponse) { + _writeType = GATTCharacteristicWriteType.withoutResponse; + } + _characteristicNotifiedSubscription = + _manager.characteristicNotified.listen((eventArgs) { + if (eventArgs.characteristic != _characteristic) { + return; + } + final log = Log( + type: 'Notified', + message: '[${eventArgs.value.length}] ${eventArgs.value}', + ); + _logs.add(log); + notifyListeners(); + }); + } + + UUID get uuid => _characteristic.uuid; + bool get canRead => + _characteristic.properties.contains(GATTCharacteristicProperty.read); + bool get canWrite => + _characteristic.properties.contains(GATTCharacteristicProperty.write); + bool get canWriteWithoutResponse => _characteristic.properties + .contains(GATTCharacteristicProperty.writeWithoutResponse); + bool get canNotify => + _characteristic.properties.contains(GATTCharacteristicProperty.notify) || + _characteristic.properties.contains(GATTCharacteristicProperty.indicate); + List get descriptorViewModels => _descriptorViewModels; + List get logs => _logs; + GATTCharacteristicWriteType get writeType => _writeType; + bool get notifyState => _notifyState; + + Future read() async { + final value = await _manager.readCharacteristic( + _peripheral, + _characteristic, + ); + final log = Log( + type: 'Read', + message: '[${value.length}] $value', + ); + _logs.add(log); + notifyListeners(); + } + + void setWriteType(GATTCharacteristicWriteType type) { + if (type == GATTCharacteristicWriteType.withResponse && !canWrite) { + throw ArgumentError.value(type); + } + if (type == GATTCharacteristicWriteType.withoutResponse && + !canWriteWithoutResponse) { + throw ArgumentError.value(type); + } + _writeType = type; + notifyListeners(); + } + + Future write(Uint8List value) async { + // Fragments the value by maximumWriteLength. + final fragmentSize = await _manager.getMaximumWriteLength( + _peripheral, + type: writeType, + ); + var start = 0; + while (start < value.length) { + final end = start + fragmentSize; + final fragmentedValue = + end < value.length ? value.sublist(start, end) : value.sublist(start); + final type = writeType; + await _manager.writeCharacteristic( + _peripheral, + _characteristic, + value: fragmentedValue, + type: type, + ); + final log = Log( + type: type == GATTCharacteristicWriteType.withResponse + ? 'Write' + : 'Write without response', + message: '[${value.length}] $value', + ); + _logs.add(log); + notifyListeners(); + start = end; + } + } + + Future setNotifyState(bool state) async { + await _manager.setCharacteristicNotifyState( + _peripheral, + _characteristic, + state: state, + ); + _notifyState = state; + notifyListeners(); + } + + void clearLogs() { + _logs.clear(); + notifyListeners(); + } + + @override + void dispose() { + _characteristicNotifiedSubscription.cancel(); + for (var descriptorViewModel in descriptorViewModels) { + descriptorViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy/example/lib/view_models/descriptor_view_model.dart b/bluetooth_low_energy/example/lib/view_models/descriptor_view_model.dart new file mode 100644 index 0000000..b9d142f --- /dev/null +++ b/bluetooth_low_energy/example/lib/view_models/descriptor_view_model.dart @@ -0,0 +1,10 @@ +import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; +import 'package:clover/clover.dart'; + +class DescriptorViewModel extends ViewModel { + final GATTDescriptor _descriptor; + + DescriptorViewModel(this._descriptor); + + UUID get uuid => _descriptor.uuid; +} diff --git a/bluetooth_low_energy/example/lib/view_models/peripheral_manager_view_model.dart b/bluetooth_low_energy/example/lib/view_models/peripheral_manager_view_model.dart new file mode 100644 index 0000000..a67544a --- /dev/null +++ b/bluetooth_low_energy/example/lib/view_models/peripheral_manager_view_model.dart @@ -0,0 +1,174 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; +import 'package:bluetooth_low_energy_example/models.dart'; +import 'package:clover/clover.dart'; +import 'package:logging/logging.dart'; + +class PeripheralManagerViewModel extends ViewModel { + final PeripheralManager _manager; + final List _logs; + bool _advertising; + + late final StreamSubscription _stateChangedSubscription; + late final StreamSubscription _characteristicReadRequestedSubscription; + late final StreamSubscription _characteristicWriteRequestedSubscription; + late final StreamSubscription _characteristicNotifyStateChangedSubscription; + + PeripheralManagerViewModel() + : _manager = PeripheralManager()..logLevel = Level.INFO, + _logs = [], + _advertising = false { + _stateChangedSubscription = _manager.stateChanged.listen((eventArgs) async { + if (eventArgs.state == BluetoothLowEnergyState.unauthorized && + Platform.isAndroid) { + await _manager.authorize(); + } + notifyListeners(); + }); + _characteristicReadRequestedSubscription = + _manager.characteristicReadRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final log = Log( + type: 'Characteristic read requested', + message: '${central.uuid}, ${characteristic.uuid}, $offset', + ); + _logs.add(log); + notifyListeners(); + final elements = List.generate(100, (i) => i % 256); + final value = Uint8List.fromList(elements); + final trimmedValue = value.sublist(offset); + await _manager.respondReadRequestWithValue( + request, + value: trimmedValue, + ); + }); + _characteristicWriteRequestedSubscription = + _manager.characteristicWriteRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final value = request.value; + final log = Log( + type: 'Characteristic write requested', + message: + '[${value.length}] ${central.uuid}, ${characteristic.uuid}, $offset, $value', + ); + _logs.add(log); + notifyListeners(); + await _manager.respondWriteRequest(request); + }); + _characteristicNotifyStateChangedSubscription = + _manager.characteristicNotifyStateChanged.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final state = eventArgs.state; + final log = Log( + type: 'Characteristic notify state changed', + message: '${central.uuid}, ${characteristic.uuid}, $state', + ); + _logs.add(log); + notifyListeners(); + // Write someting to the central when notify started. + if (state) { + final maximumNotifyLength = + await _manager.getMaximumNotifyLength(central); + final elements = List.generate(maximumNotifyLength, (i) => i % 256); + final value = Uint8List.fromList(elements); + await _manager.notifyCharacteristic( + central, + characteristic, + value: value, + ); + } + }); + } + + BluetoothLowEnergyState get state => _manager.state; + bool get advertising => _advertising; + List get logs => _logs; + + Future showAppSettings() async { + await _manager.showAppSettings(); + } + + Future startAdvertising() async { + if (_advertising) { + return; + } + await _manager.removeAllServices(); + final elements = List.generate(100, (i) => i % 256); + final value = Uint8List.fromList(elements); + final service = GATTService( + uuid: UUID.short(100), + isPrimary: true, + includedServices: [], + characteristics: [ + GATTCharacteristic.immutable( + uuid: UUID.short(200), + value: value, + descriptors: [], + ), + GATTCharacteristic.mutable( + uuid: UUID.short(201), + properties: [ + GATTCharacteristicProperty.read, + GATTCharacteristicProperty.write, + GATTCharacteristicProperty.writeWithoutResponse, + GATTCharacteristicProperty.notify, + GATTCharacteristicProperty.indicate, + ], + permissions: [ + GATTCharacteristicPermission.read, + GATTCharacteristicPermission.write, + ], + descriptors: [], + ), + ], + ); + await _manager.addService(service); + final advertisement = Advertisement( + name: Platform.isWindows ? null : 'BLE-12138', + manufacturerSpecificData: Platform.isIOS || Platform.isMacOS + ? [] + : [ + ManufacturerSpecificData( + id: 0x2e19, + data: Uint8List.fromList([0x01, 0x02, 0x03]), + ) + ], + ); + await _manager.startAdvertising(advertisement); + _advertising = true; + notifyListeners(); + } + + Future stopAdvertising() async { + if (!_advertising) { + return; + } + await _manager.stopAdvertising(); + _advertising = false; + notifyListeners(); + } + + void clearLogs() { + _logs.clear(); + notifyListeners(); + } + + @override + void dispose() { + _stateChangedSubscription.cancel(); + _characteristicReadRequestedSubscription.cancel(); + _characteristicWriteRequestedSubscription.cancel(); + _characteristicNotifyStateChangedSubscription.cancel(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy/example/lib/view_models/peripheral_view_model.dart b/bluetooth_low_energy/example/lib/view_models/peripheral_view_model.dart new file mode 100644 index 0000000..fca58b0 --- /dev/null +++ b/bluetooth_low_energy/example/lib/view_models/peripheral_view_model.dart @@ -0,0 +1,88 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; +import 'package:clover/clover.dart'; +import 'package:hybrid_logging/hybrid_logging.dart'; + +import 'service_view_model.dart'; + +class PeripheralViewModel extends ViewModel with TypeLogger { + final CentralManager _manager; + final Peripheral _peripheral; + final String? _name; + bool _connected; + List _serviceViewModels; + + late final StreamSubscription _connectionStateChangedSubscription; + late final StreamSubscription _mtuChangedChangedSubscription; + + PeripheralViewModel(DiscoveredEventArgs eventArgs) + : _manager = CentralManager(), + _peripheral = eventArgs.peripheral, + _name = eventArgs.advertisement.name, + _connected = false, + _serviceViewModels = [] { + _connectionStateChangedSubscription = + _manager.connectionStateChanged.listen((eventArgs) { + if (eventArgs.peripheral != _peripheral) { + return; + } + if (eventArgs.state == ConnectionState.connected) { + _connected = true; + } else { + _connected = false; + _serviceViewModels = []; + } + notifyListeners(); + }); + if (Platform.isAndroid || Platform.isWindows) { + _mtuChangedChangedSubscription = _manager.mtuChanged.listen((eventArgs) { + if (eventArgs.peripheral != _peripheral) { + return; + } + logger.info('MTU chanaged: ${eventArgs.mtu}'); + }); + } + } + + UUID get uuid => _peripheral.uuid; + String? get name => _name; + bool get connected => _connected; + List get serviceViewModels => _serviceViewModels; + + Future connect() async { + await _manager.connect(_peripheral); + } + + Future disconnect() async { + await _manager.disconnect(_peripheral); + } + + Future discoverGATT() async { + final services = await _manager.discoverGATT(_peripheral); + _serviceViewModels = services + .map((service) => ServiceViewModel( + manager: _manager, + peripheral: _peripheral, + service: service, + )) + .toList(); + notifyListeners(); + } + + @override + void dispose() { + if (connected) { + disconnect(); + } + _connectionStateChangedSubscription.cancel(); + if (Platform.isAndroid || Platform.isWindows) { + _mtuChangedChangedSubscription.cancel(); + } + for (var serviceViewModel in serviceViewModels) { + serviceViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy/example/lib/view_models/service_view_model.dart b/bluetooth_low_energy/example/lib/view_models/service_view_model.dart new file mode 100644 index 0000000..ecfb4f3 --- /dev/null +++ b/bluetooth_low_energy/example/lib/view_models/service_view_model.dart @@ -0,0 +1,50 @@ +import 'dart:io'; + +import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; +import 'package:clover/clover.dart'; + +import 'characteristic_view_model.dart'; + +class ServiceViewModel extends ViewModel { + final GATTService _service; + + final List _includedServiceViewModels; + final List _characteristicViewModels; + + ServiceViewModel({ + required CentralManager manager, + required Peripheral peripheral, + required GATTService service, + }) : _service = service, + _includedServiceViewModels = Platform.isLinux + ? [] + : service.includedServices + .map((service) => ServiceViewModel( + manager: manager, + peripheral: peripheral, + service: service, + )) + .toList(), + _characteristicViewModels = service.characteristics + .map((characteristic) => CharacteristicViewModel( + manager: manager, + peripheral: peripheral, + characteristic: characteristic, + )) + .toList(); + + UUID get uuid => _service.uuid; + bool get isPrimary => _service.isPrimary; + List get includedServiceViewModels => + _includedServiceViewModels; + List get characteristicViewModels => + _characteristicViewModels; + + @override + void dispose() { + for (var characteristicViewModel in characteristicViewModels) { + characteristicViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy/example/lib/views.dart b/bluetooth_low_energy/example/lib/views.dart new file mode 100644 index 0000000..b0727cb --- /dev/null +++ b/bluetooth_low_energy/example/lib/views.dart @@ -0,0 +1,4 @@ +export 'views/home_view.dart'; +export 'views/central_manager_view.dart'; +export 'views/peripheral_manager_view.dart'; +export 'views/peripheral_view.dart'; diff --git a/bluetooth_low_energy/example/lib/views/advertisement_view.dart b/bluetooth_low_energy/example/lib/views/advertisement_view.dart new file mode 100644 index 0000000..b0321db --- /dev/null +++ b/bluetooth_low_energy/example/lib/views/advertisement_view.dart @@ -0,0 +1,69 @@ +import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; +import 'package:convert/convert.dart'; +import 'package:flutter/material.dart'; + +class AdvertisementView extends StatelessWidget { + final Advertisement advertisement; + + const AdvertisementView({ + super.key, + required this.advertisement, + }); + + @override + Widget build(BuildContext context) { + final manufacturerSpecificData = advertisement.manufacturerSpecificData; + return LayoutBuilder( + builder: (context, constraints) { + const idWidth = 100.0; + final valueWidth = constraints.maxWidth - idWidth - 16.0 * 2.0; + return DataTable( + columnSpacing: 0.0, + horizontalMargin: 16.0, + columns: [ + DataColumn( + label: Container( + width: idWidth, + alignment: Alignment.center, + child: const Text('Id'), + ), + ), + DataColumn( + label: Container( + width: valueWidth, + alignment: Alignment.center, + child: const Text('Value'), + ), + ), + ], + rows: manufacturerSpecificData.map((item) { + final id = '0x${item.id.toRadixString(16).padLeft(4, '0')}'; + final value = hex.encode(item.data); + return DataRow( + cells: [ + DataCell( + Container( + width: idWidth, + alignment: Alignment.center, + child: Text(id), + ), + ), + DataCell( + Container( + width: valueWidth, + alignment: Alignment.center, + child: Text( + value, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ); + }).toList(), + ); + }, + ); + } +} diff --git a/bluetooth_low_energy/example/lib/views/central_manager_view.dart b/bluetooth_low_energy/example/lib/views/central_manager_view.dart new file mode 100644 index 0000000..e3d6dab --- /dev/null +++ b/bluetooth_low_energy/example/lib/views/central_manager_view.dart @@ -0,0 +1,127 @@ +import 'dart:io'; + +import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; +import 'package:bluetooth_low_energy_example/view_models.dart'; +import 'package:bluetooth_low_energy_example/widgets.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import 'advertisement_view.dart'; + +class CentralManagerView extends StatelessWidget { + const CentralManagerView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final discovering = viewModel.discovering; + return Scaffold( + appBar: AppBar( + title: const Text('Central Manager'), + actions: [ + TextButton( + onPressed: state == BluetoothLowEnergyState.poweredOn + ? () async { + if (discovering) { + await viewModel.stopDiscovery(); + } else { + await viewModel.startDiscovery(); + } + } + : null, + child: Text(discovering ? 'END' : 'BEGIN'), + ), + ], + ), + body: buildBody(context), + ); + } + + Widget buildBody(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final isMobile = Platform.isAndroid || Platform.isIOS; + if (state == BluetoothLowEnergyState.unauthorized && isMobile) { + return Center( + child: TextButton( + onPressed: () => viewModel.showAppSettings(), + child: const Text('Go to settings'), + ), + ); + } else if (state == BluetoothLowEnergyState.poweredOn) { + final discoveries = viewModel.discoveries; + return ListView.separated( + itemBuilder: (context, index) { + final theme = Theme.of(context); + final discovery = discoveries[index]; + final uuid = discovery.peripheral.uuid; + final name = discovery.advertisement.name; + final rssi = discovery.rssi; + return ListTile( + onTap: () { + onTapDissovery(context, discovery); + }, + onLongPress: () { + onLongPressDiscovery(context, discovery); + }, + title: Text(name ?? ''), + subtitle: Text( + '$uuid', + style: theme.textTheme.bodySmall, + softWrap: false, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RSSIIndicator(rssi), + Text('$rssi'), + ], + ), + ); + }, + separatorBuilder: (context, i) { + return const Divider( + height: 0.0, + ); + }, + itemCount: discoveries.length, + ); + } else { + return Center( + child: Text( + '$state', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + } + + void onTapDissovery( + BuildContext context, DiscoveredEventArgs discovery) async { + final viewModel = ViewModel.of(context); + if (viewModel.discovering) { + await viewModel.stopDiscovery(); + if (!context.mounted) { + return; + } + } + final uuid = discovery.peripheral.uuid; + context.go('/central/$uuid'); + } + + void onLongPressDiscovery( + BuildContext context, DiscoveredEventArgs discovery) async { + await showModalBottomSheet( + context: context, + builder: (context) { + return AdvertisementView( + advertisement: discovery.advertisement, + ); + }, + ); + } +} diff --git a/bluetooth_low_energy/example/lib/views/characteristic_tree_node_view.dart b/bluetooth_low_energy/example/lib/views/characteristic_tree_node_view.dart new file mode 100644 index 0000000..baa1763 --- /dev/null +++ b/bluetooth_low_energy/example/lib/views/characteristic_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class CharacteristicTreeNodeView extends StatelessWidget { + const CharacteristicTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ); + } +} diff --git a/bluetooth_low_energy/example/lib/views/characteristic_view.dart b/bluetooth_low_energy/example/lib/views/characteristic_view.dart new file mode 100644 index 0000000..7f32d4f --- /dev/null +++ b/bluetooth_low_energy/example/lib/views/characteristic_view.dart @@ -0,0 +1,178 @@ +import 'dart:convert'; + +import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; +import 'package:bluetooth_low_energy_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +import 'log_view.dart'; + +class CharacteristicView extends StatefulWidget { + const CharacteristicView({super.key}); + + @override + State createState() => _CharacteristicViewState(); +} + +class _CharacteristicViewState extends State { + late final TextEditingController _textController; + + @override + void initState() { + super.initState(); + _textController = TextEditingController(); + } + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final logs = viewModel.logs; + return Column( + children: [ + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + return OverflowBox( + alignment: Alignment.topCenter, + maxHeight: constraints.maxHeight + 1.0, + child: Row( + children: [ + Expanded( + child: InputDecorator( + decoration: InputDecoration( + contentPadding: const EdgeInsets.all(12.0), + border: OutlineInputBorder( + borderRadius: BorderRadius.vertical( + top: const Radius.circular(12.0), + bottom: viewModel.canWrite || + viewModel.canWriteWithoutResponse + ? Radius.zero + : const Radius.circular(12.0), + ), + ), + ), + child: ListView.builder( + itemBuilder: (context, i) { + final log = logs[i]; + return LogView( + log: log, + ); + }, + itemCount: logs.length, + ), + ), + ), + Column( + children: [ + Visibility( + visible: viewModel.canNotify, + child: viewModel.notifyState + ? IconButton.filled( + onPressed: () async { + await viewModel.setNotifyState(false); + }, + icon: + const Icon(Symbols.notifications_active), + ) + : IconButton.filledTonal( + onPressed: () async { + await viewModel.setNotifyState(true); + }, + icon: const Icon(Symbols.notifications_off), + ), + ), + Visibility( + visible: viewModel.canRead, + child: IconButton.filled( + onPressed: () async { + await viewModel.read(); + }, + icon: const Icon(Symbols.arrow_downward), + ), + ), + Visibility( + visible: viewModel.canWrite || + viewModel.canWriteWithoutResponse, + child: viewModel.writeType == + GATTCharacteristicWriteType.withResponse + ? IconButton.filled( + onPressed: viewModel.canWriteWithoutResponse + ? () { + viewModel.setWriteType( + GATTCharacteristicWriteType + .withoutResponse); + } + : null, + icon: const Icon(Symbols.swap_vert), + ) + : IconButton.filledTonal( + onPressed: viewModel.canWrite + ? () { + viewModel.setWriteType( + GATTCharacteristicWriteType + .withResponse); + } + : null, + icon: const Icon(Symbols.arrow_upward), + ), + ), + IconButton.filled( + onPressed: () => viewModel.clearLogs(), + icon: const Icon(Symbols.delete), + ), + ], + ), + ], + ), + ); + }, + ), + ), + Visibility( + visible: viewModel.canWrite || viewModel.canWriteWithoutResponse, + child: Row( + children: [ + Expanded( + child: TextField( + controller: _textController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.symmetric( + horizontal: 12.0, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12.0), + ), + ), + ), + ), + ), + ValueListenableBuilder( + valueListenable: _textController, + builder: (context, tev, child) { + final text = tev.text; + return IconButton.filled( + onPressed: text.isEmpty + ? null + : () async { + final value = utf8.encode(text); + await viewModel.write(value); + }, + icon: const Icon(Symbols.pets), + ); + }, + ), + ], + ), + ), + ], + ); + } + + @override + void dispose() { + _textController.dispose(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy/example/lib/views/descriptor_tree_node_view.dart b/bluetooth_low_energy/example/lib/views/descriptor_tree_node_view.dart new file mode 100644 index 0000000..9cb6cd2 --- /dev/null +++ b/bluetooth_low_energy/example/lib/views/descriptor_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class DescriptorTreeNodeView extends StatelessWidget { + const DescriptorTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.tertiary, + ), + ); + } +} diff --git a/bluetooth_low_energy/example/lib/views/home_view.dart b/bluetooth_low_energy/example/lib/views/home_view.dart new file mode 100644 index 0000000..9b7d146 --- /dev/null +++ b/bluetooth_low_energy/example/lib/views/home_view.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class HomeView extends StatefulWidget { + final StatefulNavigationShell navigationShell; + final List navigators; + + const HomeView({ + super.key, + required this.navigationShell, + required this.navigators, + }); + + @override + State createState() => _HomeViewState(); +} + +class _HomeViewState extends State { + late final PageController _controller; + + @override + void initState() { + super.initState(); + _controller = PageController( + initialPage: widget.navigationShell.currentIndex, + ); + } + + @override + Widget build(BuildContext context) { + final navigationShell = widget.navigationShell; + final navigators = widget.navigators; + return Scaffold( + // body: navigationShell, + body: PageView.builder( + controller: _controller, + onPageChanged: (index) { + // Ignore tap events. + if (index == navigationShell.currentIndex) { + return; + } + navigationShell.goBranch( + index, + initialLocation: false, + ); + }, + itemBuilder: (context, index) { + return navigators[index]; + }, + itemCount: navigators.length, + ), + bottomNavigationBar: BottomNavigationBar( + onTap: (index) { + navigationShell.goBranch( + index, + initialLocation: index == navigationShell.currentIndex, + ); + }, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.radar), + label: 'Central', + ), + BottomNavigationBarItem( + icon: Icon(Icons.sensors), + label: 'Peripheral', + ), + ], + currentIndex: widget.navigationShell.currentIndex, + ), + ); + } + + @override + void didUpdateWidget(covariant HomeView oldWidget) { + super.didUpdateWidget(oldWidget); + final navigationShell = widget.navigationShell; + final page = _controller.page ?? _controller.initialPage; + final index = page.round(); + // Ignore swipe events. + if (index == navigationShell.currentIndex) { + return; + } + _controller.animateToPage( + navigationShell.currentIndex, + duration: const Duration(milliseconds: 300), + curve: Curves.linear, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy/example/lib/views/log_view.dart b/bluetooth_low_energy/example/lib/views/log_view.dart new file mode 100644 index 0000000..b3a2be4 --- /dev/null +++ b/bluetooth_low_energy/example/lib/views/log_view.dart @@ -0,0 +1,43 @@ +import 'package:bluetooth_low_energy_example/models.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class LogView extends StatelessWidget { + final Log log; + + const LogView({ + super.key, + required this.log, + }); + + @override + Widget build(BuildContext context) { + final formatter = DateFormat.Hms(); + final time = formatter.format(log.time); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich( + TextSpan( + text: '[$time] ', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.error, + ), + children: [ + TextSpan( + text: log.type, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + )), + ], + ), + ), + Text( + log.message, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ); + } +} diff --git a/bluetooth_low_energy/example/lib/views/peripheral_manager_view.dart b/bluetooth_low_energy/example/lib/views/peripheral_manager_view.dart new file mode 100644 index 0000000..26aeede --- /dev/null +++ b/bluetooth_low_energy/example/lib/views/peripheral_manager_view.dart @@ -0,0 +1,79 @@ +import 'dart:io'; + +import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; +import 'package:bluetooth_low_energy_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +import 'log_view.dart'; + +class PeripheralManagerView extends StatelessWidget { + const PeripheralManagerView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final advertising = viewModel.advertising; + return Scaffold( + appBar: AppBar( + title: const Text('Peripheral Manager'), + actions: [ + TextButton( + onPressed: state == BluetoothLowEnergyState.poweredOn + ? () async { + if (advertising) { + await viewModel.stopAdvertising(); + } else { + await viewModel.startAdvertising(); + } + } + : null, + child: Text(advertising ? 'END' : 'BEGIN'), + ), + ], + ), + body: buildBody(context), + floatingActionButton: state == BluetoothLowEnergyState.poweredOn + ? FloatingActionButton( + onPressed: () => viewModel.clearLogs(), + child: const Icon(Symbols.delete), + ) + : null, + ); + } + + Widget buildBody(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final isMobile = Platform.isAndroid || Platform.isIOS; + if (state == BluetoothLowEnergyState.unauthorized && isMobile) { + return Center( + child: TextButton( + onPressed: () => viewModel.showAppSettings(), + child: const Text('Go to settings'), + ), + ); + } else if (state == BluetoothLowEnergyState.poweredOn) { + final logs = viewModel.logs; + return ListView.builder( + padding: const EdgeInsets.all(16.0), + itemBuilder: (context, i) { + final log = logs[i]; + return LogView( + log: log, + ); + }, + itemCount: logs.length, + ); + } else { + return Center( + child: Text( + '$state', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + } +} diff --git a/bluetooth_low_energy/example/lib/views/peripheral_view.dart b/bluetooth_low_energy/example/lib/views/peripheral_view.dart new file mode 100644 index 0000000..461687e --- /dev/null +++ b/bluetooth_low_energy/example/lib/views/peripheral_view.dart @@ -0,0 +1,107 @@ +import 'package:bluetooth_low_energy_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_simple_treeview/flutter_simple_treeview.dart'; + +import 'characteristic_tree_node_view.dart'; +import 'characteristic_view.dart'; +import 'descriptor_tree_node_view.dart'; +import 'service_tree_node_view.dart'; + +class PeripheralView extends StatelessWidget { + final TreeController _treeController; + + PeripheralView({super.key}) + : _treeController = TreeController( + allNodesExpanded: false, + ); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final connected = viewModel.connected; + final serviceViewModels = viewModel.serviceViewModels; + return Scaffold( + appBar: AppBar( + title: Text(viewModel.name ?? '${viewModel.uuid}'), + actions: [ + TextButton( + onPressed: () async { + if (connected) { + await viewModel.disconnect(); + } else { + await viewModel.connect(); + await viewModel.discoverGATT(); + } + }, + child: Text(connected ? 'DISCONNECT' : 'CONNECT'), + ), + ], + ), + body: SingleChildScrollView( + child: TreeView( + treeController: _treeController, + indent: 0.0, + nodes: _buildServiceTreeNodes(serviceViewModels), + ), + ), + ); + } + + List _buildServiceTreeNodes(List viewModels) { + return viewModels.map((viewModel) { + final includedServiceViewModels = viewModel.includedServiceViewModels; + final characteristicViewModels = viewModel.characteristicViewModels; + return TreeNode( + children: [ + ..._buildServiceTreeNodes(includedServiceViewModels), + ..._buildCharacteristicTreeNodes(characteristicViewModels), + ], + content: InheritedViewModel( + view: const ServiceTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } + + List _buildCharacteristicTreeNodes( + List viewModels) { + return viewModels.map((viewModel) { + final descriptorViewModels = viewModel.descriptorViewModels; + return TreeNode( + children: [ + TreeNode( + content: Expanded( + child: Container( + margin: const EdgeInsets.only(right: 40.0), + height: 360.0, + child: InheritedViewModel( + view: const CharacteristicView(), + viewModel: viewModel, + ), + ), + ), + ), + ..._buildDescriptorTreeNodes(descriptorViewModels), + ], + content: InheritedViewModel( + view: const CharacteristicTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } + + List _buildDescriptorTreeNodes( + List viewModels) { + return viewModels.map((viewModel) { + return TreeNode( + content: InheritedViewModel( + view: const DescriptorTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } +} diff --git a/bluetooth_low_energy/example/lib/views/service_tree_node_view.dart b/bluetooth_low_energy/example/lib/views/service_tree_node_view.dart new file mode 100644 index 0000000..9e18329 --- /dev/null +++ b/bluetooth_low_energy/example/lib/views/service_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class ServiceTreeNodeView extends StatelessWidget { + const ServiceTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ); + } +} diff --git a/bluetooth_low_energy/example/lib/widgets.dart b/bluetooth_low_energy/example/lib/widgets.dart new file mode 100644 index 0000000..baccf61 --- /dev/null +++ b/bluetooth_low_energy/example/lib/widgets.dart @@ -0,0 +1 @@ +export 'widgets/rssi_indicator.dart'; diff --git a/bluetooth_low_energy/example/lib/widgets/rssi_indicator.dart b/bluetooth_low_energy/example/lib/widgets/rssi_indicator.dart new file mode 100644 index 0000000..ddd59df --- /dev/null +++ b/bluetooth_low_energy/example/lib/widgets/rssi_indicator.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class RSSIIndicator extends StatelessWidget { + final int rssi; + + const RSSIIndicator( + this.rssi, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final IconData icon; + if (rssi > -70) { + icon = Icons.wifi_rounded; + } else if (rssi > -100) { + icon = Icons.wifi_2_bar_rounded; + } else { + icon = Icons.wifi_1_bar_rounded; + } + return Icon(icon); + } +} diff --git a/bluetooth_low_energy/example/linux/CMakeLists.txt b/bluetooth_low_energy/example/linux/CMakeLists.txt index 4dd7f8e..d22ea38 100644 --- a/bluetooth_low_energy/example/linux/CMakeLists.txt +++ b/bluetooth_low_energy/example/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "bluetooth_low_energy_example") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "dev.yanshouwang.bluetooth_low_energy") +set(APPLICATION_ID "dev.hebei.bluetooth_low_energy") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. @@ -125,6 +125,12 @@ foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) COMPONENT Runtime) endforeach(bundled_library) +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") diff --git a/bluetooth_low_energy/example/linux/my_application.cc b/bluetooth_low_energy/example/linux/my_application.cc index 920ef9f..a21dbcc 100644 --- a/bluetooth_low_energy/example/linux/my_application.cc +++ b/bluetooth_low_energy/example/linux/my_application.cc @@ -81,6 +81,24 @@ static gboolean my_application_local_command_line(GApplication* application, gch return TRUE; } +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + // Implements GObject::dispose. static void my_application_dispose(GObject* object) { MyApplication* self = MY_APPLICATION(object); @@ -91,6 +109,8 @@ static void my_application_dispose(GObject* object) { static void my_application_class_init(MyApplicationClass* klass) { G_APPLICATION_CLASS(klass)->activate = my_application_activate; G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; G_OBJECT_CLASS(klass)->dispose = my_application_dispose; } diff --git a/bluetooth_low_energy/example/macos/Flutter/GeneratedPluginRegistrant.swift b/bluetooth_low_energy/example/macos/Flutter/GeneratedPluginRegistrant.swift index 0e96062..4579819 100644 --- a/bluetooth_low_energy/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/bluetooth_low_energy/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,5 +8,5 @@ import Foundation import bluetooth_low_energy_darwin func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - BluetoothLowEnergyDarwin.register(with: registry.registrar(forPlugin: "BluetoothLowEnergyDarwin")) + BluetoothLowEnergyDarwinPlugin.register(with: registry.registrar(forPlugin: "BluetoothLowEnergyDarwinPlugin")) } diff --git a/bluetooth_low_energy/example/macos/Podfile.lock b/bluetooth_low_energy/example/macos/Podfile.lock index c3133f1..8451a59 100644 --- a/bluetooth_low_energy/example/macos/Podfile.lock +++ b/bluetooth_low_energy/example/macos/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - bluetooth_low_energy_darwin (2.0.2): + - bluetooth_low_energy_darwin (0.0.1): - Flutter - FlutterMacOS - FlutterMacOS (1.0.0) @@ -15,9 +15,9 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral SPEC CHECKSUMS: - bluetooth_low_energy_darwin: e37c1af3337ebc99a60807137dc5e47c6195eac7 + bluetooth_low_energy_darwin: 30da11d012831c0dbe57487b5f23bf344b90ab11 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/bluetooth_low_energy/example/macos/Runner.xcodeproj/project.pbxproj b/bluetooth_low_energy/example/macos/Runner.xcodeproj/project.pbxproj index fe555c3..edf5fab 100644 --- a/bluetooth_low_energy/example/macos/Runner.xcodeproj/project.pbxproj +++ b/bluetooth_low_energy/example/macos/Runner.xcodeproj/project.pbxproj @@ -21,14 +21,14 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 11E67697E2DFF348C5E0FBA6 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12AE3247A53A0A6C66A10AB1 /* Pods_RunnerTests.framework */; }; 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 98831461FCCC0C712B565489 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7618EFBF5D56DEA9B5BC6858 /* Pods_Runner.framework */; }; + 5FDEA1A8816A331CA78E7F6D /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AC81A8405E01D3966B98ABCC /* Pods_RunnerTests.framework */; }; + 83CB8ED1FBA8CD35DAEB069E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 60A2C50BFA238B286DE35299 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -62,9 +62,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 032DEBF51E6D235331EAFB6D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 12AE3247A53A0A6C66A10AB1 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 2C73F6F52B9D612E7A3ED61B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; @@ -81,13 +78,16 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 42843373A331898F762AE9FA /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - 69A28A72349A6C45CE7FD12D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 7618EFBF5D56DEA9B5BC6858 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 50A13F12221F58302552523F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 60A2C50BFA238B286DE35299 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 7F49A581B2FF1737F1557371 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 7BDC5BC63E0570F1277EC2A6 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - BBBF70DBDF6B0B4E82F4E8A2 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 977C1A7873ABE953C0EA17FA /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + ABB87EDC364CAC4062F6DE96 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + AC81A8405E01D3966B98ABCC /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AEF60039D20E8A370442E404 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + D545088443E99CCE89A693B8 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -95,7 +95,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 11E67697E2DFF348C5E0FBA6 /* Pods_RunnerTests.framework in Frameworks */, + 5FDEA1A8816A331CA78E7F6D /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -103,13 +103,27 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 98831461FCCC0C712B565489 /* Pods_Runner.framework in Frameworks */, + 83CB8ED1FBA8CD35DAEB069E /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 2E14B787045E896EB2B5DE61 /* Pods */ = { + isa = PBXGroup; + children = ( + 50A13F12221F58302552523F /* Pods-Runner.debug.xcconfig */, + 977C1A7873ABE953C0EA17FA /* Pods-Runner.release.xcconfig */, + D545088443E99CCE89A693B8 /* Pods-Runner.profile.xcconfig */, + 7BDC5BC63E0570F1277EC2A6 /* Pods-RunnerTests.debug.xcconfig */, + AEF60039D20E8A370442E404 /* Pods-RunnerTests.release.xcconfig */, + ABB87EDC364CAC4062F6DE96 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 331C80D6294CF71000263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -137,7 +151,7 @@ 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, - 5413C8972DC878C53291A3D1 /* Pods */, + 2E14B787045E896EB2B5DE61 /* Pods */, ); sourceTree = ""; }; @@ -185,24 +199,11 @@ path = Runner; sourceTree = ""; }; - 5413C8972DC878C53291A3D1 /* Pods */ = { - isa = PBXGroup; - children = ( - 2C73F6F52B9D612E7A3ED61B /* Pods-Runner.debug.xcconfig */, - 69A28A72349A6C45CE7FD12D /* Pods-Runner.release.xcconfig */, - 032DEBF51E6D235331EAFB6D /* Pods-Runner.profile.xcconfig */, - 42843373A331898F762AE9FA /* Pods-RunnerTests.debug.xcconfig */, - 7F49A581B2FF1737F1557371 /* Pods-RunnerTests.release.xcconfig */, - BBBF70DBDF6B0B4E82F4E8A2 /* Pods-RunnerTests.profile.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( - 7618EFBF5D56DEA9B5BC6858 /* Pods_Runner.framework */, - 12AE3247A53A0A6C66A10AB1 /* Pods_RunnerTests.framework */, + 60A2C50BFA238B286DE35299 /* Pods_Runner.framework */, + AC81A8405E01D3966B98ABCC /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -214,7 +215,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 1AB4F7EB04EA489C9D3447CC /* [CP] Check Pods Manifest.lock */, + 6E908EFB6D636F7BD9B901EE /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -233,13 +234,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - B609B32CB0330D926A7BF173 /* [CP] Check Pods Manifest.lock */, + C410E8FE456DC70BDCFC7228 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - C553F2A78B180316DDD47899 /* [CP] Embed Pods Frameworks */, + CBC02CA05960FFA536B17BBB /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -257,8 +258,9 @@ 33CC10E52044A3C60003C045 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C80D4294CF70F00263BE5 = { @@ -321,28 +323,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 1AB4F7EB04EA489C9D3447CC /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -381,7 +361,29 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - B609B32CB0330D926A7BF173 /* [CP] Check Pods Manifest.lock */ = { + 6E908EFB6D636F7BD9B901EE /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + C410E8FE456DC70BDCFC7228 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -403,7 +405,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - C553F2A78B180316DDD47899 /* [CP] Embed Pods Frameworks */ = { + CBC02CA05960FFA536B17BBB /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -471,13 +473,13 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 42843373A331898F762AE9FA /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 7BDC5BC63E0570F1277EC2A6 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_example"; @@ -486,13 +488,13 @@ }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7F49A581B2FF1737F1557371 /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = AEF60039D20E8A370442E404 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_example"; @@ -501,13 +503,13 @@ }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BBBF70DBDF6B0B4E82F4E8A2 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = ABB87EDC364CAC4062F6DE96 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_example"; @@ -519,6 +521,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -542,9 +545,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -592,6 +597,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -615,9 +621,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -645,6 +653,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -668,9 +677,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; diff --git a/bluetooth_low_energy/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/bluetooth_low_energy/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 29ceb04..b526360 100644 --- a/bluetooth_low_energy/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/bluetooth_low_energy/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ $(FLUTTER_BUILD_NUMBER) LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) - NSBluetoothAlwaysUsageDescription - Hello Bluetooth LowEnergy! NSHumanReadableCopyright $(PRODUCT_COPYRIGHT) NSMainNibFile diff --git a/bluetooth_low_energy/example/macos/RunnerTests/RunnerTests.swift b/bluetooth_low_energy/example/macos/RunnerTests/RunnerTests.swift index 3db3281..694e057 100644 --- a/bluetooth_low_energy/example/macos/RunnerTests/RunnerTests.swift +++ b/bluetooth_low_energy/example/macos/RunnerTests/RunnerTests.swift @@ -1,5 +1,5 @@ -import FlutterMacOS import Cocoa +import FlutterMacOS import XCTest @testable import bluetooth_low_energy diff --git a/bluetooth_low_energy/example/pubspec.lock b/bluetooth_low_energy/example/pubspec.lock index 7bc18b5..3521f09 100644 --- a/bluetooth_low_energy/example/pubspec.lock +++ b/bluetooth_low_energy/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" async: dependency: transitive description: @@ -23,47 +23,47 @@ packages: path: ".." relative: true source: path - version: "5.0.7" + version: "6.0.0" bluetooth_low_energy_android: dependency: transitive description: name: bluetooth_low_energy_android - sha256: "281ca129677e10cd22d4763bbb7a9fb53d801cf25fcc54815e4023073e35f22c" + sha256: "39284bb33a10cc3bc126e18968865f47fc0155495a7473afc705eb4be8b07728" url: "https://pub.dev" source: hosted - version: "5.0.5" + version: "6.0.0" bluetooth_low_energy_darwin: dependency: transitive description: name: bluetooth_low_energy_darwin - sha256: d8c37f27c48ad523e5c32d1002d8e67570b1f4e6d69c54e09bca152a07f6ab3f + sha256: "849ba53f7d34845ad7491cd9cdb3784301aa54fe682c91cab804ed55cfd259d5" url: "https://pub.dev" source: hosted - version: "5.0.5" + version: "6.0.0" bluetooth_low_energy_linux: dependency: transitive description: name: bluetooth_low_energy_linux - sha256: "2809830a8b67eac5d53fdc6e907aa878ec61884d6e6a42da2b79b826410c8327" + sha256: "4d1aaaede517f95320dcf9ad271091ab42c4ad8ba5bfa0e822744d850dcf0048" url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "6.0.0" bluetooth_low_energy_platform_interface: dependency: transitive description: name: bluetooth_low_energy_platform_interface - sha256: "5af74eb8f97a896dfdbcff2da284d4fe5b4e2e49ebb2f46f826ac1810f66e4d7" + sha256: bc2e8d97c141653e5747bcb3cdc9fe956541b6ecc6e5f158b99a2f3abc2d946a url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "6.0.0" bluetooth_low_energy_windows: dependency: transitive description: name: bluetooth_low_energy_windows - sha256: "5a0b22003d14dc84f3f16e73a77f2645df034badb9003f368e6f9bc02e8b249a" + sha256: "4904530cb3e7e1dd7a66919b4c926f8a03ed9924c3c2ce068aef7e0e10ced555" url: "https://pub.dev" source: hosted - version: "5.0.3" + version: "6.0.0" bluez: dependency: transitive description: @@ -96,8 +96,16 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + clover: + dependency: "direct main" + description: + name: clover + sha256: eba28e69b32f174a51c093eef098cf5ae646470322727081d5d3d8f66c786487 + url: "https://pub.dev" + source: hosted + version: "3.0.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a @@ -116,10 +124,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" dbus: dependency: transitive description: @@ -166,7 +174,15 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + flutter_simple_treeview: + dependency: "direct main" + description: + name: flutter_simple_treeview + sha256: ad4978d2668dd078d3a09966832da111bef9102dd636e572c50c80133b7ff4d9 url: "https://pub.dev" source: hosted version: "3.0.2" @@ -175,11 +191,32 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter source: sdk version: "0.0.0" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "6ad5662b014c06c20fa46ab78715c96b2222a7fe4f87bf77e0289592c2539e86" + url: "https://pub.dev" + source: hosted + version: "14.1.3" + hybrid_logging: + dependency: "direct main" + description: + name: hybrid_logging + sha256: "54248d52ce68c14702a42fbc4083bac5c6be30f6afad8a41be4bbadd197b8af5" + url: "https://pub.dev" + source: hosted + version: "1.0.0" integration_test: dependency: "direct dev" description: flutter @@ -197,44 +234,36 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" - log_service: - dependency: transitive - description: - name: log_service - sha256: "21124936899e227d1779268077921d46d57456e2592d1562e455be273594e2e4" - url: "https://pub.dev" - source: hosted - version: "1.0.0" + version: "4.0.0" logging: - dependency: transitive + dependency: "direct main" description: name: logging sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" @@ -257,14 +286,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.0" + material_symbols_icons: + dependency: "direct main" + description: + name: material_symbols_icons + sha256: b2d3cbc3c42b8a217715b0d97ff03aebb14b2b4592875736e5599c603fb2db7e + url: "https://pub.dev" + source: hosted + version: "4.2758.0" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" path: dependency: transitive description: @@ -362,10 +399,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -386,10 +423,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" webdriver: dependency: transitive description: @@ -407,5 +444,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.3.0-279.1.beta <4.0.0" - flutter: ">=3.0.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/bluetooth_low_energy/example/pubspec.yaml b/bluetooth_low_energy/example/pubspec.yaml index 703e02c..a15a418 100644 --- a/bluetooth_low_energy/example/pubspec.yaml +++ b/bluetooth_low_energy/example/pubspec.yaml @@ -1,11 +1,11 @@ name: bluetooth_low_energy_example -description: Demonstrates how to use the bluetooth_low_energy plugin. +description: "Demonstrates how to use the bluetooth_low_energy plugin." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: '>=3.0.0 <4.0.0' + sdk: '>=3.4.0 <4.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -27,9 +27,16 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - convert: ^3.1.1 + cupertino_icons: ^1.0.6 + clover: ^3.0.0 + go_router: ^14.1.3 + logging: ^1.2.0 + hybrid_logging: ^1.0.0 intl: ^0.19.0 + collection: ^1.18.0 + convert: ^3.1.1 + flutter_simple_treeview: ^3.0.2 + material_symbols_icons: ^4.2744.0 dev_dependencies: integration_test: @@ -42,7 +49,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: ^3.0.0 + flutter_lints: ^4.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/example/test/widget_test.dart b/bluetooth_low_energy/example/test/widget_test.dart index b15e87d..2a2b819 100644 --- a/bluetooth_low_energy/example/test/widget_test.dart +++ b/bluetooth_low_energy/example/test/widget_test.dart @@ -5,23 +5,4 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:bluetooth_low_energy_example/main.dart'; - -void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); -} +void main() {} diff --git a/bluetooth_low_energy/example/windows/CMakeLists.txt b/bluetooth_low_energy/example/windows/CMakeLists.txt index 231ec05..89e3c26 100644 --- a/bluetooth_low_energy/example/windows/CMakeLists.txt +++ b/bluetooth_low_energy/example/windows/CMakeLists.txt @@ -89,6 +89,12 @@ if(PLUGIN_BUNDLED_LIBRARIES) COMPONENT Runtime) endif() +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 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 d051da9..37c2554 100644 --- a/bluetooth_low_energy/example/windows/flutter/generated_plugin_registrant.cc +++ b/bluetooth_low_energy/example/windows/flutter/generated_plugin_registrant.cc @@ -6,9 +6,9 @@ #include "generated_plugin_registrant.h" -#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { - BluetoothLowEnergyWindowsCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("BluetoothLowEnergyWindowsCApi")); + BluetoothLowEnergyWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("BluetoothLowEnergyWindowsPluginCApi")); } diff --git a/bluetooth_low_energy/example/windows/runner/Runner.rc b/bluetooth_low_energy/example/windows/runner/Runner.rc index cdbc0d4..12e40e0 100644 --- a/bluetooth_low_energy/example/windows/runner/Runner.rc +++ b/bluetooth_low_energy/example/windows/runner/Runner.rc @@ -89,11 +89,11 @@ BEGIN BEGIN BLOCK "040904e4" BEGIN - VALUE "CompanyName", "dev.yanshouwang" "\0" + VALUE "CompanyName", "dev.hebei" "\0" VALUE "FileDescription", "bluetooth_low_energy_example" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "InternalName", "bluetooth_low_energy_example" "\0" - VALUE "LegalCopyright", "Copyright (C) 2023 dev.yanshouwang. All rights reserved." "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 dev.hebei. All rights reserved." "\0" VALUE "OriginalFilename", "bluetooth_low_energy_example.exe" "\0" VALUE "ProductName", "bluetooth_low_energy_example" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" diff --git a/bluetooth_low_energy/example/windows/runner/utils.cpp b/bluetooth_low_energy/example/windows/runner/utils.cpp index b2b0873..3a0b465 100644 --- a/bluetooth_low_energy/example/windows/runner/utils.cpp +++ b/bluetooth_low_energy/example/windows/runner/utils.cpp @@ -45,13 +45,13 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) { if (utf16_string == nullptr) { return std::string(); } - int target_length = ::WideCharToMultiByte( + unsigned int target_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, nullptr, 0, nullptr, nullptr) -1; // remove the trailing null character int input_length = (int)wcslen(utf16_string); std::string utf8_string; - if (target_length <= 0 || target_length > utf8_string.max_size()) { + if (target_length == 0 || target_length > utf8_string.max_size()) { return utf8_string; } utf8_string.resize(target_length); diff --git a/bluetooth_low_energy/lib/bluetooth_low_energy.dart b/bluetooth_low_energy/lib/bluetooth_low_energy.dart index 31d4ddd..9648025 100644 --- a/bluetooth_low_energy/lib/bluetooth_low_energy.dart +++ b/bluetooth_low_energy/lib/bluetooth_low_energy.dart @@ -4,11 +4,10 @@ library bluetooth_low_energy; export 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart' hide - MyBluetoothLowEnergyPeer, - MyCentral, - MyPeripheral, - MyGattAttribute, - MyGattAttributeUint8List, - MyGattDescriptor, - MyGattCharacteristic, - MyGattService; + PlatformBluetoothLowEnergyManager, + PlatformCentralManager, + PlatformPeripheralManager, + MutableGATTCharacteristic, + ImmutableGATTCharacteristic, + MutableGATTDescriptor, + ImmutableGATTDescriptor; diff --git a/bluetooth_low_energy/pubspec.yaml b/bluetooth_low_energy/pubspec.yaml index 0aa1533..20807a4 100644 --- a/bluetooth_low_energy/pubspec.yaml +++ b/bluetooth_low_energy/pubspec.yaml @@ -1,25 +1,34 @@ name: bluetooth_low_energy -description: A Flutter plugin for controlling the bluetooth low energy, supports central and peripheral apis. -version: 5.0.7 +description: "A Flutter plugin for controlling the bluetooth low energy, supports central and peripheral roles." +version: 6.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy +repository: https://github.com/yanshouwang/bluetooth_low_energy +issue_tracker: https://github.com/yanshouwang/bluetooth_low_energy/issues +topics: + - bluetooth + - bluetooth-low-energy + - ble +funding: + - https://paypal.me/yanshouwang5112 + - https://afdian.net/a/yanshouwang environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.0.0" + sdk: '>=3.0.0 <4.0.0' + flutter: '>=3.7.0' dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^5.0.2 - bluetooth_low_energy_android: ^5.0.5 - bluetooth_low_energy_darwin: ^5.0.5 - bluetooth_low_energy_windows: ^5.0.3 - bluetooth_low_energy_linux: ^5.0.2 + bluetooth_low_energy_platform_interface: ^6.0.0 + bluetooth_low_energy_android: ^6.0.0 + bluetooth_low_energy_darwin: ^6.0.0 + bluetooth_low_energy_windows: ^6.0.0 + bluetooth_low_energy_linux: ^6.0.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.0 + flutter_lints: ^4.0.0 flutter: plugin: diff --git a/bluetooth_low_energy/test/bluetooth_low_energy_test.dart b/bluetooth_low_energy/test/bluetooth_low_energy_test.dart new file mode 100644 index 0000000..ab73b3a --- /dev/null +++ b/bluetooth_low_energy/test/bluetooth_low_energy_test.dart @@ -0,0 +1 @@ +void main() {} diff --git a/bluetooth_low_energy_android/.gitignore b/bluetooth_low_energy_android/.gitignore index 96486fd..ac5aa98 100644 --- a/bluetooth_low_energy_android/.gitignore +++ b/bluetooth_low_energy_android/.gitignore @@ -26,5 +26,4 @@ migrate_working_dir/ /pubspec.lock **/doc/api/ .dart_tool/ -.packages build/ diff --git a/bluetooth_low_energy_android/.metadata b/bluetooth_low_energy_android/.metadata index f43a237..43d5ccc 100644 --- a/bluetooth_low_energy_android/.metadata +++ b/bluetooth_low_energy_android/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "efbf63d9c66b9f6ec30e9ad4611189aa80003d31" + revision: "5dcb86f68f239346676ceb1ed1ea385bd215fba1" channel: "stable" project_type: plugin @@ -13,11 +13,11 @@ project_type: plugin migration: platforms: - platform: root - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 - platform: android - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 # User provided section diff --git a/bluetooth_low_energy_android/CHANGELOG.md b/bluetooth_low_energy_android/CHANGELOG.md index 8ff290c..21f9f97 100644 --- a/bluetooth_low_energy_android/CHANGELOG.md +++ b/bluetooth_low_energy_android/CHANGELOG.md @@ -1,3 +1,60 @@ +## 6.0.0 + +* Add `serviceUUIDs` argument to `CentralManager#startDiscovery` method. +* Add `CentralManager#requestMTU` method. +* Add `CentralManager#retrieveConnectedPeripherals` method. +* Move `BluetoothLowEnergyManager#getState` to `BluetoothLowEnergyManager#state`. +* Move `CentralManger.instance` to factory constructor. +* Move `PeripheralManager.instance` to factory constructor. +* Remove `BluetoothLowEnergyManager#setUp` method. +* Add `CentralManager#mtuChanged` event. +* Add `PeripheralManager#mtuChanged` event. +* Add `BluetoothLowEnergyManager.authorize()` method. +* Add `BluetoothLowEnergyManager.showAppSettings()` method. +* Rewrite example with MVVM. +* Fix known issues. + +## 6.0.0-dev.6 + +* Fix known issues. + +## 6.0.0-dev.5 + +* Rewrite example with MVVM. +* Fix known issues. + +## 6.0.0-dev.4 + +* Implement new APIs. + +## 6.0.0-dev.3 + +* Move organizaioin. + +## 6.0.0-dev.2 + +* Implement new APIs. + +## 6.0.0-dev.1 + +* Add `CentralManager#mtuChanged` event. +* Add `PeripheralManager#mtuChanged` event. +* Add `BluetoothLowEnergyManager.authorize()` method. +* Add `BluetoothLowEnergyManager.showAppSettings()` method. +* Add modifiers to all classes. +* Use new capitalization rules. +* Make it possible to change the `logLevel` before `initialize()`. + +## 6.0.0-dev.0 + +* Add `serviceUUIDs` argument to `CentralManager#startDiscovery` method. +* Add `CentralManager#requestMTU` method. +* Add `CentralManager#retrieveConnectedPeripherals` method. +* Move `BluetoothLowEnergyManager#getState` to `BluetoothLowEnergyManager#state`. +* Move `CentralManger.instance` to factory constructor. +* Move `PeripheralManager.instance` to factory constructor. +* Remove `BluetoothLowEnergyManager#setUp` method. + ## 5.0.5 * Fix the issue that [Advertisement resolve failed with `NullPointerException`](https://github.com/yanshouwang/bluetooth_low_energy/issues/59) diff --git a/bluetooth_low_energy_android/LICENSE b/bluetooth_low_energy_android/LICENSE index 752d28b..3977562 100644 --- a/bluetooth_low_energy_android/LICENSE +++ b/bluetooth_low_energy_android/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 yanshouwang +Copyright (c) 2024 hebei.dev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/bluetooth_low_energy_android/android/build.gradle b/bluetooth_low_energy_android/android/build.gradle index 3f6b712..90f7439 100644 --- a/bluetooth_low_energy_android/android/build.gradle +++ b/bluetooth_low_energy_android/android/build.gradle @@ -1,16 +1,16 @@ -group 'dev.yanshouwang.bluetooth_low_energy_android' -version '1.0-SNAPSHOT' +group = "dev.hebei.bluetooth_low_energy_android" +version = "1.0-SNAPSHOT" buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = "1.7.10" repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath("com.android.tools.build:gradle:7.3.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") } } @@ -21,37 +21,37 @@ allprojects { } } -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' +apply plugin: "com.android.library" +apply plugin: "kotlin-android" android { if (project.android.hasProperty("namespace")) { - namespace 'dev.yanshouwang.bluetooth_low_energy_android' + namespace = "dev.hebei.bluetooth_low_energy_android" } - compileSdkVersion 33 + compileSdk = 34 compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = "1.8" } sourceSets { - main.java.srcDirs += 'src/main/kotlin' - test.java.srcDirs += 'src/test/kotlin' + main.java.srcDirs += "src/main/kotlin" + test.java.srcDirs += "src/test/kotlin" } defaultConfig { - minSdkVersion 21 + minSdk = 21 } dependencies { - testImplementation 'org.jetbrains.kotlin:kotlin-test' - testImplementation 'org.mockito:mockito-core:5.0.0' + testImplementation("org.jetbrains.kotlin:kotlin-test") + testImplementation("org.mockito:mockito-core:5.0.0") } testOptions { diff --git a/bluetooth_low_energy_android/android/src/main/AndroidManifest.xml b/bluetooth_low_energy_android/android/src/main/AndroidManifest.xml index f16b78f..c51c56d 100644 --- a/bluetooth_low_energy_android/android/src/main/AndroidManifest.xml +++ b/bluetooth_low_energy_android/android/src/main/AndroidManifest.xml @@ -1,15 +1,15 @@ + package="dev.hebei.bluetooth_low_energy_android"> - - + + - - + + - - - - \ No newline at end of file + + + + 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/hebei/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPlugin.kt similarity index 77% rename from bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/BluetoothLowEnergyAndroid.kt rename to bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPlugin.kt index 3ee4d2b..a824fa6 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/hebei/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPlugin.kt @@ -1,11 +1,11 @@ -package dev.yanshouwang.bluetooth_low_energy_android +package dev.hebei.bluetooth_low_energy_android import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding -/** BluetoothLowEnergyAndroid */ -class BluetoothLowEnergyAndroid : FlutterPlugin, ActivityAware { +/** BluetoothLowEnergyAndroidPlugin */ +class BluetoothLowEnergyAndroidPlugin : FlutterPlugin, ActivityAware { private lateinit var mCentralManager: MyCentralManager private lateinit var mPeripheralManager: MyPeripheralManager @@ -14,14 +14,14 @@ class BluetoothLowEnergyAndroid : FlutterPlugin, ActivityAware { val binaryMessenger = binding.binaryMessenger mCentralManager = MyCentralManager(context, binaryMessenger) mPeripheralManager = MyPeripheralManager(context, binaryMessenger) - MyCentralManagerHostApi.setUp(binaryMessenger, mCentralManager) - MyPeripheralManagerHostApi.setUp(binaryMessenger, mPeripheralManager) + MyCentralManagerHostAPI.setUp(binaryMessenger, mCentralManager) + MyPeripheralManagerHostAPI.setUp(binaryMessenger, mPeripheralManager) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { val binaryMessenger = binding.binaryMessenger - MyCentralManagerHostApi.setUp(binaryMessenger, null) - MyPeripheralManagerHostApi.setUp(binaryMessenger, null) + MyCentralManagerHostAPI.setUp(binaryMessenger, null) + MyPeripheralManagerHostAPI.setUp(binaryMessenger, null) } override fun onAttachedToActivity(binding: ActivityPluginBinding) { 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/hebei/bluetooth_low_energy_android/MyAPI.g.kt similarity index 51% rename from bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.g.kt rename to bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyAPI.g.kt index 0be0287..eaf01e3 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/hebei/bluetooth_low_energy_android/MyAPI.g.kt @@ -1,7 +1,8 @@ -// Autogenerated from Pigeon (v15.0.2), do not edit directly. +// Autogenerated from Pigeon (v19.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") -package dev.yanshouwang.bluetooth_low_energy_android +package dev.hebei.bluetooth_low_energy_android import android.util.Log import io.flutter.plugin.common.BasicMessageChannel @@ -16,14 +17,14 @@ private fun wrapResult(result: Any?): List { } private fun wrapError(exception: Throwable): List { - if (exception is FlutterError) { - return listOf( + return if (exception is FlutterError) { + listOf( exception.code, exception.message, exception.details ) } else { - return listOf( + listOf( exception.javaClass.simpleName, exception.toString(), "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) @@ -51,9 +52,9 @@ enum class MyBluetoothLowEnergyStateArgs(val raw: Int) { UNSUPPORTED(1), UNAUTHORIZED(2), OFF(3), - TURNINGON(4), + TURNING_ON(4), ON(5), - TURNINGOFF(6); + TURNING_OFF(6); companion object { fun ofRaw(raw: Int): MyBluetoothLowEnergyStateArgs? { @@ -62,62 +63,127 @@ enum class MyBluetoothLowEnergyStateArgs(val raw: Int) { } } -enum class MyGattCharacteristicPropertyArgs(val raw: Int) { +enum class MyConnectionStateArgs(val raw: Int) { + DISCONNECTED(0), + CONNECTING(1), + CONNECTED(2), + DISCONNECTING(3); + + companion object { + fun ofRaw(raw: Int): MyConnectionStateArgs? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class MyGATTCharacteristicPropertyArgs(val raw: Int) { READ(0), WRITE(1), - WRITEWITHOUTRESPONSE(2), + WRITE_WITHOUT_RESPONSE(2), NOTIFY(3), INDICATE(4); companion object { - fun ofRaw(raw: Int): MyGattCharacteristicPropertyArgs? { + fun ofRaw(raw: Int): MyGATTCharacteristicPropertyArgs? { return values().firstOrNull { it.raw == raw } } } } -enum class MyGattCharacteristicWriteTypeArgs(val raw: Int) { - WITHRESPONSE(0), - WITHOUTRESPONSE(1); +enum class MyGATTCharacteristicPermissionArgs(val raw: Int) { + READ(0), + READ_ENCRYPTED(1), + WRITE(2), + WRITE_ENCRYPTED(3); companion object { - fun ofRaw(raw: Int): MyGattCharacteristicWriteTypeArgs? { + fun ofRaw(raw: Int): MyGATTCharacteristicPermissionArgs? { return values().firstOrNull { it.raw == raw } } } } -enum class MyGattCharacteristicNotifyStateArgs(val raw: Int) { - NONE(0), - NOTIFY(1), - INDICATE(2); +enum class MyGATTCharacteristicWriteTypeArgs(val raw: Int) { + WITH_RESPONSE(0), + WITHOUT_RESPONSE(1); companion object { - fun ofRaw(raw: Int): MyGattCharacteristicNotifyStateArgs? { + fun ofRaw(raw: Int): MyGATTCharacteristicWriteTypeArgs? { return values().firstOrNull { it.raw == raw } } } } -enum class MyGattStatusArgs(val raw: Int) { +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); + READ_NOT_PERMITTED(1), + WRITE_NOT_PERMITTED(2), + INSUFFICIENT_AUTHENTICATION(3), + REQUEST_NOT_SUPPORTED(4), + INSUFFICIENT_ENCRYPTION(5), + INVALID_OFFSET(6), + INSUFFICIENT_AUTHORIZATION(7), + INVALID_ATTRIBUTE_LENGTH(8), + CONNECTION_CONGESTED(9), + FAILURE(10); companion object { - fun ofRaw(raw: Int): MyGattStatusArgs? { + fun ofRaw(raw: Int): MyGATTStatusArgs? { return values().firstOrNull { it.raw == raw } } } } +/** Generated class from Pigeon that represents data sent in messages. */ +data class MyCentralManagerArgs ( + val enableNotificationValue: ByteArray, + val enableIndicationValue: ByteArray, + val disableNotificationValue: ByteArray + +) { + companion object { + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyCentralManagerArgs { + val enableNotificationValue = __pigeon_list[0] as ByteArray + val enableIndicationValue = __pigeon_list[1] as ByteArray + val disableNotificationValue = __pigeon_list[2] as ByteArray + return MyCentralManagerArgs(enableNotificationValue, enableIndicationValue, disableNotificationValue) + } + } + fun toList(): List { + return listOf( + enableNotificationValue, + enableIndicationValue, + disableNotificationValue, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class MyPeripheralManagerArgs ( + val enableNotificationValue: ByteArray, + val enableIndicationValue: ByteArray, + val disableNotificationValue: ByteArray + +) { + companion object { + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyPeripheralManagerArgs { + val enableNotificationValue = __pigeon_list[0] as ByteArray + val enableIndicationValue = __pigeon_list[1] as ByteArray + val disableNotificationValue = __pigeon_list[2] as ByteArray + return MyPeripheralManagerArgs(enableNotificationValue, enableIndicationValue, disableNotificationValue) + } + } + fun toList(): List { + return listOf( + enableNotificationValue, + enableIndicationValue, + disableNotificationValue, + ) + } +} + /** Generated class from Pigeon that represents data sent in messages. */ data class MyManufacturerSpecificDataArgs ( val idArgs: Long, @@ -125,10 +191,10 @@ data class MyManufacturerSpecificDataArgs ( ) { 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 + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyManufacturerSpecificDataArgs { + val idArgs = __pigeon_list[0].let { num -> if (num is Int) num.toLong() else num as Long } + val dataArgs = __pigeon_list[1] as ByteArray return MyManufacturerSpecificDataArgs(idArgs, dataArgs) } } @@ -145,18 +211,16 @@ data class MyAdvertisementArgs ( val nameArgs: String? = null, val serviceUUIDsArgs: List, val serviceDataArgs: Map, - val manufacturerSpecificDataArgs: MyManufacturerSpecificDataArgs? = null + val manufacturerSpecificDataArgs: List ) { companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MyAdvertisementArgs { - val nameArgs = list[0] as String? - val serviceUUIDsArgs = list[1] as List - val serviceDataArgs = list[2] as Map - val manufacturerSpecificDataArgs: MyManufacturerSpecificDataArgs? = (list[3] as List?)?.let { - MyManufacturerSpecificDataArgs.fromList(it) - } + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyAdvertisementArgs { + val nameArgs = __pigeon_list[0] as String? + val serviceUUIDsArgs = __pigeon_list[1] as List + val serviceDataArgs = __pigeon_list[2] as Map + val manufacturerSpecificDataArgs = __pigeon_list[3] as List return MyAdvertisementArgs(nameArgs, serviceUUIDsArgs, serviceDataArgs, manufacturerSpecificDataArgs) } } @@ -165,7 +229,7 @@ data class MyAdvertisementArgs ( nameArgs, serviceUUIDsArgs, serviceDataArgs, - manufacturerSpecificDataArgs?.toList(), + manufacturerSpecificDataArgs, ) } } @@ -176,9 +240,9 @@ data class MyCentralArgs ( ) { companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MyCentralArgs { - val addressArgs = list[0] as String + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyCentralArgs { + val addressArgs = __pigeon_list[0] as String return MyCentralArgs(addressArgs) } } @@ -195,9 +259,9 @@ data class MyPeripheralArgs ( ) { companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MyPeripheralArgs { - val addressArgs = list[0] as String + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyPeripheralArgs { + val addressArgs = __pigeon_list[0] as String return MyPeripheralArgs(addressArgs) } } @@ -209,46 +273,43 @@ data class MyPeripheralArgs ( } /** Generated class from Pigeon that represents data sent in messages. */ -data class MyGattDescriptorArgs ( +data class MyGATTDescriptorArgs ( val hashCodeArgs: Long, - val uuidArgs: String, - val valueArgs: ByteArray? = null + val uuidArgs: String ) { 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 valueArgs = list[2] as ByteArray? - return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, valueArgs) + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyGATTDescriptorArgs { + val hashCodeArgs = __pigeon_list[0].let { num -> if (num is Int) num.toLong() else num as Long } + val uuidArgs = __pigeon_list[1] as String + return MyGATTDescriptorArgs(hashCodeArgs, uuidArgs) } } fun toList(): List { return listOf( hashCodeArgs, uuidArgs, - valueArgs, ) } } /** Generated class from Pigeon that represents data sent in messages. */ -data class MyGattCharacteristicArgs ( +data class MyGATTCharacteristicArgs ( val hashCodeArgs: Long, val uuidArgs: String, val propertyNumbersArgs: List, - val descriptorsArgs: List + val descriptorsArgs: List ) { companion object { - @Suppress("UNCHECKED_CAST") - fun fromList(list: List): MyGattCharacteristicArgs { - val hashCodeArgs = list[0].let { if (it is Int) it.toLong() else it as Long } - val uuidArgs = list[1] as String - val propertyNumbersArgs = list[2] as List - val descriptorsArgs = list[3] as List - return MyGattCharacteristicArgs(hashCodeArgs, uuidArgs, propertyNumbersArgs, descriptorsArgs) + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyGATTCharacteristicArgs { + val hashCodeArgs = __pigeon_list[0].let { num -> if (num is Int) num.toLong() else num as Long } + val uuidArgs = __pigeon_list[1] as String + val propertyNumbersArgs = __pigeon_list[2] as List + val descriptorsArgs = __pigeon_list[3] as List + return MyGATTCharacteristicArgs(hashCodeArgs, uuidArgs, propertyNumbersArgs, descriptorsArgs) } } fun toList(): List { @@ -262,47 +323,149 @@ data class MyGattCharacteristicArgs ( } /** Generated class from Pigeon that represents data sent in messages. */ -data class MyGattServiceArgs ( +data class MyGATTServiceArgs ( val hashCodeArgs: Long, val uuidArgs: String, - val characteristicsArgs: List + val isPrimaryArgs: Boolean, + val includedServicesArgs: List, + val characteristicsArgs: List ) { companion object { - @Suppress("UNCHECKED_CAST") - 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 characteristicsArgs = list[2] as List - return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs) + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyGATTServiceArgs { + val hashCodeArgs = __pigeon_list[0].let { num -> if (num is Int) num.toLong() else num as Long } + val uuidArgs = __pigeon_list[1] as String + val isPrimaryArgs = __pigeon_list[2] as Boolean + val includedServicesArgs = __pigeon_list[3] as List + val characteristicsArgs = __pigeon_list[4] as List + return MyGATTServiceArgs(hashCodeArgs, uuidArgs, isPrimaryArgs, includedServicesArgs, characteristicsArgs) } } fun toList(): List { return listOf( hashCodeArgs, uuidArgs, + isPrimaryArgs, + includedServicesArgs, characteristicsArgs, ) } } -@Suppress("UNCHECKED_CAST") -private object MyCentralManagerHostApiCodec : StandardMessageCodec() { +/** Generated class from Pigeon that represents data sent in messages. */ +data class MyMutableGATTDescriptorArgs ( + val hashCodeArgs: Long, + val uuidArgs: String, + val permissionNumbersArgs: List + +) { + companion object { + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyMutableGATTDescriptorArgs { + val hashCodeArgs = __pigeon_list[0].let { num -> if (num is Int) num.toLong() else num as Long } + val uuidArgs = __pigeon_list[1] as String + val permissionNumbersArgs = __pigeon_list[2] as List + return MyMutableGATTDescriptorArgs(hashCodeArgs, uuidArgs, permissionNumbersArgs) + } + } + fun toList(): List { + return listOf( + hashCodeArgs, + uuidArgs, + permissionNumbersArgs, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class MyMutableGATTCharacteristicArgs ( + val hashCodeArgs: Long, + val uuidArgs: String, + val permissionNumbersArgs: List, + val propertyNumbersArgs: List, + val descriptorsArgs: List + +) { + companion object { + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyMutableGATTCharacteristicArgs { + val hashCodeArgs = __pigeon_list[0].let { num -> if (num is Int) num.toLong() else num as Long } + val uuidArgs = __pigeon_list[1] as String + val permissionNumbersArgs = __pigeon_list[2] as List + val propertyNumbersArgs = __pigeon_list[3] as List + val descriptorsArgs = __pigeon_list[4] as List + return MyMutableGATTCharacteristicArgs(hashCodeArgs, uuidArgs, permissionNumbersArgs, propertyNumbersArgs, descriptorsArgs) + } + } + fun toList(): List { + return listOf( + hashCodeArgs, + uuidArgs, + permissionNumbersArgs, + propertyNumbersArgs, + descriptorsArgs, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class MyMutableGATTServiceArgs ( + val hashCodeArgs: Long, + val uuidArgs: String, + val isPrimaryArgs: Boolean, + val includedServicesArgs: List, + val characteristicsArgs: List + +) { + companion object { + @Suppress("LocalVariableName") + fun fromList(__pigeon_list: List): MyMutableGATTServiceArgs { + val hashCodeArgs = __pigeon_list[0].let { num -> if (num is Int) num.toLong() else num as Long } + val uuidArgs = __pigeon_list[1] as String + val isPrimaryArgs = __pigeon_list[2] as Boolean + val includedServicesArgs = __pigeon_list[3] as List + val characteristicsArgs = __pigeon_list[4] as List + return MyMutableGATTServiceArgs(hashCodeArgs, uuidArgs, isPrimaryArgs, includedServicesArgs, characteristicsArgs) + } + } + fun toList(): List { + return listOf( + hashCodeArgs, + uuidArgs, + isPrimaryArgs, + includedServicesArgs, + characteristicsArgs, + ) + } +} + +private object MyCentralManagerHostAPICodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { 128.toByte() -> { return (readValue(buffer) as? List)?.let { - MyGattCharacteristicArgs.fromList(it) + MyCentralManagerArgs.fromList(it) } } 129.toByte() -> { return (readValue(buffer) as? List)?.let { - MyGattDescriptorArgs.fromList(it) + MyGATTCharacteristicArgs.fromList(it) } } 130.toByte() -> { return (readValue(buffer) as? List)?.let { - MyGattServiceArgs.fromList(it) + MyGATTDescriptorArgs.fromList(it) + } + } + 131.toByte() -> { + return (readValue(buffer) as? List)?.let { + MyGATTServiceArgs.fromList(it) + } + } + 132.toByte() -> { + return (readValue(buffer) as? List)?.let { + MyPeripheralArgs.fromList(it) } } else -> super.readValueOfType(type, buffer) @@ -310,91 +473,67 @@ private object MyCentralManagerHostApiCodec : StandardMessageCodec() { } override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { - is MyGattCharacteristicArgs -> { + is MyCentralManagerArgs -> { stream.write(128) writeValue(stream, value.toList()) } - is MyGattDescriptorArgs -> { + is MyGATTCharacteristicArgs -> { stream.write(129) writeValue(stream, value.toList()) } - is MyGattServiceArgs -> { + is MyGATTDescriptorArgs -> { stream.write(130) writeValue(stream, value.toList()) } + is MyGATTServiceArgs -> { + stream.write(131) + writeValue(stream, value.toList()) + } + is MyPeripheralArgs -> { + stream.write(132) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } } /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ -interface MyCentralManagerHostApi { - fun setUp(callback: (Result) -> Unit) - fun startDiscovery(callback: (Result) -> Unit) +interface MyCentralManagerHostAPI { + fun initialize(): MyCentralManagerArgs + fun getState(): MyBluetoothLowEnergyStateArgs + fun authorize(callback: (Result) -> Unit) + fun showAppSettings(callback: (Result) -> Unit) + fun startDiscovery(serviceUUIDsArgs: List, callback: (Result) -> Unit) fun stopDiscovery() fun connect(addressArgs: String, callback: (Result) -> Unit) fun disconnect(addressArgs: String, callback: (Result) -> Unit) + fun retrieveConnectedPeripherals(): List fun requestMTU(addressArgs: String, mtuArgs: Long, callback: (Result) -> Unit) fun readRSSI(addressArgs: String, callback: (Result) -> Unit) - fun discoverServices(addressArgs: String, callback: (Result>) -> Unit) + fun discoverGATT(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 writeCharacteristic(addressArgs: String, hashCodeArgs: Long, valueArgs: ByteArray, typeArgs: MyGATTCharacteristicWriteTypeArgs, callback: (Result) -> Unit) + fun setCharacteristicNotification(addressArgs: String, hashCodeArgs: Long, enableArgs: Boolean) 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. */ + /** The codec used by MyCentralManagerHostAPI. */ val codec: MessageCodec by lazy { - MyCentralManagerHostApiCodec + MyCentralManagerHostAPICodec } - /** Sets up an instance of `MyCentralManagerHostApi` to handle messages through the `binaryMessenger`. */ - @Suppress("UNCHECKED_CAST") - fun setUp(binaryMessenger: BinaryMessenger, api: MyCentralManagerHostApi?) { + /** Sets up an instance of `MyCentralManagerHostAPI` to handle messages through the `binaryMessenger`. */ + fun setUp(binaryMessenger: BinaryMessenger, api: MyCentralManagerHostAPI?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setUp", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.initialize$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { _, reply -> - api.setUp() { result: Result -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(wrapError(error)) - } else { - reply.reply(wrapResult(null)) - } - } - } - } else { - channel.setMessageHandler(null) - } - } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.startDiscovery", codec) - if (api != null) { - channel.setMessageHandler { _, reply -> - api.startDiscovery() { result: Result -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(wrapError(error)) - } else { - reply.reply(wrapResult(null)) - } - } - } - } else { - channel.setMessageHandler(null) - } - } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.stopDiscovery", codec) - if (api != null) { - channel.setMessageHandler { _, reply -> - var wrapped: List - try { - api.stopDiscovery() - wrapped = listOf(null) + val wrapped: List = try { + listOf(api.initialize()) } catch (exception: Throwable) { - wrapped = wrapError(exception) + wrapError(exception) } reply.reply(wrapped) } @@ -403,7 +542,92 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.connect", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.getState$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + listOf(api.getState().raw) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.authorize$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + api.authorize() { 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.showAppSettings$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + api.showAppSettings() { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + reply.reply(wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.startDiscovery$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val serviceUUIDsArgsArg = args[0] as List + api.startDiscovery(serviceUUIDsArgsArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(wrapError(error)) + } else { + reply.reply(wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.stopDiscovery$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.stopDiscovery() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.connect$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -422,7 +646,7 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.disconnect", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.disconnect$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -441,12 +665,27 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.requestMTU", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.retrieveConnectedPeripherals$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + listOf(api.retrieveConnectedPeripherals()) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.requestMTU$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val addressArgsArg = args[0] as String - val mtuArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val mtuArgsArg = args[1].let { num -> if (num is Int) num.toLong() else num as Long } api.requestMTU(addressArgsArg, mtuArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { @@ -462,7 +701,7 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readRSSI", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.readRSSI$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -482,12 +721,12 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.discoverServices", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.discoverGATT$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val addressArgsArg = args[0] as String - api.discoverServices(addressArgsArg) { result: Result> -> + api.discoverGATT(addressArgsArg) { result: Result> -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -502,12 +741,12 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readCharacteristic", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.readCharacteristic$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val addressArgsArg = args[0] as String - val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val hashCodeArgsArg = args[1].let { num -> if (num is Int) num.toLong() else num as Long } api.readCharacteristic(addressArgsArg, hashCodeArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { @@ -523,15 +762,15 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.writeCharacteristic", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.writeCharacteristic$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val addressArgsArg = args[0] as String - val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val hashCodeArgsArg = args[1].let { num -> if (num is Int) num.toLong() else num 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(addressArgsArg, hashCodeArgsArg, valueArgsArg, typeNumberArgsArg) { result: Result -> + val typeArgsArg = MyGATTCharacteristicWriteTypeArgs.ofRaw(args[3] as Int)!! + api.writeCharacteristic(addressArgsArg, hashCodeArgsArg, valueArgsArg, typeArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -545,33 +784,32 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setCharacteristicNotifyState", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.setCharacteristicNotification$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List 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)) - } else { - reply.reply(wrapResult(null)) - } + val hashCodeArgsArg = args[1].let { num -> if (num is Int) num.toLong() else num as Long } + val enableArgsArg = args[2] as Boolean + val wrapped: List = try { + api.setCharacteristicNotification(addressArgsArg, hashCodeArgsArg, enableArgsArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) } + reply.reply(wrapped) } } else { channel.setMessageHandler(null) } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readDescriptor", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.readDescriptor$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val addressArgsArg = args[0] as String - val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val hashCodeArgsArg = args[1].let { num -> if (num is Int) num.toLong() else num as Long } api.readDescriptor(addressArgsArg, hashCodeArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { @@ -587,12 +825,12 @@ interface MyCentralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.writeDescriptor", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.writeDescriptor$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val addressArgsArg = args[0] as String - val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val hashCodeArgsArg = args[1].let { num -> if (num is Int) num.toLong() else num as Long } val valueArgsArg = args[2] as ByteArray api.writeDescriptor(addressArgsArg, hashCodeArgsArg, valueArgsArg) { result: Result -> val error = result.exceptionOrNull() @@ -610,8 +848,7 @@ interface MyCentralManagerHostApi { } } } -@Suppress("UNCHECKED_CAST") -private object MyCentralManagerFlutterApiCodec : StandardMessageCodec() { +private object MyCentralManagerFlutterAPICodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { 128.toByte() -> { @@ -621,10 +858,20 @@ private object MyCentralManagerFlutterApiCodec : StandardMessageCodec() { } 129.toByte() -> { return (readValue(buffer) as? List)?.let { - MyManufacturerSpecificDataArgs.fromList(it) + 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() -> { return (readValue(buffer) as? List)?.let { MyPeripheralArgs.fromList(it) } @@ -638,32 +885,41 @@ private object MyCentralManagerFlutterApiCodec : StandardMessageCodec() { stream.write(128) writeValue(stream, value.toList()) } - is MyManufacturerSpecificDataArgs -> { + is MyGATTCharacteristicArgs -> { stream.write(129) writeValue(stream, value.toList()) } - is MyPeripheralArgs -> { + is MyGATTDescriptorArgs -> { stream.write(130) writeValue(stream, value.toList()) } + is MyManufacturerSpecificDataArgs -> { + stream.write(131) + writeValue(stream, value.toList()) + } + is MyPeripheralArgs -> { + stream.write(132) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } } /** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ -@Suppress("UNCHECKED_CAST") -class MyCentralManagerFlutterApi(private val binaryMessenger: BinaryMessenger) { +class MyCentralManagerFlutterAPI(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { companion object { - /** The codec used by MyCentralManagerFlutterApi. */ + /** The codec used by MyCentralManagerFlutterAPI. */ val codec: MessageCodec by lazy { - MyCentralManagerFlutterApiCodec + MyCentralManagerFlutterAPICodec } } - fun onStateChanged(stateNumberArgsArg: Long, callback: (Result) -> Unit) { - val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onStateChanged" + fun onStateChanged(stateArgsArg: MyBluetoothLowEnergyStateArgs, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onStateChanged$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(stateNumberArgsArg)) { + channel.send(listOf(stateArgsArg.raw)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -675,8 +931,10 @@ class MyCentralManagerFlutterApi(private val binaryMessenger: BinaryMessenger) { } } } - fun onDiscovered(peripheralArgsArg: MyPeripheralArgs, rssiArgsArg: Long, advertisementArgsArg: MyAdvertisementArgs, callback: (Result) -> Unit) { - val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onDiscovered" + fun onDiscovered(peripheralArgsArg: MyPeripheralArgs, rssiArgsArg: Long, advertisementArgsArg: MyAdvertisementArgs, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onDiscovered$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) channel.send(listOf(peripheralArgsArg, rssiArgsArg, advertisementArgsArg)) { if (it is List<*>) { @@ -690,10 +948,12 @@ class MyCentralManagerFlutterApi(private val binaryMessenger: BinaryMessenger) { } } } - fun onConnectionStateChanged(addressArgsArg: String, stateArgsArg: Boolean, callback: (Result) -> Unit) { - val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onConnectionStateChanged" + fun onConnectionStateChanged(peripheralArgsArg: MyPeripheralArgs, stateArgsArg: MyConnectionStateArgs, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onConnectionStateChanged$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(addressArgsArg, stateArgsArg)) { + channel.send(listOf(peripheralArgsArg, stateArgsArg.raw)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -705,10 +965,12 @@ class MyCentralManagerFlutterApi(private val binaryMessenger: BinaryMessenger) { } } } - fun onMtuChanged(addressArgsArg: String, mtuArgsArg: Long, callback: (Result) -> Unit) { - val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onMtuChanged" + fun onMTUChanged(peripheralArgsArg: MyPeripheralArgs, mtuArgsArg: Long, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onMTUChanged$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(addressArgsArg, mtuArgsArg)) { + channel.send(listOf(peripheralArgsArg, 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?))) @@ -720,10 +982,12 @@ class MyCentralManagerFlutterApi(private val binaryMessenger: BinaryMessenger) { } } } - fun onCharacteristicNotified(addressArgsArg: String, hashCodeArgsArg: Long, valueArgsArg: ByteArray, callback: (Result) -> Unit) { - val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicNotified" + fun onCharacteristicNotified(peripheralArgsArg: MyPeripheralArgs, characteristicArgsArg: MyGATTCharacteristicArgs, valueArgsArg: ByteArray, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onCharacteristicNotified$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(addressArgsArg, hashCodeArgsArg, valueArgsArg)) { + channel.send(listOf(peripheralArgsArg, characteristicArgsArg, 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?))) @@ -736,8 +1000,7 @@ class MyCentralManagerFlutterApi(private val binaryMessenger: BinaryMessenger) { } } } -@Suppress("UNCHECKED_CAST") -private object MyPeripheralManagerHostApiCodec : StandardMessageCodec() { +private object MyPeripheralManagerHostAPICodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { 128.toByte() -> { @@ -747,22 +1010,27 @@ private object MyPeripheralManagerHostApiCodec : StandardMessageCodec() { } 129.toByte() -> { return (readValue(buffer) as? List)?.let { - MyGattCharacteristicArgs.fromList(it) + MyManufacturerSpecificDataArgs.fromList(it) } } 130.toByte() -> { return (readValue(buffer) as? List)?.let { - MyGattDescriptorArgs.fromList(it) + MyMutableGATTCharacteristicArgs.fromList(it) } } 131.toByte() -> { return (readValue(buffer) as? List)?.let { - MyGattServiceArgs.fromList(it) + MyMutableGATTDescriptorArgs.fromList(it) } } 132.toByte() -> { return (readValue(buffer) as? List)?.let { - MyManufacturerSpecificDataArgs.fromList(it) + MyMutableGATTServiceArgs.fromList(it) + } + } + 133.toByte() -> { + return (readValue(buffer) as? List)?.let { + MyPeripheralManagerArgs.fromList(it) } } else -> super.readValueOfType(type, buffer) @@ -774,51 +1042,108 @@ private object MyPeripheralManagerHostApiCodec : StandardMessageCodec() { stream.write(128) writeValue(stream, value.toList()) } - is MyGattCharacteristicArgs -> { + is MyManufacturerSpecificDataArgs -> { stream.write(129) writeValue(stream, value.toList()) } - is MyGattDescriptorArgs -> { + is MyMutableGATTCharacteristicArgs -> { stream.write(130) writeValue(stream, value.toList()) } - is MyGattServiceArgs -> { + is MyMutableGATTDescriptorArgs -> { stream.write(131) writeValue(stream, value.toList()) } - is MyManufacturerSpecificDataArgs -> { + is MyMutableGATTServiceArgs -> { stream.write(132) writeValue(stream, value.toList()) } + is MyPeripheralManagerArgs -> { + stream.write(133) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } } /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ -interface MyPeripheralManagerHostApi { - fun setUp(callback: (Result) -> Unit) - fun addService(serviceArgs: MyGattServiceArgs, callback: (Result) -> Unit) +interface MyPeripheralManagerHostAPI { + fun initialize(): MyPeripheralManagerArgs + fun getState(): MyBluetoothLowEnergyStateArgs + fun authorize(callback: (Result) -> Unit) + fun showAppSettings(callback: (Result) -> Unit) + fun openGATTServer() + fun closeGATTServer() + fun addService(serviceArgs: MyMutableGATTServiceArgs, callback: (Result) -> Unit) fun removeService(hashCodeArgs: Long) - fun clearServices() + fun removeAllServices() fun startAdvertising(advertisementArgs: MyAdvertisementArgs, callback: (Result) -> Unit) fun stopAdvertising() - 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) + fun sendResponse(addressArgs: String, idArgs: Long, statusArgs: MyGATTStatusArgs, offsetArgs: Long, valueArgs: ByteArray?) + fun notifyCharacteristicChanged(addressArgs: String, hashCodeArgs: Long, confirmArgs: Boolean, valueArgs: ByteArray, callback: (Result) -> Unit) companion object { - /** The codec used by MyPeripheralManagerHostApi. */ + /** The codec used by MyPeripheralManagerHostAPI. */ val codec: MessageCodec by lazy { - MyPeripheralManagerHostApiCodec + MyPeripheralManagerHostAPICodec } - /** Sets up an instance of `MyPeripheralManagerHostApi` to handle messages through the `binaryMessenger`. */ - @Suppress("UNCHECKED_CAST") - fun setUp(binaryMessenger: BinaryMessenger, api: MyPeripheralManagerHostApi?) { + /** Sets up an instance of `MyPeripheralManagerHostAPI` to handle messages through the `binaryMessenger`. */ + fun setUp(binaryMessenger: BinaryMessenger, api: MyPeripheralManagerHostAPI?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.setUp", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.initialize$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { _, reply -> - api.setUp() { result: Result -> + val wrapped: List = try { + listOf(api.initialize()) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.getState$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + listOf(api.getState().raw) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.authorize$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + api.authorize() { 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.MyPeripheralManagerHostAPI.showAppSettings$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + api.showAppSettings() { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -832,11 +1157,43 @@ interface MyPeripheralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.addService", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.openGATTServer$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.openGATTServer() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.closeGATTServer$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.closeGATTServer() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.addService$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val serviceArgsArg = args[0] as MyGattServiceArgs + val serviceArgsArg = args[0] as MyMutableGATTServiceArgs api.addService(serviceArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { @@ -851,17 +1208,16 @@ interface MyPeripheralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.removeService", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.removeService$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val hashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - var wrapped: List - try { + val hashCodeArgsArg = args[0].let { num -> if (num is Int) num.toLong() else num as Long } + val wrapped: List = try { api.removeService(hashCodeArgsArg) - wrapped = listOf(null) + listOf(null) } catch (exception: Throwable) { - wrapped = wrapError(exception) + wrapError(exception) } reply.reply(wrapped) } @@ -870,15 +1226,14 @@ interface MyPeripheralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.clearServices", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.removeAllServices$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { _, reply -> - var wrapped: List - try { - api.clearServices() - wrapped = listOf(null) + val wrapped: List = try { + api.removeAllServices() + listOf(null) } catch (exception: Throwable) { - wrapped = wrapError(exception) + wrapError(exception) } reply.reply(wrapped) } @@ -887,7 +1242,7 @@ interface MyPeripheralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.startAdvertising", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.startAdvertising$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -906,15 +1261,14 @@ interface MyPeripheralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.stopAdvertising", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.stopAdvertising$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { _, reply -> - var wrapped: List - try { + val wrapped: List = try { api.stopAdvertising() - wrapped = listOf(null) + listOf(null) } catch (exception: Throwable) { - wrapped = wrapError(exception) + wrapError(exception) } reply.reply(wrapped) } @@ -923,21 +1277,20 @@ interface MyPeripheralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.sendResponse", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.sendResponse$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List 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 idArgsArg = args[1].let { num -> if (num is Int) num.toLong() else num as Long } + val statusArgsArg = MyGATTStatusArgs.ofRaw(args[2] as Int)!! + val offsetArgsArg = args[3].let { num -> if (num is Int) num.toLong() else num as Long } val valueArgsArg = args[4] as ByteArray? - var wrapped: List - try { - api.sendResponse(addressArgsArg, idArgsArg, statusNumberArgsArg, offsetArgsArg, valueArgsArg) - wrapped = listOf(null) + val wrapped: List = try { + api.sendResponse(addressArgsArg, idArgsArg, statusArgsArg, offsetArgsArg, valueArgsArg) + listOf(null) } catch (exception: Throwable) { - wrapped = wrapError(exception) + wrapError(exception) } reply.reply(wrapped) } @@ -946,15 +1299,15 @@ interface MyPeripheralManagerHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.notifyCharacteristicChanged", codec) + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.notifyCharacteristicChanged$separatedMessageChannelSuffix", codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List - val hashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } - val valueArgsArg = args[1] as ByteArray + val addressArgsArg = args[0] as String + val hashCodeArgsArg = args[1].let { num -> if (num is Int) num.toLong() else num as Long } val confirmArgsArg = args[2] as Boolean - val addressArgsArg = args[3] as String - api.notifyCharacteristicChanged(hashCodeArgsArg, valueArgsArg, confirmArgsArg, addressArgsArg) { result: Result -> + val valueArgsArg = args[3] as ByteArray + api.notifyCharacteristicChanged(addressArgsArg, hashCodeArgsArg, confirmArgsArg, valueArgsArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(wrapError(error)) @@ -970,8 +1323,7 @@ interface MyPeripheralManagerHostApi { } } } -@Suppress("UNCHECKED_CAST") -private object MyPeripheralManagerFlutterApiCodec : StandardMessageCodec() { +private object MyPeripheralManagerFlutterAPICodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { 128.toByte() -> { @@ -994,18 +1346,19 @@ private object MyPeripheralManagerFlutterApiCodec : StandardMessageCodec() { } /** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ -@Suppress("UNCHECKED_CAST") -class MyPeripheralManagerFlutterApi(private val binaryMessenger: BinaryMessenger) { +class MyPeripheralManagerFlutterAPI(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { companion object { - /** The codec used by MyPeripheralManagerFlutterApi. */ + /** The codec used by MyPeripheralManagerFlutterAPI. */ val codec: MessageCodec by lazy { - MyPeripheralManagerFlutterApiCodec + MyPeripheralManagerFlutterAPICodec } } - fun onStateChanged(stateNumberArgsArg: Long, callback: (Result) -> Unit) { - val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onStateChanged" + fun onStateChanged(stateArgsArg: MyBluetoothLowEnergyStateArgs, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onStateChanged$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(stateNumberArgsArg)) { + channel.send(listOf(stateArgsArg.raw)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -1017,10 +1370,12 @@ class MyPeripheralManagerFlutterApi(private val binaryMessenger: BinaryMessenger } } } - fun onConnectionStateChanged(centralArgsArg: MyCentralArgs, stateArgsArg: Boolean, callback: (Result) -> Unit) { - val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onConnectionStateChanged" + fun onConnectionStateChanged(centralArgsArg: MyCentralArgs, statusArgsArg: Long, stateArgsArg: MyConnectionStateArgs, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onConnectionStateChanged$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(centralArgsArg, stateArgsArg)) { + channel.send(listOf(centralArgsArg, statusArgsArg, stateArgsArg.raw)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -1032,10 +1387,12 @@ class MyPeripheralManagerFlutterApi(private val binaryMessenger: BinaryMessenger } } } - fun onMtuChanged(addressArgsArg: String, mtuArgsArg: Long, callback: (Result) -> Unit) { - val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onMtuChanged" + fun onMTUChanged(centralArgsArg: MyCentralArgs, mtuArgsArg: Long, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onMTUChanged$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(addressArgsArg, mtuArgsArg)) { + channel.send(listOf(centralArgsArg, 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?))) @@ -1047,10 +1404,12 @@ class MyPeripheralManagerFlutterApi(private val binaryMessenger: BinaryMessenger } } } - fun onCharacteristicReadRequest(addressArgsArg: String, hashCodeArgsArg: Long, idArgsArg: Long, offsetArgsArg: Long, callback: (Result) -> Unit) { - val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest" + fun onCharacteristicReadRequest(centralArgsArg: MyCentralArgs, idArgsArg: Long, offsetArgsArg: Long, hashCodeArgsArg: Long, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onCharacteristicReadRequest$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(addressArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg)) { + channel.send(listOf(centralArgsArg, idArgsArg, offsetArgsArg, hashCodeArgsArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -1062,10 +1421,12 @@ class MyPeripheralManagerFlutterApi(private val binaryMessenger: BinaryMessenger } } } - 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" + fun onCharacteristicWriteRequest(centralArgsArg: MyCentralArgs, idArgsArg: Long, hashCodeArgsArg: Long, preparedWriteArgsArg: Boolean, responseNeededArgsArg: Boolean, offsetArgsArg: Long, valueArgsArg: ByteArray, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onCharacteristicWriteRequest$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(addressArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg, valueArgsArg, preparedWriteArgsArg, responseNeededArgsArg)) { + channel.send(listOf(centralArgsArg, idArgsArg, hashCodeArgsArg, preparedWriteArgsArg, responseNeededArgsArg, offsetArgsArg, 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?))) @@ -1077,10 +1438,12 @@ class MyPeripheralManagerFlutterApi(private val binaryMessenger: BinaryMessenger } } } - fun onCharacteristicNotifyStateChanged(addressArgsArg: String, hashCodeArgsArg: Long, stateNumberArgsArg: Long, callback: (Result) -> Unit) { - val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged" + fun onDescriptorReadRequest(centralArgsArg: MyCentralArgs, idArgsArg: Long, offsetArgsArg: Long, hashCodeArgsArg: Long, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorReadRequest$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(addressArgsArg, hashCodeArgsArg, stateNumberArgsArg)) { + channel.send(listOf(centralArgsArg, idArgsArg, offsetArgsArg, hashCodeArgsArg)) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) @@ -1092,10 +1455,12 @@ class MyPeripheralManagerFlutterApi(private val binaryMessenger: BinaryMessenger } } } - fun onDescriptorReadRequest(addressArgsArg: String, hashCodeArgsArg: Long, idArgsArg: Long, offsetArgsArg: Long, callback: (Result) -> Unit) { - val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onDescriptorReadRequest" + fun onDescriptorWriteRequest(centralArgsArg: MyCentralArgs, idArgsArg: Long, hashCodeArgsArg: Long, preparedWriteArgsArg: Boolean, responseNeededArgsArg: Boolean, offsetArgsArg: Long, valueArgsArg: ByteArray, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) - channel.send(listOf(addressArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg)) { + channel.send(listOf(centralArgsArg, idArgsArg, hashCodeArgsArg, preparedWriteArgsArg, responseNeededArgsArg, offsetArgsArg, 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?))) @@ -1107,25 +1472,12 @@ class MyPeripheralManagerFlutterApi(private val binaryMessenger: BinaryMessenger } } } - fun onDescriptorWriteRequest(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.onDescriptorWriteRequest" + fun onExecuteWrite(centralArgsArg: MyCentralArgs, idArgsArg: Long, executeArgsArg: Boolean, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onExecuteWrite$separatedMessageChannelSuffix" 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)) { + channel.send(listOf(centralArgsArg, 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?))) diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyAPI.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyAPI.kt new file mode 100644 index 0000000..2eb0d55 --- /dev/null +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyAPI.kt @@ -0,0 +1,258 @@ +package dev.hebei.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.BluetoothProfile +import android.bluetooth.le.AdvertiseData +import android.bluetooth.le.ScanResult +import android.os.Build +import android.os.ParcelUuid +import android.util.SparseArray +import java.util.UUID + +//region ToObject +fun MyGATTCharacteristicWriteTypeArgs.toType(): Int { + return when (this) { + MyGATTCharacteristicWriteTypeArgs.WITH_RESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT + MyGATTCharacteristicWriteTypeArgs.WITHOUT_RESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE + } +} + +fun MyGATTStatusArgs.toStatus(): Int { + return when (this) { + MyGATTStatusArgs.SUCCESS -> BluetoothGatt.GATT_SUCCESS + MyGATTStatusArgs.READ_NOT_PERMITTED -> BluetoothGatt.GATT_READ_NOT_PERMITTED + MyGATTStatusArgs.WRITE_NOT_PERMITTED -> BluetoothGatt.GATT_WRITE_NOT_PERMITTED + MyGATTStatusArgs.INSUFFICIENT_AUTHENTICATION -> BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION + MyGATTStatusArgs.REQUEST_NOT_SUPPORTED -> BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED + MyGATTStatusArgs.INSUFFICIENT_ENCRYPTION -> BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION + MyGATTStatusArgs.INVALID_OFFSET -> BluetoothGatt.GATT_INVALID_OFFSET + MyGATTStatusArgs.INSUFFICIENT_AUTHORIZATION -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION + else BluetoothGatt.GATT_FAILURE + MyGATTStatusArgs.INVALID_ATTRIBUTE_LENGTH -> BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH + MyGATTStatusArgs.CONNECTION_CONGESTED -> BluetoothGatt.GATT_CONNECTION_CONGESTED + MyGATTStatusArgs.FAILURE -> BluetoothGatt.GATT_FAILURE + } +} + +fun MyAdvertisementArgs.toAdvertiseData(): AdvertiseData { + val advertiseDataBuilder = AdvertiseData.Builder() + for (serviceUuidArgs in serviceUUIDsArgs) { + val serviceUUID = ParcelUuid.fromString(serviceUuidArgs) + advertiseDataBuilder.addServiceUuid(serviceUUID) + } + for (entry in serviceDataArgs) { + val serviceDataUUID = ParcelUuid.fromString(entry.key as String) + val serviceData = entry.value as ByteArray + advertiseDataBuilder.addServiceData(serviceDataUUID, serviceData) + } + for (args in manufacturerSpecificDataArgs) { + val itemArgs = args as MyManufacturerSpecificDataArgs + val manufacturerId = itemArgs.idArgs.toInt() + val manufacturerSpecificData = itemArgs.dataArgs + advertiseDataBuilder.addManufacturerData(manufacturerId, manufacturerSpecificData) + } + return advertiseDataBuilder.build() +} + +fun MyAdvertisementArgs.toScanResponse(): AdvertiseData { + val advertiseDataBuilder = AdvertiseData.Builder() + val includeDeviceName = nameArgs != null + advertiseDataBuilder.setIncludeDeviceName(includeDeviceName) + return advertiseDataBuilder.build() +} + +fun MyMutableGATTDescriptorArgs.toDescriptor(): BluetoothGattDescriptor { + val uuid = UUID.fromString(uuidArgs) + val permissions = BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE + return BluetoothGattDescriptor(uuid, permissions) +} + +fun MyMutableGATTCharacteristicArgs.toCharacteristic(): BluetoothGattCharacteristic { + val uuid = UUID.fromString(uuidArgs) + val properties = getProperties() + val permissions = getPermissions() + return BluetoothGattCharacteristic(uuid, properties, permissions) +} + +fun MyMutableGATTCharacteristicArgs.getProperties(): Int { + val propertiesArgs = propertyNumbersArgs.requireNoNulls().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.WRITE_WITHOUT_RESPONSE) + 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 MyMutableGATTCharacteristicArgs.getPermissions(): Int { + val propertiesArgs = propertyNumbersArgs.requireNoNulls().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.WRITE_WITHOUT_RESPONSE) + var permissions = 0 + if (read) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_READ + if (write || writeWithoutResponse) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_WRITE + return permissions +} + +fun MyMutableGATTServiceArgs.toService(): BluetoothGattService { + val uuid = UUID.fromString(uuidArgs) + val serviceType = if (isPrimaryArgs) BluetoothGattService.SERVICE_TYPE_PRIMARY + else BluetoothGattService.SERVICE_TYPE_SECONDARY + return BluetoothGattService(uuid, serviceType) +} //endregion + +//region ToArgs +fun Int.toBluetoothLowEnergyStateArgs(): MyBluetoothLowEnergyStateArgs { + return when (this) { + BluetoothAdapter.STATE_OFF -> MyBluetoothLowEnergyStateArgs.OFF + BluetoothAdapter.STATE_TURNING_ON -> MyBluetoothLowEnergyStateArgs.TURNING_ON + BluetoothAdapter.STATE_ON -> MyBluetoothLowEnergyStateArgs.ON + BluetoothAdapter.STATE_TURNING_OFF -> MyBluetoothLowEnergyStateArgs.TURNING_OFF + else -> MyBluetoothLowEnergyStateArgs.UNKNOWN + } +} + +fun Int.toConnectionStateArgs(): MyConnectionStateArgs { + return when (this) { + BluetoothProfile.STATE_DISCONNECTED -> MyConnectionStateArgs.DISCONNECTED + BluetoothProfile.STATE_CONNECTING -> MyConnectionStateArgs.CONNECTING + BluetoothProfile.STATE_CONNECTED -> MyConnectionStateArgs.CONNECTED + BluetoothProfile.STATE_DISCONNECTING -> MyConnectionStateArgs.DISCONNECTING + else -> throw IllegalArgumentException() + } +} + +fun SparseArray.toManufacturerSpecificDataArgs(): List { + 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 +} + +fun ScanResult.toAdvertisementArgs(): MyAdvertisementArgs { + val record = scanRecord + return if (record == null) { + val nameArgs = null + val serviceUUIDsArgs = emptyList() + val serviceDataArgs = emptyMap() + val manufacturerSpecificDataArgs = emptyList() + 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() ?: emptyArray() + val serviceDataArgs = mapOf(*pairs) + val manufacturerSpecificDataArgs = record.manufacturerSpecificData?.toManufacturerSpecificDataArgs() ?: emptyList() + 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 BluetoothGattDescriptor.toArgs(): MyGATTDescriptorArgs { + val hashCodeArgs = hashCode().toLong() + val uuidArgs = this.uuid.toString() + return MyGATTDescriptorArgs(hashCodeArgs, uuidArgs) +} + +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.WRITE_WITHOUT_RESPONSE.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 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 BluetoothGattService.toArgs(): MyGATTServiceArgs { + val hashCodeArgs = hashCode().toLong() + val uuidArgs = uuid.toString() + val isPrimaryArgs = type == BluetoothGattService.SERVICE_TYPE_PRIMARY + val includedServicesArgs = includedServices.map { it.toArgs() } + val characteristicsArgs = characteristics.map { it.toArgs() } + return MyGATTServiceArgs(hashCodeArgs, uuidArgs, isPrimaryArgs, includedServicesArgs, characteristicsArgs) +} //endregion + +val Any.hashCode get() = this.hashCode() +val Int.args get() = this.toLong() + +//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/hebei/bluetooth_low_energy_android/MyActivityResultListener.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyActivityResultListener.kt new file mode 100644 index 0000000..d172073 --- /dev/null +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyActivityResultListener.kt @@ -0,0 +1,16 @@ +package dev.hebei.bluetooth_low_energy_android + +import android.content.Intent +import io.flutter.plugin.common.PluginRegistry + +class MyActivityResultListener(manager: MyBluetoothLowEnergyManager) : PluginRegistry.ActivityResultListener { + private val mManager: MyBluetoothLowEnergyManager + + init { + mManager = manager + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { + return mManager.onActivityResult(requestCode, resultCode, data) + } +} \ No newline at end of file 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/hebei/bluetooth_low_energy_android/MyAdvertiseCallback.kt similarity index 91% rename from bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyAdvertiseCallback.kt rename to bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyAdvertiseCallback.kt index 40c4671..0be1487 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/hebei/bluetooth_low_energy_android/MyAdvertiseCallback.kt @@ -1,4 +1,4 @@ -package dev.yanshouwang.bluetooth_low_energy_android +package dev.hebei.bluetooth_low_energy_android import android.bluetooth.le.AdvertiseCallback import android.bluetooth.le.AdvertiseSettings 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/hebei/bluetooth_low_energy_android/MyBluetoothGattCallback.kt similarity index 74% rename from bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothGattCallback.kt rename to bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyBluetoothGattCallback.kt index 49f4e70..4b86aac 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/hebei/bluetooth_low_energy_android/MyBluetoothGattCallback.kt @@ -1,4 +1,4 @@ -package dev.yanshouwang.bluetooth_low_energy_android +package dev.hebei.bluetooth_low_energy_android import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGattCallback @@ -7,8 +7,7 @@ import android.bluetooth.BluetoothGattDescriptor import android.os.Build import java.util.concurrent.Executor -class MyBluetoothGattCallback(manager: MyCentralManager, executor: Executor) : - BluetoothGattCallback() { +class MyBluetoothGattCallback(manager: MyCentralManager, executor: Executor) : BluetoothGattCallback() { private val mManager: MyCentralManager private val mExecutor: Executor @@ -45,12 +44,7 @@ class MyBluetoothGattCallback(manager: MyCentralManager, executor: Executor) : } } - 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) mExecutor.execute { mManager.onCharacteristicRead(gatt, characteristic, status, value) @@ -58,9 +52,7 @@ class MyBluetoothGattCallback(manager: MyCentralManager, executor: Executor) : } // 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 @@ -71,18 +63,14 @@ class MyBluetoothGattCallback(manager: MyCentralManager, executor: Executor) : } } - override fun onCharacteristicWrite( - gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int - ) { + override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { super.onCharacteristicWrite(gatt, 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) mExecutor.execute { mManager.onCharacteristicChanged(gatt, characteristic, value) @@ -90,9 +78,7 @@ class MyBluetoothGattCallback(manager: MyCentralManager, executor: Executor) : } // 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 @@ -103,9 +89,7 @@ class MyBluetoothGattCallback(manager: MyCentralManager, executor: Executor) : } } - 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) mExecutor.execute { mManager.onDescriptorRead(gatt, descriptor, status, value) @@ -113,9 +97,7 @@ class MyBluetoothGattCallback(manager: MyCentralManager, executor: Executor) : } // 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 @@ -126,9 +108,7 @@ class MyBluetoothGattCallback(manager: MyCentralManager, executor: Executor) : } } - override fun onDescriptorWrite( - gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int - ) { + override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { super.onDescriptorWrite(gatt, descriptor, status) mExecutor.execute { mManager.onDescriptorWrite(gatt, descriptor, status) 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/hebei/bluetooth_low_energy_android/MyBluetoothGattServerCallback.kt similarity index 57% rename from bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothGattServerCallback.kt rename to bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyBluetoothGattServerCallback.kt index d66cfe9..93d5a0b 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/hebei/bluetooth_low_energy_android/MyBluetoothGattServerCallback.kt @@ -1,4 +1,4 @@ -package dev.yanshouwang.bluetooth_low_energy_android +package dev.hebei.bluetooth_low_energy_android import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothGattCharacteristic @@ -7,14 +7,13 @@ import android.bluetooth.BluetoothGattServerCallback import android.bluetooth.BluetoothGattService import java.util.concurrent.Executor -class MyBluetoothGattServerCallback(manager: MyPeripheralManager, executor: Executor) : - BluetoothGattServerCallback() { +class MyBluetoothGattServerCallback(manager: MyPeripheralManager, mExecutor: Executor) : BluetoothGattServerCallback() { private val mManager: MyPeripheralManager private val mExecutor: Executor init { mManager = manager - mExecutor = executor + this.mExecutor = mExecutor } override fun onServiceAdded(status: Int, service: BluetoothGattService) { @@ -38,41 +37,17 @@ class MyBluetoothGattServerCallback(manager: MyPeripheralManager, executor: Exec } } - 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) 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 - ) + 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) - mExecutor.execute { - mManager.onExecuteWrite(device, requestId, execute) + mManager.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value) } } @@ -83,31 +58,24 @@ class MyBluetoothGattServerCallback(manager: MyPeripheralManager, executor: Exec } } - 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) 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 - ) + 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 - ) + mManager.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value) + } + } + + override fun onExecuteWrite(device: BluetoothDevice, requestId: Int, execute: Boolean) { + super.onExecuteWrite(device, requestId, execute) + mExecutor.execute { + mManager.onExecuteWrite(device, requestId, execute) } } } \ No newline at end of file diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyBluetoothLowEnergyManager.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyBluetoothLowEnergyManager.kt new file mode 100644 index 0000000..6d3296b --- /dev/null +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyBluetoothLowEnergyManager.kt @@ -0,0 +1,46 @@ +package dev.hebei.bluetooth_low_energy_android + +import android.app.Activity +import android.bluetooth.BluetoothAdapter +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding +import io.flutter.plugin.common.PluginRegistry + +abstract class MyBluetoothLowEnergyManager(val context: Context) { + companion object { + const val AUTHORIZE_CODE = 0x00 + const val SHOW_APP_SETTINGS_CODE = 0x01 + } + + private val mBroadcastReceiver: BroadcastReceiver by lazy { MyBroadcastReceiver(this) } + private val mRequestPermissionsResultListener: PluginRegistry.RequestPermissionsResultListener by lazy { MyRequestPermissionResultListener(this) } + private val mActivityResultListener: PluginRegistry.ActivityResultListener by lazy { MyActivityResultListener(this) } + + private lateinit var mBinding: ActivityPluginBinding + + init { + val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) + context.registerReceiver(mBroadcastReceiver, filter) + } + + val activity: Activity get() = mBinding.activity + + fun onAttachedToActivity(binding: ActivityPluginBinding) { + binding.addRequestPermissionsResultListener(mRequestPermissionsResultListener) + binding.addActivityResultListener(mActivityResultListener) + mBinding = binding + } + + fun onDetachedFromActivity() { + mBinding.removeRequestPermissionsResultListener(mRequestPermissionsResultListener) + mBinding.removeActivityResultListener(mActivityResultListener) + } + + abstract fun onReceive(context: Context, intent: Intent) + abstract fun onRequestPermissionsResult(requestCode: Int, permissions: Array, results: IntArray): Boolean + abstract fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean +} + 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/hebei/bluetooth_low_energy_android/MyBroadcastReceiver.kt similarity index 88% rename from bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBroadcastReceiver.kt rename to bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyBroadcastReceiver.kt index 6895840..69ac8e7 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/hebei/bluetooth_low_energy_android/MyBroadcastReceiver.kt @@ -1,4 +1,4 @@ -package dev.yanshouwang.bluetooth_low_energy_android +package dev.hebei.bluetooth_low_energy_android import android.content.BroadcastReceiver import android.content.Context 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/hebei/bluetooth_low_energy_android/MyCentralManager.kt similarity index 57% rename from bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyCentralManager.kt rename to bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyCentralManager.kt index 0f6fc95..e30c44d 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/hebei/bluetooth_low_energy_android/MyCentralManager.kt @@ -1,10 +1,13 @@ -package dev.yanshouwang.bluetooth_low_energy_android +package dev.hebei.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.BluetoothManager import android.bluetooth.BluetoothProfile import android.bluetooth.BluetoothStatusCodes import android.bluetooth.le.BluetoothLeScanner @@ -13,47 +16,46 @@ 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.net.Uri import android.os.Build +import android.os.ParcelUuid +import android.provider.Settings +import androidx.core.app.ActivityCompat +import androidx.core.app.ActivityOptionsCompat +import androidx.core.content.ContextCompat import io.flutter.plugin.common.BinaryMessenger +import java.util.concurrent.Executor -class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : - MyBluetoothLowEnergyManager(context), MyCentralManagerHostApi { - companion object { - const val REQUEST_CODE = 443 - } +class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : MyBluetoothLowEnergyManager(context), MyCentralManagerHostAPI { + private val mAPI: MyCentralManagerFlutterAPI - 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) - } + private val mScanCallback: ScanCallback by lazy { MyScanCallback(this) } + private val mBluetoothGattCallback: BluetoothGattCallback by lazy { MyBluetoothGattCallback(this, executor) } private var mDiscovering: Boolean private val mDevices: MutableMap private val mGATTs: MutableMap - private val mCharacteristics: MutableMap> - private val mDescriptors: MutableMap> + private val mCharacteristics: MutableMap> + private val mDescriptors: MutableMap> - private var mSetUpCallback: ((Result) -> Unit)? + private var mAuthorizeCallback: ((Result) -> Unit)? + private var mShowAppSettingsCallback: ((Result) -> Unit)? 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 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) + mAPI = MyCentralManagerFlutterAPI(binaryMessenger) mDiscovering = false @@ -62,7 +64,8 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : mCharacteristics = mutableMapOf() mDescriptors = mutableMapOf() - mSetUpCallback = null + mAuthorizeCallback = null + mShowAppSettingsCallback = null mStartDiscoveryCallback = null mConnectCallbacks = mutableMapOf() mDisconnectCallbacks = mutableMapOf() @@ -75,56 +78,91 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : mWriteDescriptorCallbacks = mutableMapOf() } - private val mScanner: BluetoothLeScanner get() = adapter.bluetoothLeScanner - - override val permissions: Array + private 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 - ) + 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 - ) + arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION) + } + private val manager get() = ContextCompat.getSystemService(context, BluetoothManager::class.java) as BluetoothManager + private val adapter get() = manager.adapter as BluetoothAdapter + private val scanner: BluetoothLeScanner get() = adapter.bluetoothLeScanner + private val executor get() = ContextCompat.getMainExecutor(context) as Executor + + override fun initialize(): MyCentralManagerArgs { + if (mDiscovering) { + stopDiscovery() } - override val requestCode: Int - get() = REQUEST_CODE + for (gatt in mGATTs.values) { + gatt.disconnect() + } - override fun setUp(callback: (Result) -> Unit) { + mDevices.clear() + mGATTs.clear() + mCharacteristics.clear() + mDescriptors.clear() + + mAuthorizeCallback = null + mShowAppSettingsCallback = null + mStartDiscoveryCallback = null + mConnectCallbacks.clear() + mDisconnectCallbacks.clear() + mRequestMtuCallbacks.clear() + mReadRssiCallbacks.clear() + mDiscoverServicesCallbacks.clear() + mReadCharacteristicCallbacks.clear() + mWriteCharacteristicCallbacks.clear() + mReadDescriptorCallbacks.clear() + mWriteDescriptorCallbacks.clear() + + val enableNotificationValue = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + val enableIndicationValue = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE + val disableNotificationValue = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE + return MyCentralManagerArgs(enableNotificationValue, enableIndicationValue, disableNotificationValue) + } + + override fun getState(): MyBluetoothLowEnergyStateArgs { + val supported = context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) + return if (supported) { + val authorized = permissions.all { permission -> ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED } + return if (authorized) adapter.state.toBluetoothLowEnergyStateArgs() + else MyBluetoothLowEnergyStateArgs.UNAUTHORIZED + } else MyBluetoothLowEnergyStateArgs.UNSUPPORTED + } + + override fun authorize(callback: (Result) -> Unit) { try { - mClearState() - val stateArgs = if (hasFeature) { - val granted = checkPermissions() - if (granted) { - registerReceiver() - adapter.state.toBluetoothLowEnergyStateArgs() - } else { - requestPermissions() - mSetUpCallback = callback - return - } - } else MyBluetoothLowEnergyStateArgs.UNSUPPORTED - mOnStateChanged(stateArgs) - callback(Result.success(Unit)) + ActivityCompat.requestPermissions(activity, permissions, AUTHORIZE_CODE) + mAuthorizeCallback = callback } catch (e: Throwable) { callback(Result.failure(e)) } } - override fun startDiscovery(callback: (Result) -> Unit) { + override fun showAppSettings(callback: (Result) -> Unit) { try { - val filters = emptyList() - val settings = - ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build() - mScanner.startScan(filters, settings, mScanCallback) - executor.execute { - onScanSucceed() + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intent.data = Uri.fromParts("package", activity.packageName, null) + val options = ActivityOptionsCompat.makeBasic().toBundle() + ActivityCompat.startActivityForResult(activity, intent, SHOW_APP_SETTINGS_CODE, options) + mShowAppSettingsCallback = callback + } catch (e: Throwable) { + callback(Result.failure(e)) + } + } + + override fun startDiscovery(serviceUUIDsArgs: List, callback: (Result) -> Unit) { + try { + val filters = mutableListOf() + for (serviceUuidArgs in serviceUUIDsArgs) { + val serviceUUID = ParcelUuid.fromString(serviceUuidArgs) + val filter = ScanFilter.Builder().setServiceUuid(serviceUUID).build() + filters.add(filter) } + val settings = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build() + scanner.startScan(filters, settings, mScanCallback) + executor.execute { onScanSucceeded() } mStartDiscoveryCallback = callback } catch (e: Throwable) { callback(Result.failure(e)) @@ -132,20 +170,19 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } override fun stopDiscovery() { - mScanner.stopScan(mScanCallback) + scanner.stopScan(mScanCallback) mDiscovering = false } override fun connect(addressArgs: String, callback: (Result) -> Unit) { try { - val device = mDevices[addressArgs] as BluetoothDevice - val autoConnect = false - // Add to bluetoothGATTs cache. + val device = mDevices[addressArgs] ?: throw IllegalArgumentException() + val autoConnect = false // Add to bluetoothGATTs cache. mGATTs[addressArgs] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val transport = BluetoothDevice.TRANSPORT_LE - device.connectGatt(mContext, autoConnect, mBluetoothGattCallback, transport) + device.connectGatt(context, autoConnect, mBluetoothGattCallback, transport) } else { - device.connectGatt(mContext, autoConnect, mBluetoothGattCallback) + device.connectGatt(context, autoConnect, mBluetoothGattCallback) } mConnectCallbacks[addressArgs] = callback } catch (e: Throwable) { @@ -155,7 +192,7 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : override fun disconnect(addressArgs: String, callback: (Result) -> Unit) { try { - val gatt = mGATTs[addressArgs] as BluetoothGatt + val gatt = mGATTs[addressArgs] ?: throw IllegalArgumentException() gatt.disconnect() mDisconnectCallbacks[addressArgs] = callback } catch (e: Throwable) { @@ -163,9 +200,21 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } + override fun retrieveConnectedPeripherals(): List { + // The `BluetoothProfile.GATT` and `BluetoothProfile.GATT_SERVER` return same devices. + val devices = manager.getConnectedDevices(BluetoothProfile.GATT) + val peripheralsArgs = devices.map { device -> + val peripheralArgs = device.toPeripheralArgs() + val addressArgs = peripheralArgs.addressArgs + mDevices[addressArgs] = device + return@map peripheralArgs + } + return peripheralsArgs + } + override fun requestMTU(addressArgs: String, mtuArgs: Long, callback: (Result) -> Unit) { try { - val gatt = mGATTs[addressArgs] as BluetoothGatt + val gatt = mGATTs[addressArgs] ?: throw IllegalArgumentException() val mtu = mtuArgs.toInt() val requesting = gatt.requestMtu(mtu) if (!requesting) { @@ -179,7 +228,7 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : override fun readRSSI(addressArgs: String, callback: (Result) -> Unit) { try { - val gatt = mGATTs[addressArgs] as BluetoothGatt + val gatt = mGATTs[addressArgs] ?: throw IllegalArgumentException() val reading = gatt.readRemoteRssi() if (!reading) { throw IllegalStateException() @@ -190,11 +239,9 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } - override fun discoverServices( - addressArgs: String, callback: (Result>) -> Unit - ) { + override fun discoverGATT(addressArgs: String, callback: (Result>) -> Unit) { try { - val gatt = mGATTs[addressArgs] as BluetoothGatt + val gatt = mGATTs[addressArgs] ?: throw IllegalArgumentException() val discovering = gatt.discoverServices() if (!discovering) { throw IllegalStateException() @@ -205,13 +252,10 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } - override fun readCharacteristic( - addressArgs: String, hashCodeArgs: Long, callback: (Result) -> Unit - ) { + override fun readCharacteristic(addressArgs: String, hashCodeArgs: Long, callback: (Result) -> Unit) { try { - val gatt = mGATTs[addressArgs] as BluetoothGatt - val characteristic = - mRetrieveCharacteristic(addressArgs, hashCodeArgs) as BluetoothGattCharacteristic + val gatt = mGATTs[addressArgs] ?: throw IllegalArgumentException() + val characteristic = retrieveCharacteristic(addressArgs, hashCodeArgs) val reading = gatt.readCharacteristic(characteristic) if (!reading) { throw IllegalStateException() @@ -223,26 +267,15 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } - override fun writeCharacteristic( - addressArgs: String, - hashCodeArgs: Long, - valueArgs: ByteArray, - typeNumberArgs: Long, - callback: (Result) -> Unit - ) { + override fun writeCharacteristic(addressArgs: String, hashCodeArgs: Long, valueArgs: ByteArray, typeArgs: MyGATTCharacteristicWriteTypeArgs, callback: (Result) -> Unit) { try { - 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 gatt = mGATTs[addressArgs] ?: throw IllegalArgumentException() + val characteristic = retrieveCharacteristic(addressArgs, hashCodeArgs) val type = typeArgs.toType() val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val code = gatt.writeCharacteristic(characteristic, valueArgs, type) code == BluetoothStatusCodes.SUCCESS - } else { - // TODO: remove this when minSdkVersion >= 33 + } else { // TODO: remove this when minSdkVersion >= 33 characteristic.value = valueArgs characteristic.writeType = type gatt.writeCharacteristic(characteristic) @@ -257,47 +290,19 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } - override fun setCharacteristicNotifyState( - addressArgs: String, - hashCodeArgs: Long, - stateNumberArgs: Long, - callback: (Result) -> Unit - ) { - try { - 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 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 setCharacteristicNotification(addressArgs: String, hashCodeArgs: Long, enableArgs: Boolean) { + val gatt = mGATTs[addressArgs] ?: throw IllegalArgumentException() + val characteristic = retrieveCharacteristic(addressArgs, hashCodeArgs) + val notifying = gatt.setCharacteristicNotification(characteristic, enableArgs) + if (!notifying) { + throw IllegalStateException() } } - override fun readDescriptor( - addressArgs: String, hashCodeArgs: Long, callback: (Result) -> Unit - ) { + override fun readDescriptor(addressArgs: String, hashCodeArgs: Long, callback: (Result) -> Unit) { try { - val gatt = mGATTs[addressArgs] as BluetoothGatt - val descriptor = - mRetrieveDescriptor(addressArgs, hashCodeArgs) as BluetoothGattDescriptor + val gatt = mGATTs[addressArgs] ?: throw IllegalArgumentException() + val descriptor = retrieveDescriptor(addressArgs, hashCodeArgs) val reading = gatt.readDescriptor(descriptor) if (!reading) { throw IllegalStateException() @@ -309,21 +314,14 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } - override fun writeDescriptor( - addressArgs: String, - hashCodeArgs: Long, - valueArgs: ByteArray, - callback: (Result) -> Unit - ) { + override fun writeDescriptor(addressArgs: String, hashCodeArgs: Long, valueArgs: ByteArray, callback: (Result) -> Unit) { try { - val gatt = mGATTs[addressArgs] as BluetoothGatt - val descriptor = - mRetrieveDescriptor(addressArgs, hashCodeArgs) as BluetoothGattDescriptor + val gatt = mGATTs[addressArgs] ?: throw IllegalArgumentException() + val descriptor = retrieveDescriptor(addressArgs, hashCodeArgs) val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val code = gatt.writeDescriptor(descriptor, valueArgs) code == BluetoothStatusCodes.SUCCESS - } else { - // TODO: remove this when minSdkVersion >= 33 + } else { // TODO: remove this when minSdkVersion >= 33 descriptor.value = valueArgs gatt.writeDescriptor(descriptor) } @@ -337,22 +335,37 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } - override fun onPermissionsRequested(granted: Boolean) { - val callback = mSetUpCallback ?: return - val stateArgs = if (granted) { - registerReceiver() - adapter.state.toBluetoothLowEnergyStateArgs() - } else MyBluetoothLowEnergyStateArgs.UNAUTHORIZED - mOnStateChanged(stateArgs) - callback(Result.success(Unit)) - } - - override fun onAdapterStateChanged(state: Int) { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action != BluetoothAdapter.ACTION_STATE_CHANGED) { + return + } + val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) val stateArgs = state.toBluetoothLowEnergyStateArgs() - mOnStateChanged(stateArgs) + mAPI.onStateChanged(stateArgs) {} } - private fun onScanSucceed() { + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, results: IntArray): Boolean { + if (requestCode != AUTHORIZE_CODE) { + return false + } + val callback = mAuthorizeCallback ?: return false + mAuthorizeCallback = null + val authorized = permissions.contentEquals(this.permissions) && results.all { r -> r == PackageManager.PERMISSION_GRANTED } + callback(Result.success(authorized)) + return true + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { + if (requestCode != SHOW_APP_SETTINGS_CODE) { + return false + } + val callback = mShowAppSettingsCallback ?: return false + mShowAppSettingsCallback = null + callback(Result.success(Unit)) + return true + } + + private fun onScanSucceeded() { mDiscovering = true val callback = mStartDiscoveryCallback ?: return mStartDiscoveryCallback = null @@ -370,16 +383,15 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : val device = result.device val peripheralArgs = device.toPeripheralArgs() val addressArgs = peripheralArgs.addressArgs - val rssiArgs = result.rssi.toLong() + val rssiArgs = result.rssi.args val advertisementArgs = result.toAdvertisementArgs() mDevices[addressArgs] = device - mApi.onDiscovered(peripheralArgs, rssiArgs, advertisementArgs) {} + mAPI.onDiscovered(peripheralArgs, rssiArgs, advertisementArgs) {} } fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { val device = gatt.device - val addressArgs = device.address - // check connection state. + val addressArgs = device.address // check connection state. if (newState == BluetoothProfile.STATE_DISCONNECTED) { gatt.close() mGATTs.remove(addressArgs) @@ -427,9 +439,7 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } } - val stateArgs = newState == BluetoothProfile.STATE_CONNECTED - mApi.onConnectionStateChanged(addressArgs, stateArgs) {} - // check connect & disconnect callbacks. + // check connect callback. val connectCallback = mConnectCallbacks.remove(addressArgs) if (connectCallback != null) { if (status == BluetoothGatt.GATT_SUCCESS) { @@ -439,6 +449,7 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : connectCallback(Result.failure(error)) } } + // check disconnect callback. val disconnectCallback = mDisconnectCallbacks.remove(addressArgs) if (disconnectCallback != null) { if (status == BluetoothGatt.GATT_SUCCESS) { @@ -448,14 +459,19 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : disconnectCallback(Result.failure(error)) } } + // invoke connection state changed event. + val peripheralArgs = device.toPeripheralArgs() + val stateArgs = newState.toConnectionStateArgs() + mAPI.onConnectionStateChanged(peripheralArgs, stateArgs) {} } fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { val device = gatt.device val addressArgs = device.address val result = if (status == BluetoothGatt.GATT_SUCCESS) { - val mtuArgs = mtu.toLong() - mApi.onMtuChanged(addressArgs, mtuArgs) {} + val peripheralArgs = device.toPeripheralArgs() + val mtuArgs = mtu.args + mAPI.onMTUChanged(peripheralArgs, mtuArgs) {} Result.success(mtuArgs) } else { val error = IllegalStateException("Read RSSI failed with status: $status") @@ -470,7 +486,7 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : val addressArgs = device.address val callback = mReadRssiCallbacks.remove(addressArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { - val rssiArgs = rssi.toLong() + val rssiArgs = rssi.args callback(Result.success(rssiArgs)) } else { val error = IllegalStateException("Read RSSI failed with status: $status") @@ -484,10 +500,9 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : val callback = mDiscoverServicesCallbacks.remove(addressArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { val services = gatt.services - 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() } + for (service in services) { + addService(addressArgs, service) + } val servicesArgs = services.map { it.toArgs() } callback(Result.success(servicesArgs)) } else { @@ -496,15 +511,10 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } - fun onCharacteristicRead( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int, - value: ByteArray - ) { + fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int, value: ByteArray) { val device = gatt.device val addressArgs = device.address - val hashCodeArgs = characteristic.hashCode().toLong() + val hashCodeArgs = characteristic.hashCode.args val callbacks = mReadCharacteristicCallbacks[addressArgs] ?: return val callback = callbacks.remove(hashCodeArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { @@ -515,12 +525,10 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } - fun onCharacteristicWrite( - gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int - ) { + fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { val device = gatt.device val addressArgs = device.address - val hashCodeArgs = characteristic.hashCode().toLong() + val hashCodeArgs = characteristic.hashCode.args val callbacks = mWriteCharacteristicCallbacks[addressArgs] ?: return val callback = callbacks.remove(hashCodeArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { @@ -531,21 +539,17 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } - fun onCharacteristicChanged( - gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray - ) { + 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) {} + val peripheralArgs = device.toPeripheralArgs() + val characteristicArgs = characteristic.toArgs() + mAPI.onCharacteristicNotified(peripheralArgs, characteristicArgs, value) {} } - fun onDescriptorRead( - gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray - ) { + fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) { val device = gatt.device val addressArgs = device.address - val hashCodeArgs = descriptor.hashCode().toLong() + val hashCodeArgs = descriptor.hashCode.args val callbacks = mReadDescriptorCallbacks[addressArgs] ?: return val callback = callbacks.remove(hashCodeArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { @@ -559,7 +563,7 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { val device = gatt.device val addressArgs = device.address - val hashCodeArgs = descriptor.hashCode().toLong() + val hashCodeArgs = descriptor.hashCode.args val callbacks = mWriteDescriptorCallbacks[addressArgs] ?: return val callback = callbacks.remove(hashCodeArgs) ?: return if (status == BluetoothGatt.GATT_SUCCESS) { @@ -570,47 +574,28 @@ class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) : } } - private fun mClearState() { - if (mDiscovering) { - stopDiscovery() + private fun addService(addressArgs: String, service: BluetoothGattService) { + val includedServices = service.includedServices + for (includedService in includedServices) { + addService(addressArgs, includedService) } - for (gatt in mGATTs.values) { - gatt.disconnect() + for (characteristic in service.characteristics) { + for (descriptor in characteristic.descriptors) { + val descriptors = mDescriptors.getOrPut(addressArgs) { mutableMapOf() } + descriptors[descriptor.hashCode.args] = descriptor + } + val characteristics = mCharacteristics.getOrPut(addressArgs) { mutableMapOf() } + characteristics[characteristic.hashCode.args] = characteristic } - mDevices.clear() - mGATTs.clear() - mCharacteristics.clear() - mDescriptors.clear() - - mSetUpCallback = null - mStartDiscoveryCallback = null - mConnectCallbacks.clear() - mDisconnectCallbacks.clear() - mRequestMtuCallbacks.clear() - mReadRssiCallbacks.clear() - mDiscoverServicesCallbacks.clear() - mReadCharacteristicCallbacks.clear() - mWriteCharacteristicCallbacks.clear() - mReadDescriptorCallbacks.clear() - mWriteDescriptorCallbacks.clear() } - private fun mOnStateChanged(stateArgs: MyBluetoothLowEnergyStateArgs) { - val stateNumberArgs = stateArgs.raw.toLong() - mApi.onStateChanged(stateNumberArgs) {} + private fun retrieveCharacteristic(addressArgs: String, hashCodeArgs: Long): BluetoothGattCharacteristic { + val characteristics = mCharacteristics[addressArgs] ?: throw IllegalArgumentException() + return characteristics[hashCodeArgs] ?: throw IllegalArgumentException() } - 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] + private fun retrieveDescriptor(addressArgs: String, hashCodeArgs: Long): BluetoothGattDescriptor { + val descriptors = mDescriptors[addressArgs] ?: throw IllegalArgumentException() + return descriptors[hashCodeArgs] ?: throw IllegalArgumentException() } } diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyPeripheralManager.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyPeripheralManager.kt new file mode 100644 index 0000000..1e67f6a --- /dev/null +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyPeripheralManager.kt @@ -0,0 +1,446 @@ +package dev.hebei.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.BluetoothManager +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.net.Uri +import android.os.Build +import android.provider.Settings +import androidx.core.app.ActivityCompat +import androidx.core.app.ActivityOptionsCompat +import androidx.core.content.ContextCompat +import io.flutter.plugin.common.BinaryMessenger +import java.util.concurrent.Executor + +class MyPeripheralManager(context: Context, binaryMessenger: BinaryMessenger) : MyBluetoothLowEnergyManager(context), MyPeripheralManagerHostAPI { + private val mAPI: MyPeripheralManagerFlutterAPI + + private val mBluetoothGattServerCallback: BluetoothGattServerCallback by lazy { MyBluetoothGattServerCallback(this, executor) } + private val mAdvertiseCallback: AdvertiseCallback by lazy { MyAdvertiseCallback(this) } + + private var mServer: BluetoothGattServer? + private var mAdvertising: Boolean + + 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 mAuthorizeCallback: ((Result) -> Unit)? + private var mShowAppSettingsCallback: ((Result) -> Unit)? + private var mAddServiceCallback: ((Result) -> Unit)? + private var mStartAdvertisingCallback: ((Result) -> Unit)? + private val mNotifyCharacteristicValueChangedCallbacks: MutableMap) -> Unit> + + init { + mAPI = MyPeripheralManagerFlutterAPI(binaryMessenger) + + mServer = null + mAdvertising = false + + mServicesArgs = mutableMapOf() + mCharacteristicsArgs = mutableMapOf() + mDescriptorsArgs = mutableMapOf() + + mDevices = mutableMapOf() + mServices = mutableMapOf() + mCharacteristics = mutableMapOf() + mDescriptors = mutableMapOf() + + mAuthorizeCallback = null + mShowAppSettingsCallback = null + mAddServiceCallback = null + mStartAdvertisingCallback = null + mNotifyCharacteristicValueChangedCallbacks = mutableMapOf() + } + + private 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) + } + private val manager get() = ContextCompat.getSystemService(context, BluetoothManager::class.java) as BluetoothManager + private val adapter get() = manager.adapter as BluetoothAdapter + private val advertiser get() = adapter.bluetoothLeAdvertiser + private val server get() = mServer ?: throw IllegalStateException() + private val executor get() = ContextCompat.getMainExecutor(context) as Executor + + override fun initialize(): MyPeripheralManagerArgs { + if (mAdvertising) { + stopAdvertising() + } + + mServer?.close() + + mServicesArgs.clear() + mCharacteristicsArgs.clear() + mDescriptorsArgs.clear() + + mDevices.clear() + mServices.clear() + mCharacteristics.clear() + mDescriptors.clear() + + mAuthorizeCallback = null + mShowAppSettingsCallback = null + mAddServiceCallback = null + mStartAdvertisingCallback = null + mNotifyCharacteristicValueChangedCallbacks.clear() + + val enableNotificationValue = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE + val enableIndicationValue = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE + val disableNotificationValue = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE + return MyPeripheralManagerArgs(enableNotificationValue, enableIndicationValue, disableNotificationValue) + } + + override fun getState(): MyBluetoothLowEnergyStateArgs { + val supported = context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) + return if (supported) { + val authorized = permissions.all { permission -> ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED } + return if (authorized) adapter.state.toBluetoothLowEnergyStateArgs() + else MyBluetoothLowEnergyStateArgs.UNAUTHORIZED + } else MyBluetoothLowEnergyStateArgs.UNSUPPORTED + } + + override fun authorize(callback: (Result) -> Unit) { + try { + ActivityCompat.requestPermissions(activity, permissions, AUTHORIZE_CODE) + mAuthorizeCallback = callback + } catch (e: Throwable) { + callback(Result.failure(e)) + } + } + + override fun showAppSettings(callback: (Result) -> Unit) { + try { + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intent.data = Uri.fromParts("package", activity.packageName, null) + val options = ActivityOptionsCompat.makeBasic().toBundle() + ActivityCompat.startActivityForResult(activity, intent, SHOW_APP_SETTINGS_CODE, options) + mShowAppSettingsCallback = callback + } catch (e: Throwable) { + callback(Result.failure(e)) + } + } + + override fun openGATTServer() { + mServer = manager.openGattServer(context, mBluetoothGattServerCallback) + } + + override fun closeGATTServer() { + server.close() + } + + override fun addService(serviceArgs: MyMutableGATTServiceArgs, callback: (Result) -> Unit) { + try { + val service = addServiceArgs(serviceArgs) + val adding = server.addService(service) + if (!adding) { + throw IllegalStateException() + } + mAddServiceCallback = callback + } catch (e: Throwable) { + callback(Result.failure(e)) + } + } + + override fun removeService(hashCodeArgs: Long) { + val service = mServices[hashCodeArgs] ?: throw IllegalArgumentException() + val removed = server.removeService(service) + if (!removed) { + throw IllegalStateException() + } + val hashCode = service.hashCode() + val serviceArgs = mServicesArgs[hashCode] ?: throw IllegalArgumentException() + removeServiceArgs(serviceArgs) + } + + override fun removeAllServices() { + server.clearServices() + mServices.clear() + mCharacteristics.clear() + mDescriptors.clear() + + mServicesArgs.clear() + mCharacteristicsArgs.clear() + mDescriptorsArgs.clear() + } + + override fun startAdvertising(advertisementArgs: MyAdvertisementArgs, callback: (Result) -> Unit) { + try { + val settings = AdvertiseSettings.Builder().setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED).setConnectable(true).build() + // 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 + val nameArgs = advertisementArgs.nameArgs + if (nameArgs != null) { + adapter.name = nameArgs + } + val advertiseData = advertisementArgs.toAdvertiseData() + val scanResponse = advertisementArgs.toScanResponse() + advertiser.startAdvertising(settings, advertiseData, scanResponse, mAdvertiseCallback) + mStartAdvertisingCallback = callback + } catch (e: Throwable) { + callback(Result.failure(e)) + } + } + + override fun stopAdvertising() { + advertiser.stopAdvertising(mAdvertiseCallback) + mAdvertising = false + } + + override fun sendResponse(addressArgs: String, idArgs: Long, statusArgs: MyGATTStatusArgs, offsetArgs: Long, valueArgs: ByteArray?) { + val device = mDevices[addressArgs] ?: throw IllegalArgumentException() + val requestId = idArgs.toInt() + val status = statusArgs.toStatus() + val offset = offsetArgs.toInt() + val sent = server.sendResponse(device, requestId, status, offset, valueArgs) + if (!sent) { + throw IllegalStateException("Send response failed.") + } + } + + override fun notifyCharacteristicChanged(addressArgs: String, hashCodeArgs: Long, confirmArgs: Boolean, valueArgs: ByteArray, callback: (Result) -> Unit) { + try { + val device = mDevices[addressArgs] ?: throw IllegalArgumentException() + val characteristic = mCharacteristics[hashCodeArgs] ?: throw IllegalArgumentException() + val notifying = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val statusCode = server.notifyCharacteristicChanged(device, characteristic, confirmArgs, valueArgs) + statusCode == BluetoothStatusCodes.SUCCESS + } else { // TODO: remove this when minSdkVersion >= 33 + characteristic.value = valueArgs + server.notifyCharacteristicChanged(device, characteristic, confirmArgs) + } + if (!notifying) { + throw IllegalStateException() + } + mNotifyCharacteristicValueChangedCallbacks[addressArgs] = callback + } catch (e: Throwable) { + callback(Result.failure(e)) + } + } + + override fun onReceive(context: Context, intent: Intent) { + if (intent.action != BluetoothAdapter.ACTION_STATE_CHANGED) { + return + } + val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) + val stateArgs = state.toBluetoothLowEnergyStateArgs() + mAPI.onStateChanged(stateArgs) {} + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, results: IntArray): Boolean { + if (requestCode != AUTHORIZE_CODE) { + return false + } + val callback = mAuthorizeCallback ?: return false + mAuthorizeCallback = null + val authorized = permissions.contentEquals(this.permissions) && results.all { r -> r == PackageManager.PERMISSION_GRANTED } + callback(Result.success(authorized)) + return true + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { + if (requestCode != SHOW_APP_SETTINGS_CODE) { + return false + } + val callback = mShowAppSettingsCallback ?: return false + mShowAppSettingsCallback = null + callback(Result.success(Unit)) + return true + } + + fun onServiceAdded(status: Int, service: BluetoothGattService) { + val callback = mAddServiceCallback ?: return + mAddServiceCallback = null + if (status == BluetoothGatt.GATT_SUCCESS) { + callback(Result.success(Unit)) + } else { + val error = IllegalStateException("Read rssi failed with status: $status") + callback(Result.failure(error)) + } + } + + fun onStartSuccess(settingsInEffect: AdvertiseSettings) { + mAdvertising = true + val callback = mStartAdvertisingCallback ?: return + mStartAdvertisingCallback = null + callback(Result.success(Unit)) + } + + fun onStartFailure(errorCode: Int) { + 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 centralArgs = device.toCentralArgs() + val addressArgs = centralArgs.addressArgs + val statusArgs = status.args + val stateArgs = newState.toConnectionStateArgs() + mDevices[addressArgs] = device + mAPI.onConnectionStateChanged(centralArgs, statusArgs, stateArgs) {} + } + + fun onMtuChanged(device: BluetoothDevice, mtu: Int) { + val centralArgs = device.toCentralArgs() + val mtuArgs = mtu.args + mAPI.onMTUChanged(centralArgs, mtuArgs) {} + } + + fun onCharacteristicReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, characteristic: BluetoothGattCharacteristic) { + val centralArgs = device.toCentralArgs() + val idArgs = requestId.args + val offsetArgs = offset.args + val hashCode = characteristic.hashCode() + val characteristicArgs = mCharacteristicsArgs[hashCode] + if (characteristicArgs == null) { + val status = BluetoothGatt.GATT_FAILURE + server.sendResponse(device, requestId, status, offset, null) + } else { + val hashCodeArgs = characteristicArgs.hashCodeArgs + mAPI.onCharacteristicReadRequest(centralArgs, idArgs, offsetArgs, hashCodeArgs) {} + } + } + + fun onCharacteristicWriteRequest(device: BluetoothDevice, requestId: Int, characteristic: BluetoothGattCharacteristic, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) { + val centralArgs = device.toCentralArgs() + val idArgs = requestId.args + val hashCode = characteristic.hashCode() + val characteristicArgs = mCharacteristicsArgs[hashCode] + if (characteristicArgs == null) { + if (!responseNeeded) { + return + } + val status = BluetoothGatt.GATT_FAILURE + server.sendResponse(device, requestId, status, offset, null) + } else { + val hashCodeArgs = characteristicArgs.hashCodeArgs + val offsetArgs = offset.args + mAPI.onCharacteristicWriteRequest(centralArgs, idArgs, hashCodeArgs, preparedWrite, responseNeeded, offsetArgs, value) {} + } + } + + fun onNotificationSent(device: BluetoothDevice, status: Int) { + 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") + callback(Result.failure(error)) + } + } + + fun onDescriptorReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor) { + val centralArgs = device.toCentralArgs() + val idArgs = requestId.args + val offsetArgs = offset.args + val hashCode = descriptor.hashCode() + val descriptorArgs = mDescriptorsArgs[hashCode] + if (descriptorArgs == null) { + val status = BluetoothGatt.GATT_FAILURE + server.sendResponse(device, requestId, status, offset, null) + } else { + val hashCodeArgs = descriptorArgs.hashCodeArgs + mAPI.onDescriptorReadRequest(centralArgs, idArgs, offsetArgs, hashCodeArgs) {} + } + } + + fun onDescriptorWriteRequest(device: BluetoothDevice, requestId: Int, descriptor: BluetoothGattDescriptor, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) { + val centralArgs = device.toCentralArgs() + val idArgs = requestId.args + val hashCode = descriptor.hashCode() + val descriptorArgs = mDescriptorsArgs[hashCode] + if (descriptorArgs == null) { + if (!responseNeeded) { + return + } + val status = BluetoothGatt.GATT_FAILURE + server.sendResponse(device, requestId, status, offset, null) + } else { + val hashCodeArgs = descriptorArgs.hashCodeArgs + val offsetArgs = offset.args + mAPI.onDescriptorWriteRequest(centralArgs, idArgs, hashCodeArgs, preparedWrite, responseNeeded, offsetArgs, value) {} + } + } + + fun onExecuteWrite(device: BluetoothDevice, requestId: Int, execute: Boolean) { + val centralArgs = device.toCentralArgs() + val idArgs = requestId.args + mAPI.onExecuteWrite(centralArgs, idArgs, execute) {} + } + + private fun addServiceArgs(serviceArgs: MyMutableGATTServiceArgs): BluetoothGattService { + val service = serviceArgs.toService() + this.mServicesArgs[service.hashCode] = serviceArgs + this.mServices[serviceArgs.hashCodeArgs] = service + val includedServicesArgs = serviceArgs.includedServicesArgs.requireNoNulls() + for (includedServiceArgs in includedServicesArgs) { + val includedService = addServiceArgs(includedServiceArgs) + val adding = service.addService(includedService) + if (!adding) { + throw IllegalStateException() + } + } + val characteristicsArgs = serviceArgs.characteristicsArgs.requireNoNulls() + for (characteristicArgs in characteristicsArgs) { + val characteristic = characteristicArgs.toCharacteristic() + this.mCharacteristicsArgs[characteristic.hashCode] = characteristicArgs + this.mCharacteristics[characteristicArgs.hashCodeArgs] = characteristic + val descriptorsArgs = characteristicArgs.descriptorsArgs.requireNoNulls() + for (descriptorArgs in descriptorsArgs) { + val descriptor = descriptorArgs.toDescriptor() + this.mDescriptorsArgs[descriptor.hashCode] = descriptorArgs + this.mDescriptors[descriptorArgs.hashCodeArgs] = descriptor + val descriptorAdded = characteristic.addDescriptor(descriptor) + if (!descriptorAdded) { + throw IllegalStateException() + } + } + val characteristicAdded = service.addCharacteristic(characteristic) + if (!characteristicAdded) { + throw IllegalStateException() + } + } + return service + } + + private fun removeServiceArgs(serviceArgs: MyMutableGATTServiceArgs) { + val includedServicesArgs = serviceArgs.includedServicesArgs.requireNoNulls() + for (includedServiceArgs in includedServicesArgs) { + removeServiceArgs(includedServiceArgs) + } + val characteristicsArgs = serviceArgs.characteristicsArgs.requireNoNulls() + for (characteristicArgs in characteristicsArgs) { + val descriptorsArgs = characteristicArgs.descriptorsArgs.requireNoNulls() + for (descriptorArgs in descriptorsArgs) { + val descriptor = mDescriptors.remove(descriptorArgs.hashCodeArgs) ?: throw IllegalArgumentException() + this.mDescriptorsArgs.remove(descriptor.hashCode) + } + val characteristic = mCharacteristics.remove(characteristicArgs.hashCodeArgs) ?: throw IllegalArgumentException() + this.mCharacteristicsArgs.remove(characteristic.hashCode) + } + val service = mServices.remove(serviceArgs.hashCodeArgs) ?: throw IllegalArgumentException() + mServicesArgs.remove(service.hashCode) + } +} \ No newline at end of file diff --git a/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyRequestPermissionResultListener.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyRequestPermissionResultListener.kt new file mode 100644 index 0000000..0ba6629 --- /dev/null +++ b/bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyRequestPermissionResultListener.kt @@ -0,0 +1,15 @@ +package dev.hebei.bluetooth_low_energy_android + +import io.flutter.plugin.common.PluginRegistry + +class MyRequestPermissionResultListener(manager: MyBluetoothLowEnergyManager) : PluginRegistry.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/hebei/bluetooth_low_energy_android/MyScanCallback.kt similarity index 91% rename from bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyScanCallback.kt rename to bluetooth_low_energy_android/android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyScanCallback.kt index 155b078..5ccdcf7 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/hebei/bluetooth_low_energy_android/MyScanCallback.kt @@ -1,4 +1,4 @@ -package dev.yanshouwang.bluetooth_low_energy_android +package dev.hebei.bluetooth_low_energy_android import android.bluetooth.le.ScanCallback import android.bluetooth.le.ScanResult 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 deleted file mode 100644 index 5cb28f5..0000000 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.kt +++ /dev/null @@ -1,270 +0,0 @@ -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.ScanResult -import android.os.ParcelUuid -import android.util.SparseArray -import java.util.UUID - -//region ToObj -fun MyGattCharacteristicWriteTypeArgs.toType(): Int { - return when (this) { - MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT - MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE - } -} - -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 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 - } -} - -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) - } - for (serviceUuidArgs in serviceUUIDsArgs) { - val serviceUUID = ParcelUuid.fromString(serviceUuidArgs) - advertiseDataBuilder.addServiceUuid(serviceUUID) - } - for (entry in serviceDataArgs) { - val serviceDataUUID = ParcelUuid.fromString(entry.key as String) - val serviceData = entry.value as ByteArray - advertiseDataBuilder.addServiceData(serviceDataUUID, serviceData) - } - if (manufacturerSpecificDataArgs != null) { - val manufacturerId = manufacturerSpecificDataArgs.idArgs.toInt() - val manufacturerSpecificData = manufacturerSpecificDataArgs.dataArgs - advertiseDataBuilder.addManufacturerData(manufacturerId, manufacturerSpecificData) - } - return advertiseDataBuilder.build() -} - -fun MyGattDescriptorArgs.toDescriptor(): BluetoothGattDescriptor { - val uuid = UUID.fromString(uuidArgs) - val permissions = - BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE - return BluetoothGattDescriptor(uuid, permissions) -} - -fun MyGattCharacteristicArgs.toCharacteristic(): BluetoothGattCharacteristic { - val uuid = UUID.fromString(uuidArgs) - val properties = getProperties() - val permissions = getPermissions() - return BluetoothGattCharacteristic(uuid, properties, permissions) -} - -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 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 { - val uuid = UUID.fromString(uuidArgs) - val serviceType = BluetoothGattService.SERVICE_TYPE_PRIMARY - return BluetoothGattService(uuid, serviceType) -} -//endregion - -//region ToArgs -fun Int.toBluetoothLowEnergyStateArgs(): MyBluetoothLowEnergyStateArgs { - return when (this) { - BluetoothAdapter.STATE_OFF -> MyBluetoothLowEnergyStateArgs.OFF - BluetoothAdapter.STATE_TURNING_ON -> MyBluetoothLowEnergyStateArgs.TURNINGON - BluetoothAdapter.STATE_ON -> MyBluetoothLowEnergyStateArgs.ON - BluetoothAdapter.STATE_TURNING_OFF -> MyBluetoothLowEnergyStateArgs.TURNINGOFF - else -> MyBluetoothLowEnergyStateArgs.UNKNOWN - } -} - -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() ?: emptyArray() - 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/MyBluetoothLowEnergyManager.kt b/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothLowEnergyManager.kt deleted file mode 100644 index f46ecc2..0000000 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyBluetoothLowEnergyManager.kt +++ /dev/null @@ -1,106 +0,0 @@ -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.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(context: Context) { - companion object { - val CLIENT_CHARACTERISTIC_CONFIG_UUID = - UUID.fromString("00002902-0000-1000-8000-00805f9b34fb") as UUID - } - - private val mContext: Context - - private val mRequestPermissionsResultListener: RequestPermissionsResultListener by lazy { - MyRequestPermissionResultListener(this) - } - private val mBroadcastReceiver: BroadcastReceiver by lazy { - MyBroadcastReceiver(this) - } - - 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 hasFeature get() = mContext.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) - protected val manager - get() = ContextCompat.getSystemService( - mContext, BluetoothManager::class.java - ) as BluetoothManager - protected val adapter get() = manager.adapter as BluetoothAdapter - - protected fun checkPermissions(): Boolean { - return permissions.all { permission -> - ActivityCompat.checkSelfPermission( - mContext, permission - ) == PackageManager.PERMISSION_GRANTED - } - } - - protected fun requestPermissions() { - val activity = mBinding.activity - ActivityCompat.requestPermissions(activity, permissions, requestCode) - } - - fun onAttachedToActivity(binding: ActivityPluginBinding) { - binding.addRequestPermissionsResultListener(mRequestPermissionsResultListener) - mBinding = binding - } - - fun onDetachedFromActivity() { - mBinding.removeRequestPermissionsResultListener(mRequestPermissionsResultListener) - } - - fun onRequestPermissionsResult( - requestCode: Int, permissions: Array, results: IntArray - ): Boolean { - if (this.requestCode != requestCode) { - return false - } - val granted = - permissions.contentEquals(this.permissions) && results.all { r -> r == PackageManager.PERMISSION_GRANTED } - onPermissionsRequested(granted) - return true - } - - fun onReceive(context: Context, intent: Intent) { - val action = intent.action - if (action != BluetoothAdapter.ACTION_STATE_CHANGED) { - return - } - val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) - onAdapterStateChanged(state) - } - - protected fun registerReceiver() { - if (mRegistered) { - return - } - val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) - mContext.registerReceiver(mBroadcastReceiver, filter) - mRegistered = true - } - - abstract val permissions: Array - abstract val requestCode: Int - - abstract fun onPermissionsRequested(granted: Boolean) - abstract fun onAdapterStateChanged(state: Int) -} - 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 deleted file mode 100644 index d1b1daf..0000000 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyPeripheralManager.kt +++ /dev/null @@ -1,461 +0,0 @@ -package dev.yanshouwang.bluetooth_low_energy_android - -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.os.Build -import io.flutter.plugin.common.BinaryMessenger - -class MyPeripheralManager(context: Context, binaryMessenger: BinaryMessenger) : - MyBluetoothLowEnergyManager(context), MyPeripheralManagerHostApi { - companion object { - const val REQUEST_CODE = 445 - } - - private val advertiser get() = adapter.bluetoothLeAdvertiser - - private val mContext: Context - private val mApi: MyPeripheralManagerFlutterApi - - private val bluetoothGattServerCallback: BluetoothGattServerCallback by lazy { - MyBluetoothGattServerCallback(this, executor) - } - private val advertiseCallback: AdvertiseCallback by lazy { - MyAdvertiseCallback(this) - } - - 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 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 val requestCode: Int - get() = REQUEST_CODE - - override fun setUp(callback: (Result) -> Unit) { - try { - mClearState() - val stateArgs = if (hasFeature) { - val granted = checkPermissions() - if (granted) { - registerReceiver() - adapter.state.toBluetoothLowEnergyStateArgs() - } else { - requestPermissions() - mSetUpCallback = callback - return - } - } else MyBluetoothLowEnergyStateArgs.UNSUPPORTED - mOnStateChanged(stateArgs) - callback(Result.success(Unit)) - } catch (e: Throwable) { - callback(Result.failure(e)) - } - } - - override fun addService(serviceArgs: MyGattServiceArgs, callback: (Result) -> Unit) { - try { - val service = serviceArgs.toService() - val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull() - for (characteristicArgs in characteristicsArgs) { - val characteristic = characteristicArgs.toCharacteristic() - val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull() - for (descriptorArgs in descriptorsArgs) { - val descriptor = descriptorArgs.toDescriptor() - 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 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 serviceHashCodeArgs = serviceArgs.hashCodeArgs - val serviceHashCode = service.hashCode() - this.mServicesArgs[serviceHashCode] = serviceArgs - this.mServices[serviceHashCodeArgs] = service - val adding = mServer.addService(service) - if (!adding) { - throw IllegalStateException() - } - mAddServiceCallback = callback - } catch (e: Throwable) { - callback(Result.failure(e)) - } - } - - override fun removeService(hashCodeArgs: Long) { - val service = mServices.remove(hashCodeArgs) as BluetoothGattService - val removed = mServer.removeService(service) - if (!removed) { - throw IllegalStateException() - } - val hashCode = service.hashCode() - val serviceArgs = mServicesArgs.remove(hashCode) as MyGattServiceArgs - val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull() - for (characteristicArgs in characteristicsArgs) { - val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - val characteristic = - mCharacteristics.remove(characteristicHashCodeArgs) as BluetoothGattCharacteristic - val characteristicHashCode = characteristic.hashCode() - mCharacteristicsArgs.remove(characteristicHashCode) - val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull() - for (descriptorArgs in descriptorsArgs) { - val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - val descriptor = - mDescriptors.remove(descriptorHashCodeArgs) as BluetoothGattDescriptor - val descriptorHashCode = descriptor.hashCode() - mDescriptorsArgs.remove(descriptorHashCode) - } - } - } - - override fun clearServices() { - mServer.clearServices() - mServices.clear() - mCharacteristics.clear() - mDescriptors.clear() - - mServicesArgs.clear() - mCharacteristicsArgs.clear() - mDescriptorsArgs.clear() - } - - override fun startAdvertising( - advertisementArgs: MyAdvertisementArgs, callback: (Result) -> Unit - ) { - try { - val settings = AdvertiseSettings.Builder() - .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED).setConnectable(true) - .build() - val advertiseData = advertisementArgs.toAdvertiseData(adapter) - advertiser.startAdvertising(settings, advertiseData, advertiseCallback) - mStartAdvertisingCallback = callback - } catch (e: Throwable) { - callback(Result.failure(e)) - } - } - - override fun stopAdvertising() { - advertiser.stopAdvertising(advertiseCallback) - mAdvertising = false - } - - override fun sendResponse( - addressArgs: String, - idArgs: Long, - statusNumberArgs: Long, - offsetArgs: Long, - valueArgs: ByteArray? - ) { - val device = mDevices[addressArgs] as BluetoothDevice - val requestId = idArgs.toInt() - val statusRawArgs = statusNumberArgs.toInt() - val statusArgs = MyGattStatusArgs.ofRaw(statusRawArgs) ?: throw IllegalArgumentException() - val status = statusArgs.toStatus() - val offset = offsetArgs.toInt() - val sent = mServer.sendResponse(device, requestId, status, offset, valueArgs) - if (!sent) { - throw IllegalStateException("Send response failed.") - } - } - - override fun notifyCharacteristicChanged( - hashCodeArgs: Long, - valueArgs: ByteArray, - confirmArgs: Boolean, - addressArgs: String, - callback: (Result) -> Unit - ) { - try { - 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 = mServer.notifyCharacteristicChanged( - device, characteristic, confirmArgs, valueArgs - ) - statusCode == BluetoothStatusCodes.SUCCESS - } else { - // TODO: remove this when minSdkVersion >= 33 - characteristic.value = valueArgs - mServer.notifyCharacteristicChanged(device, characteristic, confirmArgs) - } - if (!notifying) { - throw IllegalStateException() - } - mNotifyCharacteristicValueChangedCallbacks[addressArgs] = callback - } catch (e: Throwable) { - callback(Result.failure(e)) - } - } - - override fun onPermissionsRequested(granted: Boolean) { - val callback = mSetUpCallback ?: return - val stateArgs = if (granted) { - registerReceiver() - adapter.state.toBluetoothLowEnergyStateArgs() - } else MyBluetoothLowEnergyStateArgs.UNAUTHORIZED - mOnStateChanged(stateArgs) - callback(Result.success(Unit)) - } - - override fun onAdapterStateChanged(state: Int) { - val stateArgs = state.toBluetoothLowEnergyStateArgs() - mOnStateChanged(stateArgs) - } - - fun onServiceAdded(status: Int, service: BluetoothGattService) { - val callback = mAddServiceCallback ?: return - mAddServiceCallback = null - if (status == BluetoothGatt.GATT_SUCCESS) { - callback(Result.success(Unit)) - } else { - val error = IllegalStateException("Read rssi failed with status: $status") - callback(Result.failure(error)) - } - } - - fun onStartSuccess(settingsInEffect: AdvertiseSettings) { - mAdvertising = true - val callback = mStartAdvertisingCallback ?: return - mStartAdvertisingCallback = null - callback(Result.success(Unit)) - } - - fun onStartFailure(errorCode: Int) { - 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 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 addressArgs = device.address - val mtuArgs = mtu.toLong() - mApi.onMtuChanged(addressArgs, mtuArgs) {} - } - - 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() - mApi.onCharacteristicReadRequest(addressArgs, hashCodeArgs, idArgs, offsetArgs) {} - } - - 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 addressArgs = device.address - val idArgs = requestId.toLong() - mApi.onExecuteWrite(addressArgs, idArgs, execute) {} - } - - fun onDescriptorReadRequest( - device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor - ) { - val addressArgs = device.address - val hashCode = descriptor.hashCode() - val descriptorArgs = mDescriptorsArgs[hashCode] ?: return - val hashCodeArgs = descriptorArgs.hashCodeArgs - val idArgs = requestId.toLong() - val offsetArgs = offset.toLong() - mApi.onDescriptorReadRequest(addressArgs, hashCodeArgs, idArgs, offsetArgs) {} - } - - fun onDescriptorWriteRequest( - device: BluetoothDevice, - requestId: Int, - descriptor: BluetoothGattDescriptor, - preparedWrite: Boolean, - responseNeeded: Boolean, - offset: Int, - value: ByteArray - ) { - val addressArgs = device.address - val hashCode = descriptor.hashCode() - val descriptorArgs = mDescriptorsArgs[hashCode] ?: return - val hashCodeArgs = descriptorArgs.hashCodeArgs - val idArgs = requestId.toLong() - val offsetArgs = offset.toLong() - mApi.onDescriptorWriteRequest( - addressArgs, hashCodeArgs, idArgs, offsetArgs, value, preparedWrite, responseNeeded - ) {} - if (descriptor.uuid == CLIENT_CHARACTERISTIC_CONFIG_UUID) { - val characteristic = descriptor.characteristic - mOnCharacteristicNotifyStateChanged(device, characteristic, value) - } - } - - fun onNotificationSent(device: BluetoothDevice, status: Int) { - 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") - 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 mOnStateChanged(stateArgs: MyBluetoothLowEnergyStateArgs) { - val stateNumberArgs = stateArgs.raw.toLong() - mApi.onStateChanged(stateNumberArgs) {} - // Renew GATT server when bluetooth adapter state changed. - when (stateArgs) { - MyBluetoothLowEnergyStateArgs.OFF -> mCloseServer() - MyBluetoothLowEnergyStateArgs.ON -> mOpenServer() - else -> {} - } - } - - private fun mOpenServer() { - if (mOpening) { - return - } - mServer = manager.openGattServer(mContext, bluetoothGattServerCallback) - mOpening = true - } - - private fun mCloseServer() { - if (!mOpening) { - return - } - mServer.close() - mOpening = false - } - - private fun mOnCharacteristicNotifyStateChanged( - device: BluetoothDevice, - characteristic: BluetoothGattCharacteristic, - value: ByteArray - ) { - val addressArgs = device.address - 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) {} - } -} \ 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 deleted file mode 100644 index a3350fe..0000000 --- a/bluetooth_low_energy_android/android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyRequestPermissionResultListener.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.yanshouwang.bluetooth_low_energy_android - -import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener - -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/test/kotlin/dev/hebei/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPluginTest.kt b/bluetooth_low_energy_android/android/src/test/kotlin/dev/hebei/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPluginTest.kt new file mode 100644 index 0000000..5a83703 --- /dev/null +++ b/bluetooth_low_energy_android/android/src/test/kotlin/dev/hebei/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPluginTest.kt @@ -0,0 +1,10 @@ +package dev.hebei.bluetooth_low_energy_android + +/* + * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. + * + * Once you have built the plugin's example app, you can run these tests from the command + * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or + * you can run them directly from IDEs that support JUnit such as Android Studio. + */ + diff --git a/bluetooth_low_energy_android/android/src/test/kotlin/dev/yanshouwang/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPluginTest.kt b/bluetooth_low_energy_android/android/src/test/kotlin/dev/yanshouwang/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPluginTest.kt deleted file mode 100644 index 131f8ca..0000000 --- a/bluetooth_low_energy_android/android/src/test/kotlin/dev/yanshouwang/bluetooth_low_energy_android/BluetoothLowEnergyAndroidPluginTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -package dev.yanshouwang.bluetooth_low_energy_android - -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import kotlin.test.Test -import org.mockito.Mockito - -/* - * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. - * - * Once you have built the plugin's example app, you can run these tests from the command - * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or - * you can run them directly from IDEs that support JUnit such as Android Studio. - */ - -internal class BluetoothLowEnergyAndroidPluginTest { - @Test - fun onMethodCall_getPlatformVersion_returnsExpectedValue() { - val plugin = BluetoothLowEnergyAndroidPlugin() - - val call = MethodCall("getPlatformVersion", null) - val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java) - plugin.onMethodCall(call, mockResult) - - Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE) - } -} diff --git a/bluetooth_low_energy_android/example/.gitignore b/bluetooth_low_energy_android/example/.gitignore index 24476c5..29a3a50 100644 --- a/bluetooth_low_energy_android/example/.gitignore +++ b/bluetooth_low_energy_android/example/.gitignore @@ -27,7 +27,6 @@ migrate_working_dir/ .dart_tool/ .flutter-plugins .flutter-plugins-dependencies -.packages .pub-cache/ .pub/ /build/ diff --git a/bluetooth_low_energy_android/example/README.md b/bluetooth_low_energy_android/example/README.md index 13063a6..db3ba9e 100644 --- a/bluetooth_low_energy_android/example/README.md +++ b/bluetooth_low_energy_android/example/README.md @@ -1,6 +1,6 @@ -# bluetooth_low_energy_example +# bluetooth_low_energy_android_example -Demonstrates how to use the bluetooth_low_energy plugin. +Demonstrates how to use the bluetooth_low_energy_android plugin. ## Getting Started diff --git a/bluetooth_low_energy_android/example/android/app/build.gradle b/bluetooth_low_energy_android/example/android/app/build.gradle index c5ff818..3948192 100644 --- a/bluetooth_low_energy_android/example/android/app/build.gradle +++ b/bluetooth_low_energy_android/example/android/app/build.gradle @@ -1,68 +1,58 @@ plugins { id "com.android.application" id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id "dev.flutter.flutter-gradle-plugin" } def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') +def localPropertiesFile = rootProject.file("local.properties") if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> + localPropertiesFile.withReader("UTF-8") { reader -> localProperties.load(reader) } } -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +def flutterVersionCode = localProperties.getProperty("flutter.versionCode") if (flutterVersionCode == null) { - flutterVersionCode = '1' + flutterVersionCode = "1" } -def flutterVersionName = localProperties.getProperty('flutter.versionName') +def flutterVersionName = localProperties.getProperty("flutter.versionName") if (flutterVersionName == null) { - flutterVersionName = '1.0' + flutterVersionName = "1.0" } android { - namespace "dev.yanshouwang.bluetooth_low_energy_example" - compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion + namespace = "dev.hebei.bluetooth_low_energy_android_example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "dev.yanshouwang.bluetooth_low_energy_example" + applicationId = "dev.hebei.bluetooth_low_energy_android_example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - // minSdkVersion flutter.minSdkVersion - minSdkVersion 21 - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutterVersionCode.toInteger() + versionName = flutterVersionName } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig = signingConfigs.debug } } } flutter { - source '../..' + source = "../.." } - -dependencies {} diff --git a/bluetooth_low_energy_android/example/android/app/src/main/AndroidManifest.xml b/bluetooth_low_energy_android/example/android/app/src/main/AndroidManifest.xml index bfe71e3..4989c50 100644 --- a/bluetooth_low_energy_android/example/android/app/src/main/AndroidManifest.xml +++ b/bluetooth_low_energy_android/example/android/app/src/main/AndroidManifest.xml @@ -1,12 +1,13 @@ + + + + + + + diff --git a/bluetooth_low_energy_android/example/android/app/src/main/kotlin/dev/hebei/bluetooth_low_energy_android_example/MainActivity.kt b/bluetooth_low_energy_android/example/android/app/src/main/kotlin/dev/hebei/bluetooth_low_energy_android_example/MainActivity.kt new file mode 100644 index 0000000..00b6246 --- /dev/null +++ b/bluetooth_low_energy_android/example/android/app/src/main/kotlin/dev/hebei/bluetooth_low_energy_android_example/MainActivity.kt @@ -0,0 +1,5 @@ +package dev.hebei.bluetooth_low_energy_android_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/bluetooth_low_energy_android/example/android/app/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_example/MainActivity.kt b/bluetooth_low_energy_android/example/android/app/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_example/MainActivity.kt deleted file mode 100644 index aa60869..0000000 --- a/bluetooth_low_energy_android/example/android/app/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_example/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.yanshouwang.bluetooth_low_energy_example - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/bluetooth_low_energy_android/example/android/build.gradle b/bluetooth_low_energy_android/example/android/build.gradle index f7eb7f6..d2ffbff 100644 --- a/bluetooth_low_energy_android/example/android/build.gradle +++ b/bluetooth_low_energy_android/example/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() @@ -18,12 +5,12 @@ allprojects { } } -rootProject.buildDir = '../build' +rootProject.buildDir = "../build" subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { - project.evaluationDependsOn(':app') + project.evaluationDependsOn(":app") } tasks.register("clean", Delete) { diff --git a/bluetooth_low_energy_android/example/android/gradle.properties b/bluetooth_low_energy_android/example/android/gradle.properties index 94adc3a..3b5b324 100644 --- a/bluetooth_low_energy_android/example/android/gradle.properties +++ b/bluetooth_low_energy_android/example/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true diff --git a/bluetooth_low_energy_android/example/android/gradle/wrapper/gradle-wrapper.properties b/bluetooth_low_energy_android/example/android/gradle/wrapper/gradle-wrapper.properties index 3c472b9..e1ca574 100644 --- a/bluetooth_low_energy_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/bluetooth_low_energy_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/bluetooth_low_energy_android/example/android/settings.gradle b/bluetooth_low_energy_android/example/android/settings.gradle index 55c4ca8..536165d 100644 --- a/bluetooth_low_energy_android/example/android/settings.gradle +++ b/bluetooth_low_energy_android/example/android/settings.gradle @@ -5,16 +5,21 @@ pluginManagement { def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" return flutterSdkPath - } - settings.ext.flutterSdkPath = flutterSdkPath() + }() - includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - plugins { - id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false + repositories { + google() + mavenCentral() + gradlePluginPortal() } } -include ":app" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} -apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle" +include ":app" diff --git a/bluetooth_low_energy_android/example/integration_test/plugin_integration_test.dart b/bluetooth_low_energy_android/example/integration_test/plugin_integration_test.dart index c03e5a8..d53d2ae 100644 --- a/bluetooth_low_energy_android/example/integration_test/plugin_integration_test.dart +++ b/bluetooth_low_energy_android/example/integration_test/plugin_integration_test.dart @@ -6,17 +6,8 @@ // For more information about Flutter integration tests, please see // https://docs.flutter.dev/cookbook/testing/integration/introduction -// import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - // testWidgets('getPlatformVersion test', (WidgetTester tester) async { - // final BluetoothLowEnergy plugin = BluetoothLowEnergy(); - // final String? version = await plugin.getPlatformVersion(); - // // The version string depends on the host platform running the test, so - // // just assert that some non-empty string is returned. - // expect(version?.isNotEmpty, true); - // }); } diff --git a/bluetooth_low_energy_android/example/lib/main.dart b/bluetooth_low_energy_android/example/lib/main.dart index 3cfddfa..a588ab4 100644 --- a/bluetooth_low_energy_android/example/lib/main.dart +++ b/bluetooth_low_energy_android/example/lib/main.dart @@ -1,12 +1,10 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:developer'; -import 'dart:typed_data'; -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:convert/convert.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; +import 'package:logging/logging.dart'; + +import 'router_config.dart'; void main() { runZonedGuarded(onStartUp, onCrashed); @@ -14,11 +12,7 @@ void main() { void onStartUp() async { Logger.root.onRecord.listen(onLogRecord); - // hierarchicalLoggingEnabled = true; - // CentralManager.instance.logLevel = Level.WARNING; - WidgetsFlutterBinding.ensureInitialized(); - await CentralManager.instance.setUp(); - await PeripheralManager.instance.setUp(); + hierarchicalLoggingEnabled = true; runApp(const MyApp()); } @@ -39,1124 +33,19 @@ void onLogRecord(LogRecord record) { ); } -class MyApp extends StatefulWidget { +class MyApp extends StatelessWidget { const MyApp({super.key}); - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData.light( - useMaterial3: true, - ).copyWith( + return MaterialApp.router( + routerConfig: routerConfig, + theme: ThemeData.light().copyWith( materialTapTargetSize: MaterialTapTargetSize.padded, ), - home: const HomeView(), - routes: { - 'peripheral': (context) { - final route = ModalRoute.of(context); - final eventArgs = route!.settings.arguments as DiscoveredEventArgs; - return PeripheralView( - eventArgs: eventArgs, - ); - }, - }, - ); - } -} - -class HomeView extends StatefulWidget { - 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(); - } -} - -class ScannerView extends StatefulWidget { - const ScannerView({super.key}); - - @override - State createState() => _ScannerViewState(); -} - -class _ScannerViewState extends State { - late final ValueNotifier state; - late final ValueNotifier discovering; - late final ValueNotifier> discoveredEventArgs; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription discoveredSubscription; - - @override - void initState() { - super.initState(); - state = ValueNotifier(BluetoothLowEnergyState.unknown); - discovering = ValueNotifier(false); - discoveredEventArgs = ValueNotifier([]); - stateChangedSubscription = CentralManager.instance.stateChanged.listen( - (eventArgs) { - state.value = eventArgs.state; - }, - ); - discoveredSubscription = CentralManager.instance.discovered.listen( - (eventArgs) { - final items = discoveredEventArgs.value; - final i = items.indexWhere( - (item) => item.peripheral == eventArgs.peripheral, - ); - if (i < 0) { - discoveredEventArgs.value = [...items, eventArgs]; - } else { - items[i] = eventArgs; - discoveredEventArgs.value = [...items]; - } - }, - ); - _initialize(); - } - - void _initialize() async { - state.value = await CentralManager.instance.getState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), - ); - } - - PreferredSizeWidget buildAppBar(BuildContext context) { - return AppBar( - title: const Text('Scanner'), - actions: [ - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: discovering, - builder: (context, discovering, child) { - return TextButton( - onPressed: state == BluetoothLowEnergyState.poweredOn - ? () async { - if (discovering) { - await stopDiscovery(); - } else { - await startDiscovery(); - } - } - : null, - child: Text( - discovering ? 'END' : 'BEGIN', - ), - ); - }, - ); - }, - ), - ], - ); - } - - Future startDiscovery() async { - discoveredEventArgs.value = []; - await CentralManager.instance.startDiscovery(); - discovering.value = true; - } - - Future stopDiscovery() async { - await CentralManager.instance.stopDiscovery(); - discovering.value = false; - } - - Widget buildBody(BuildContext context) { - return ValueListenableBuilder( - valueListenable: discoveredEventArgs, - builder: (context, discoveredEventArgs, child) { - // final items = discoveredEventArgs; - final items = discoveredEventArgs - .where((eventArgs) => eventArgs.advertisement.name != null) - .toList(); - return ListView.separated( - itemBuilder: (context, i) { - final theme = Theme.of(context); - final item = items[i]; - final uuid = item.peripheral.uuid; - final rssi = item.rssi; - final advertisement = item.advertisement; - final name = advertisement.name; - return ListTile( - onTap: () async { - final discovering = this.discovering.value; - if (discovering) { - await stopDiscovery(); - } - if (!mounted) { - throw UnimplementedError(); - } - await Navigator.of(context).pushNamed( - 'peripheral', - arguments: item, - ); - if (discovering) { - await startDiscovery(); - } - }, - onLongPress: () async { - await showModalBottomSheet( - context: context, - builder: (context) { - return BottomSheet( - onClosing: () {}, - clipBehavior: Clip.antiAlias, - builder: (context) { - final manufacturerSpecificData = - advertisement.manufacturerSpecificData; - return ListView.separated( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 40.0, - ), - itemBuilder: (context, i) { - const idWidth = 80.0; - if (i == 0) { - return const Row( - children: [ - SizedBox( - width: idWidth, - child: Text('ID'), - ), - Expanded( - child: Text('DATA'), - ), - ], - ); - } else { - final id = - '0x${manufacturerSpecificData!.id.toRadixString(16).padLeft(4, '0')}'; - final value = - hex.encode(manufacturerSpecificData.data); - return Row( - children: [ - SizedBox( - width: idWidth, - child: Text(id), - ), - Expanded( - child: Text(value), - ), - ], - ); - } - }, - separatorBuilder: (context, i) { - return const Divider(); - }, - itemCount: manufacturerSpecificData == null ? 1 : 2, - ); - }, - ); - }, - ); - }, - title: Text(name ?? 'N/A'), - subtitle: Text( - '$uuid', - style: theme.textTheme.bodySmall, - softWrap: false, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - RssiWidget(rssi), - Text('$rssi'), - ], - ), - ); - }, - separatorBuilder: (context, i) { - return const Divider( - height: 0.0, - ); - }, - itemCount: items.length, - ); - }, - ); - } - - @override - void dispose() { - super.dispose(); - stateChangedSubscription.cancel(); - discoveredSubscription.cancel(); - state.dispose(); - discovering.dispose(); - discoveredEventArgs.dispose(); - } -} - -class PeripheralView extends StatefulWidget { - final DiscoveredEventArgs eventArgs; - - const PeripheralView({ - super.key, - required this.eventArgs, - }); - - @override - State createState() => _PeripheralViewState(); -} - -class _PeripheralViewState extends 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> logs; - late final TextEditingController writeController; - late final StreamSubscription connectionStateChangedSubscription; - late final StreamSubscription characteristicNotifiedSubscription; - - @override - void initState() { - super.initState(); - eventArgs = widget.eventArgs; - connectionState = ValueNotifier(false); - services = ValueNotifier([]); - characteristics = ValueNotifier([]); - service = ValueNotifier(null); - characteristic = ValueNotifier(null); - writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); - logs = ValueNotifier([]); - writeController = TextEditingController(); - connectionStateChangedSubscription = - CentralManager.instance.connectionStateChanged.listen( - (eventArgs) { - if (eventArgs.peripheral != this.eventArgs.peripheral) { - return; - } - final connectionState = eventArgs.connectionState; - this.connectionState.value = connectionState; - if (!connectionState) { - services.value = []; - characteristics.value = []; - service.value = null; - characteristic.value = null; - logs.value = []; - } - }, - ); - characteristicNotifiedSubscription = - CentralManager.instance.characteristicNotified.listen( - (eventArgs) { - // final characteristic = this.characteristic.value; - // if (eventArgs.characteristic != characteristic) { - // return; - // } - const type = LogType.notify; - final log = Log(type, eventArgs.value); - logs.value = [ - ...logs.value, - log, - ]; - }, - ); - } - - @override - Widget build(BuildContext context) { - return PopScope( - onPopInvoked: (didPop) async { - if (connectionState.value) { - final peripheral = eventArgs.peripheral; - await CentralManager.instance.disconnect(peripheral); - } - }, - child: Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), + darkTheme: ThemeData.dark().copyWith( + materialTapTargetSize: MaterialTapTargetSize.padded, ), ); } - - PreferredSizeWidget buildAppBar(BuildContext context) { - final title = eventArgs.advertisement.name ?? ''; - return AppBar( - title: Text(title), - actions: [ - ValueListenableBuilder( - valueListenable: connectionState, - builder: (context, state, child) { - return TextButton( - onPressed: () async { - final peripheral = eventArgs.peripheral; - if (state) { - await CentralManager.instance.disconnect(peripheral); - } else { - await CentralManager.instance.connect(peripheral); - services.value = - await CentralManager.instance.discoverGATT(peripheral); - } - }, - child: Text(state ? 'DISCONNECT' : 'CONNECT'), - ); - }, - ), - ], - ); - } - - Widget buildBody(BuildContext context) { - final theme = Theme.of(context); - return Container( - margin: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ValueListenableBuilder( - valueListenable: services, - builder: (context, services, child) { - final items = services.map((service) { - return DropdownMenuItem( - value: service, - child: Text( - '${service.uuid}', - style: theme.textTheme.bodyMedium, - ), - ); - }).toList(); - return ValueListenableBuilder( - valueListenable: service, - builder: (context, service, child) { - return DropdownButton( - isExpanded: true, - items: items, - hint: const Text('CHOOSE A SERVICE'), - value: service, - onChanged: (service) async { - this.service.value = service; - characteristic.value = null; - if (service == null) { - return; - } - characteristics.value = service.characteristics; - }, - ); - }, - ); - }, - ), - ValueListenableBuilder( - valueListenable: characteristics, - builder: (context, characteristics, child) { - final items = characteristics.map((characteristic) { - return DropdownMenuItem( - value: characteristic, - child: Text( - '${characteristic.uuid}', - style: theme.textTheme.bodyMedium, - ), - ); - }).toList(); - return ValueListenableBuilder( - valueListenable: characteristic, - builder: (context, characteristic, child) { - return DropdownButton( - isExpanded: true, - items: items, - 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; - } - }, - ); - }, - ); - }, - ), - Expanded( - child: 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 = 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, - ); - }, - ), - ), - 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: [ - ElevatedButton( - onPressed: characteristic != null && canNotify - ? () async { - await CentralManager.instance - .setCharacteristicNotifyState( - characteristic, - state: true, - ); - } - : null, - child: const Text('NOTIFY'), - ), - const SizedBox(width: 8.0), - ElevatedButton( - onPressed: characteristic != null && canRead - ? () async { - final value = await CentralManager.instance - .readCharacteristic(characteristic); - const type = LogType.read; - final log = Log(type, value); - logs.value = [...logs.value, log]; - } - : null, - child: const Text('READ'), - ) - ], - ), - ), - 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), - ], - ); - }, - ), - ], - ), - ); - } - - @override - void dispose() { - super.dispose(); - connectionStateChangedSubscription.cancel(); - characteristicNotifiedSubscription.cancel(); - connectionState.dispose(); - services.dispose(); - characteristics.dispose(); - service.dispose(); - characteristic.dispose(); - writeType.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 characteristicReadSubscription; - late final StreamSubscription characteristicWrittenSubscription; - late final StreamSubscription characteristicNotifyStateChangedSubscription; - - @override - void initState() { - super.initState(); - state = ValueNotifier(BluetoothLowEnergyState.unknown); - advertising = ValueNotifier(false); - logs = ValueNotifier([]); - stateChangedSubscription = PeripheralManager.instance.stateChanged.listen( - (eventArgs) { - state.value = eventArgs.state; - }, - ); - characteristicReadSubscription = - PeripheralManager.instance.characteristicRead.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final value = eventArgs.value; - final log = Log( - LogType.read, - value, - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', - ); - logs.value = [ - ...logs.value, - log, - ]; - }, - ); - characteristicWrittenSubscription = - PeripheralManager.instance.characteristicWritten.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final value = eventArgs.value; - final log = Log( - LogType.write, - value, - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', - ); - logs.value = [ - ...logs.value, - log, - ]; - }, - ); - characteristicNotifyStateChangedSubscription = - PeripheralManager.instance.characteristicNotifyStateChanged.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final state = eventArgs.state; - final log = Log( - LogType.notify, - 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 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 - 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.instance.clearServices(); - final elements = List.generate(1000, (i) => i % 256); - final value = Uint8List.fromList(elements); - final service = GattService( - uuid: UUID.short(100), - characteristics: [ - GattCharacteristic( - uuid: UUID.short(200), - properties: [ - GattCharacteristicProperty.read, - ], - value: value, - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(201), - properties: [ - GattCharacteristicProperty.write, - GattCharacteristicProperty.writeWithoutResponse, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(202), - properties: [ - GattCharacteristicProperty.notify, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(203), - properties: [ - GattCharacteristicProperty.indicate, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(204), - properties: [ - GattCharacteristicProperty.read, - GattCharacteristicProperty.write, - GattCharacteristicProperty.writeWithoutResponse, - GattCharacteristicProperty.notify, - GattCharacteristicProperty.indicate, - ], - value: value, - descriptors: [], - ), - ], - ); - await PeripheralManager.instance.addService(service); - final advertisement = Advertisement( - name: 'le12138', - manufacturerSpecificData: ManufacturerSpecificData( - id: 0x2e19, - data: Uint8List.fromList([0x01, 0x02, 0x03]), - ), - ); - await PeripheralManager.instance.startAdvertising(advertisement); - advertising.value = true; - } - - Future stopAdvertising() async { - await PeripheralManager.instance.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(); - characteristicReadSubscription.cancel(); - characteristicWrittenSubscription.cancel(); - characteristicNotifyStateChangedSubscription.cancel(); - state.dispose(); - advertising.dispose(); - logs.dispose(); - } -} - -class Log { - final DateTime time; - final LogType type; - final Uint8List value; - final String? detail; - - Log( - this.type, - this.value, [ - this.detail, - ]) : time = DateTime.now(); - - @override - String toString() { - final type = this.type.toString().split('.').last; - final formatter = DateFormat.Hms(); - final time = formatter.format(this.time); - final message = hex.encode(value); - if (detail == null) { - return '[$type]$time: $message'; - } else { - return '[$type]$time: $message /* $detail */'; - } - } -} - -enum LogType { - read, - write, - notify, - error, -} - -class RssiWidget extends StatelessWidget { - final int rssi; - - const RssiWidget( - this.rssi, { - super.key, - }); - - @override - Widget build(BuildContext context) { - final IconData icon; - if (rssi > -70) { - icon = Icons.wifi_rounded; - } else if (rssi > -100) { - icon = Icons.wifi_2_bar_rounded; - } else { - icon = Icons.wifi_1_bar_rounded; - } - return Icon(icon); - } } diff --git a/bluetooth_low_energy_android/example/lib/models.dart b/bluetooth_low_energy_android/example/lib/models.dart new file mode 100644 index 0000000..627f4d2 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/models.dart @@ -0,0 +1 @@ +export 'models/log.dart'; diff --git a/bluetooth_low_energy_android/example/lib/models/log.dart b/bluetooth_low_energy_android/example/lib/models/log.dart new file mode 100644 index 0000000..2bcfbf7 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/models/log.dart @@ -0,0 +1,10 @@ +class Log { + final DateTime time; + final String type; + final String message; + + Log({ + required this.type, + required this.message, + }) : time = DateTime.now(); +} diff --git a/bluetooth_low_energy_android/example/lib/router_config.dart b/bluetooth_low_energy_android/example/lib/router_config.dart new file mode 100644 index 0000000..1d344c7 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/router_config.dart @@ -0,0 +1,85 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:collection/collection.dart'; +import 'package:go_router/go_router.dart'; + +import 'view_models.dart'; +import 'views.dart'; + +final routerConfig = GoRouter( + redirect: (context, state) { + if (state.matchedLocation == '/') { + return '/central'; + } + return null; + }, + routes: [ + StatefulShellRoute( + // builder: (context, state, navigationShell) { + // return HomeView(navigationShell: navigationShell); + // }, + builder: (context, state, navigationShell) => navigationShell, + navigatorContainerBuilder: (context, navigationShell, children) { + final navigators = children.mapIndexed( + (index, element) { + if (index == 0) { + return ViewModelBinding( + viewBuilder: (context) => element, + viewModelBuilder: (context) => CentralManagerViewModel(), + ); + } else { + return element; + } + }, + ).toList(); + return HomeView( + navigationShell: navigationShell, + navigators: navigators, + ); + }, + branches: [ + StatefulShellBranch( + routes: [ + GoRoute( + path: '/central', + builder: (context, state) { + return const CentralManagerView(); + }, + routes: [ + GoRoute( + path: ':uuid', + builder: (context, state) { + final uuidValue = state.pathParameters['uuid']!; + final uuid = UUID.fromString(uuidValue); + final viewModel = + ViewModel.of(context); + final eventArgs = viewModel.discoveries.firstWhere( + (discovery) => discovery.peripheral.uuid == uuid); + return ViewModelBinding( + viewBuilder: (context) => PeripheralView(), + viewModelBuilder: (context) => + PeripheralViewModel(eventArgs), + ); + }, + ), + ], + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/peripheral', + builder: (context, state) { + return ViewModelBinding( + viewBuilder: (context) => const PeripheralManagerView(), + viewModelBuilder: (context) => PeripheralManagerViewModel(), + ); + }, + ), + ], + ), + ], + ), + ], +); diff --git a/bluetooth_low_energy_android/example/lib/view_models.dart b/bluetooth_low_energy_android/example/lib/view_models.dart new file mode 100644 index 0000000..6c0c1fc --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/view_models.dart @@ -0,0 +1,6 @@ +export 'view_models/central_manager_view_model.dart'; +export 'view_models/peripheral_view_model.dart'; +export 'view_models/service_view_model.dart'; +export 'view_models/characteristic_view_model.dart'; +export 'view_models/descriptor_view_model.dart'; +export 'view_models/peripheral_manager_view_model.dart'; diff --git a/bluetooth_low_energy_android/example/lib/view_models/central_manager_view_model.dart b/bluetooth_low_energy_android/example/lib/view_models/central_manager_view_model.dart new file mode 100644 index 0000000..41a8411 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/view_models/central_manager_view_model.dart @@ -0,0 +1,74 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:logging/logging.dart'; + +class CentralManagerViewModel extends ViewModel { + final CentralManager _manager; + final List _discoveries; + bool _discovering; + + late final StreamSubscription _stateChangedSubscription; + late final StreamSubscription _discoveredSubscription; + + CentralManagerViewModel() + : _manager = CentralManager()..logLevel = Level.INFO, + _discoveries = [], + _discovering = false { + _stateChangedSubscription = _manager.stateChanged.listen((eventArgs) async { + if (eventArgs.state == BluetoothLowEnergyState.unauthorized) { + await _manager.authorize(); + } + notifyListeners(); + }); + _discoveredSubscription = _manager.discovered.listen((eventArgs) { + final peripheral = eventArgs.peripheral; + final index = _discoveries.indexWhere((i) => i.peripheral == peripheral); + if (index < 0) { + _discoveries.add(eventArgs); + } else { + _discoveries[index] = eventArgs; + } + notifyListeners(); + }); + } + + BluetoothLowEnergyState get state => _manager.state; + bool get discovering => _discovering; + List get discoveries => _discoveries; + + Future showAppSettings() async { + await _manager.showAppSettings(); + } + + Future startDiscovery({ + List? serviceUUIDs, + }) async { + if (_discovering) { + return; + } + _discoveries.clear(); + await _manager.startDiscovery( + serviceUUIDs: serviceUUIDs, + ); + _discovering = true; + notifyListeners(); + } + + Future stopDiscovery() async { + if (!_discovering) { + return; + } + await _manager.stopDiscovery(); + _discovering = false; + notifyListeners(); + } + + @override + void dispose() { + _stateChangedSubscription.cancel(); + _discoveredSubscription.cancel(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_android/example/lib/view_models/characteristic_view_model.dart b/bluetooth_low_energy_android/example/lib/view_models/characteristic_view_model.dart new file mode 100644 index 0000000..389e76a --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/view_models/characteristic_view_model.dart @@ -0,0 +1,145 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_android_example/models.dart'; +import 'package:clover/clover.dart'; + +import 'descriptor_view_model.dart'; + +class CharacteristicViewModel extends ViewModel { + final CentralManager _manager; + final Peripheral _peripheral; + final GATTCharacteristic _characteristic; + final List _descriptorViewModels; + final List _logs; + + GATTCharacteristicWriteType _writeType; + bool _notifyState; + + late final StreamSubscription _characteristicNotifiedSubscription; + + CharacteristicViewModel({ + required CentralManager manager, + required Peripheral peripheral, + required GATTCharacteristic characteristic, + }) : _manager = manager, + _peripheral = peripheral, + _characteristic = characteristic, + _descriptorViewModels = characteristic.descriptors + .map((descriptor) => DescriptorViewModel(descriptor)) + .toList(), + _logs = [], + _writeType = GATTCharacteristicWriteType.withResponse, + _notifyState = false { + if (!canWrite && canWriteWithoutResponse) { + _writeType = GATTCharacteristicWriteType.withoutResponse; + } + _characteristicNotifiedSubscription = + _manager.characteristicNotified.listen((eventArgs) { + if (eventArgs.characteristic != _characteristic) { + return; + } + final log = Log( + type: 'Notified', + message: '[${eventArgs.value.length}] ${eventArgs.value}', + ); + _logs.add(log); + notifyListeners(); + }); + } + + UUID get uuid => _characteristic.uuid; + bool get canRead => + _characteristic.properties.contains(GATTCharacteristicProperty.read); + bool get canWrite => + _characteristic.properties.contains(GATTCharacteristicProperty.write); + bool get canWriteWithoutResponse => _characteristic.properties + .contains(GATTCharacteristicProperty.writeWithoutResponse); + bool get canNotify => + _characteristic.properties.contains(GATTCharacteristicProperty.notify) || + _characteristic.properties.contains(GATTCharacteristicProperty.indicate); + List get descriptorViewModels => _descriptorViewModels; + List get logs => _logs; + GATTCharacteristicWriteType get writeType => _writeType; + bool get notifyState => _notifyState; + + Future read() async { + final value = await _manager.readCharacteristic( + _peripheral, + _characteristic, + ); + final log = Log( + type: 'Read', + message: '[${value.length}] $value', + ); + _logs.add(log); + notifyListeners(); + } + + void setWriteType(GATTCharacteristicWriteType type) { + if (type == GATTCharacteristicWriteType.withResponse && !canWrite) { + throw ArgumentError.value(type); + } + if (type == GATTCharacteristicWriteType.withoutResponse && + !canWriteWithoutResponse) { + throw ArgumentError.value(type); + } + _writeType = type; + notifyListeners(); + } + + Future write(Uint8List value) async { + // Fragments the value by maximumWriteLength. + final fragmentSize = await _manager.getMaximumWriteLength( + _peripheral, + type: writeType, + ); + var start = 0; + while (start < value.length) { + final end = start + fragmentSize; + final fragmentedValue = + end < value.length ? value.sublist(start, end) : value.sublist(start); + final type = writeType; + await _manager.writeCharacteristic( + _peripheral, + _characteristic, + value: fragmentedValue, + type: type, + ); + final log = Log( + type: type == GATTCharacteristicWriteType.withResponse + ? 'Write' + : 'Write without response', + message: '[${value.length}] $value', + ); + _logs.add(log); + notifyListeners(); + start = end; + } + } + + Future setNotifyState(bool state) async { + await _manager.setCharacteristicNotifyState( + _peripheral, + _characteristic, + state: state, + ); + _notifyState = state; + notifyListeners(); + } + + void clearLogs() { + _logs.clear(); + notifyListeners(); + } + + @override + void dispose() { + _characteristicNotifiedSubscription.cancel(); + for (var descriptorViewModel in descriptorViewModels) { + descriptorViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_android/example/lib/view_models/descriptor_view_model.dart b/bluetooth_low_energy_android/example/lib/view_models/descriptor_view_model.dart new file mode 100644 index 0000000..a03bce4 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/view_models/descriptor_view_model.dart @@ -0,0 +1,10 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; + +class DescriptorViewModel extends ViewModel { + final GATTDescriptor _descriptor; + + DescriptorViewModel(this._descriptor); + + UUID get uuid => _descriptor.uuid; +} diff --git a/bluetooth_low_energy_android/example/lib/view_models/peripheral_manager_view_model.dart b/bluetooth_low_energy_android/example/lib/view_models/peripheral_manager_view_model.dart new file mode 100644 index 0000000..e0fc489 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/view_models/peripheral_manager_view_model.dart @@ -0,0 +1,170 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_android_example/models.dart'; +import 'package:clover/clover.dart'; +import 'package:logging/logging.dart'; + +class PeripheralManagerViewModel extends ViewModel { + final PeripheralManager _manager; + final List _logs; + bool _advertising; + + late final StreamSubscription _stateChangedSubscription; + late final StreamSubscription _characteristicReadRequestedSubscription; + late final StreamSubscription _characteristicWriteRequestedSubscription; + late final StreamSubscription _characteristicNotifyStateChangedSubscription; + + PeripheralManagerViewModel() + : _manager = PeripheralManager()..logLevel = Level.INFO, + _logs = [], + _advertising = false { + _stateChangedSubscription = _manager.stateChanged.listen((eventArgs) async { + if (eventArgs.state == BluetoothLowEnergyState.unauthorized) { + await _manager.authorize(); + } + notifyListeners(); + }); + _characteristicReadRequestedSubscription = + _manager.characteristicReadRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final log = Log( + type: 'Characteristic read requested', + message: '${central.uuid}, ${characteristic.uuid}, $offset', + ); + _logs.add(log); + notifyListeners(); + final elements = List.generate(100, (i) => i % 256); + final value = Uint8List.fromList(elements); + final trimmedValue = value.sublist(offset); + await _manager.respondReadRequestWithValue( + request, + value: trimmedValue, + ); + }); + _characteristicWriteRequestedSubscription = + _manager.characteristicWriteRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final value = request.value; + final log = Log( + type: 'Characteristic write requested', + message: + '[${value.length}] ${central.uuid}, ${characteristic.uuid}, $offset, $value', + ); + _logs.add(log); + notifyListeners(); + await _manager.respondWriteRequest(request); + }); + _characteristicNotifyStateChangedSubscription = + _manager.characteristicNotifyStateChanged.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final state = eventArgs.state; + final log = Log( + type: 'Characteristic notify state changed', + message: '${central.uuid}, ${characteristic.uuid}, $state', + ); + _logs.add(log); + notifyListeners(); + // Write someting to the central when notify started. + if (state) { + final maximumNotifyLength = + await _manager.getMaximumNotifyLength(central); + final elements = List.generate(maximumNotifyLength, (i) => i % 256); + final value = Uint8List.fromList(elements); + await _manager.notifyCharacteristic( + central, + characteristic, + value: value, + ); + } + }); + } + + BluetoothLowEnergyState get state => _manager.state; + bool get advertising => _advertising; + List get logs => _logs; + + Future showAppSettings() async { + await _manager.showAppSettings(); + } + + Future startAdvertising() async { + if (_advertising) { + return; + } + await _manager.removeAllServices(); + final elements = List.generate(100, (i) => i % 256); + final value = Uint8List.fromList(elements); + final service = GATTService( + uuid: UUID.short(100), + isPrimary: true, + includedServices: [], + characteristics: [ + GATTCharacteristic.immutable( + uuid: UUID.short(200), + value: value, + descriptors: [], + ), + GATTCharacteristic.mutable( + uuid: UUID.short(201), + properties: [ + GATTCharacteristicProperty.read, + GATTCharacteristicProperty.write, + GATTCharacteristicProperty.writeWithoutResponse, + GATTCharacteristicProperty.notify, + GATTCharacteristicProperty.indicate, + ], + permissions: [ + GATTCharacteristicPermission.read, + GATTCharacteristicPermission.write, + ], + descriptors: [], + ), + ], + ); + await _manager.addService(service); + final advertisement = Advertisement( + name: 'BLE-12138', + manufacturerSpecificData: [ + ManufacturerSpecificData( + id: 0x2e19, + data: Uint8List.fromList([0x01, 0x02, 0x03]), + ) + ], + ); + await _manager.startAdvertising(advertisement); + _advertising = true; + notifyListeners(); + } + + Future stopAdvertising() async { + if (!_advertising) { + return; + } + await _manager.stopAdvertising(); + _advertising = false; + notifyListeners(); + } + + void clearLogs() { + _logs.clear(); + notifyListeners(); + } + + @override + void dispose() { + _stateChangedSubscription.cancel(); + _characteristicReadRequestedSubscription.cancel(); + _characteristicWriteRequestedSubscription.cancel(); + _characteristicNotifyStateChangedSubscription.cancel(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_android/example/lib/view_models/peripheral_view_model.dart b/bluetooth_low_energy_android/example/lib/view_models/peripheral_view_model.dart new file mode 100644 index 0000000..52c47e0 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/view_models/peripheral_view_model.dart @@ -0,0 +1,83 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:hybrid_logging/hybrid_logging.dart'; + +import 'service_view_model.dart'; + +class PeripheralViewModel extends ViewModel with TypeLogger { + final CentralManager _manager; + final Peripheral _peripheral; + final String? _name; + bool _connected; + List _serviceViewModels; + + late final StreamSubscription _connectionStateChangedSubscription; + late final StreamSubscription _mtuChangedChangedSubscription; + + PeripheralViewModel(DiscoveredEventArgs eventArgs) + : _manager = CentralManager(), + _peripheral = eventArgs.peripheral, + _name = eventArgs.advertisement.name, + _connected = false, + _serviceViewModels = [] { + _connectionStateChangedSubscription = + _manager.connectionStateChanged.listen((eventArgs) { + if (eventArgs.peripheral != _peripheral) { + return; + } + if (eventArgs.state == ConnectionState.connected) { + _connected = true; + } else { + _connected = false; + _serviceViewModels = []; + } + notifyListeners(); + }); + _mtuChangedChangedSubscription = _manager.mtuChanged.listen((eventArgs) { + if (eventArgs.peripheral != _peripheral) { + return; + } + logger.info('MTU chanaged: ${eventArgs.mtu}'); + }); + } + + UUID get uuid => _peripheral.uuid; + String? get name => _name; + bool get connected => _connected; + List get serviceViewModels => _serviceViewModels; + + Future connect() async { + await _manager.connect(_peripheral); + } + + Future disconnect() async { + await _manager.disconnect(_peripheral); + } + + Future discoverGATT() async { + final services = await _manager.discoverGATT(_peripheral); + _serviceViewModels = services + .map((service) => ServiceViewModel( + manager: _manager, + peripheral: _peripheral, + service: service, + )) + .toList(); + notifyListeners(); + } + + @override + void dispose() { + if (connected) { + disconnect(); + } + _connectionStateChangedSubscription.cancel(); + _mtuChangedChangedSubscription.cancel(); + for (var serviceViewModel in serviceViewModels) { + serviceViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_android/example/lib/view_models/service_view_model.dart b/bluetooth_low_energy_android/example/lib/view_models/service_view_model.dart new file mode 100644 index 0000000..0dfd888 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/view_models/service_view_model.dart @@ -0,0 +1,46 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; + +import 'characteristic_view_model.dart'; + +class ServiceViewModel extends ViewModel { + final GATTService _service; + + final List _includedServiceViewModels; + final List _characteristicViewModels; + + ServiceViewModel({ + required CentralManager manager, + required Peripheral peripheral, + required GATTService service, + }) : _service = service, + _includedServiceViewModels = service.includedServices + .map((service) => ServiceViewModel( + manager: manager, + peripheral: peripheral, + service: service, + )) + .toList(), + _characteristicViewModels = service.characteristics + .map((characteristic) => CharacteristicViewModel( + manager: manager, + peripheral: peripheral, + characteristic: characteristic, + )) + .toList(); + + UUID get uuid => _service.uuid; + bool get isPrimary => _service.isPrimary; + List get includedServiceViewModels => + _includedServiceViewModels; + List get characteristicViewModels => + _characteristicViewModels; + + @override + void dispose() { + for (var characteristicViewModel in characteristicViewModels) { + characteristicViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_android/example/lib/views.dart b/bluetooth_low_energy_android/example/lib/views.dart new file mode 100644 index 0000000..b0727cb --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/views.dart @@ -0,0 +1,4 @@ +export 'views/home_view.dart'; +export 'views/central_manager_view.dart'; +export 'views/peripheral_manager_view.dart'; +export 'views/peripheral_view.dart'; diff --git a/bluetooth_low_energy_android/example/lib/views/advertisement_view.dart b/bluetooth_low_energy_android/example/lib/views/advertisement_view.dart new file mode 100644 index 0000000..2082064 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/views/advertisement_view.dart @@ -0,0 +1,69 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:convert/convert.dart'; +import 'package:flutter/material.dart'; + +class AdvertisementView extends StatelessWidget { + final Advertisement advertisement; + + const AdvertisementView({ + super.key, + required this.advertisement, + }); + + @override + Widget build(BuildContext context) { + final manufacturerSpecificData = advertisement.manufacturerSpecificData; + return LayoutBuilder( + builder: (context, constraints) { + const idWidth = 100.0; + final valueWidth = constraints.maxWidth - idWidth - 16.0 * 2.0; + return DataTable( + columnSpacing: 0.0, + horizontalMargin: 16.0, + columns: [ + DataColumn( + label: Container( + width: idWidth, + alignment: Alignment.center, + child: const Text('Id'), + ), + ), + DataColumn( + label: Container( + width: valueWidth, + alignment: Alignment.center, + child: const Text('Value'), + ), + ), + ], + rows: manufacturerSpecificData.map((item) { + final id = '0x${item.id.toRadixString(16).padLeft(4, '0')}'; + final value = hex.encode(item.data); + return DataRow( + cells: [ + DataCell( + Container( + width: idWidth, + alignment: Alignment.center, + child: Text(id), + ), + ), + DataCell( + Container( + width: valueWidth, + alignment: Alignment.center, + child: Text( + value, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ); + }).toList(), + ); + }, + ); + } +} diff --git a/bluetooth_low_energy_android/example/lib/views/central_manager_view.dart b/bluetooth_low_energy_android/example/lib/views/central_manager_view.dart new file mode 100644 index 0000000..7d15ddb --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/views/central_manager_view.dart @@ -0,0 +1,124 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_android_example/view_models.dart'; +import 'package:bluetooth_low_energy_android_example/widgets.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import 'advertisement_view.dart'; + +class CentralManagerView extends StatelessWidget { + const CentralManagerView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final discovering = viewModel.discovering; + return Scaffold( + appBar: AppBar( + title: const Text('Central Manager'), + actions: [ + TextButton( + onPressed: state == BluetoothLowEnergyState.poweredOn + ? () async { + if (discovering) { + await viewModel.stopDiscovery(); + } else { + await viewModel.startDiscovery(); + } + } + : null, + child: Text(discovering ? 'END' : 'BEGIN'), + ), + ], + ), + body: buildBody(context), + ); + } + + Widget buildBody(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + if (state == BluetoothLowEnergyState.unauthorized) { + return Center( + child: TextButton( + onPressed: () => viewModel.showAppSettings(), + child: const Text('Go to settings'), + ), + ); + } else if (state == BluetoothLowEnergyState.poweredOn) { + final discoveries = viewModel.discoveries; + return ListView.separated( + itemBuilder: (context, index) { + final theme = Theme.of(context); + final discovery = discoveries[index]; + final uuid = discovery.peripheral.uuid; + final name = discovery.advertisement.name; + final rssi = discovery.rssi; + return ListTile( + onTap: () { + onTapDissovery(context, discovery); + }, + onLongPress: () { + onLongPressDiscovery(context, discovery); + }, + title: Text(name ?? ''), + subtitle: Text( + '$uuid', + style: theme.textTheme.bodySmall, + softWrap: false, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RSSIIndicator(rssi), + Text('$rssi'), + ], + ), + ); + }, + separatorBuilder: (context, i) { + return const Divider( + height: 0.0, + ); + }, + itemCount: discoveries.length, + ); + } else { + return Center( + child: Text( + '$state', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + } + + void onTapDissovery( + BuildContext context, DiscoveredEventArgs discovery) async { + final viewModel = ViewModel.of(context); + if (viewModel.discovering) { + await viewModel.stopDiscovery(); + if (!context.mounted) { + return; + } + } + final uuid = discovery.peripheral.uuid; + context.go('/central/$uuid'); + } + + void onLongPressDiscovery( + BuildContext context, DiscoveredEventArgs discovery) async { + await showModalBottomSheet( + context: context, + builder: (context) { + return AdvertisementView( + advertisement: discovery.advertisement, + ); + }, + ); + } +} diff --git a/bluetooth_low_energy_android/example/lib/views/characteristic_tree_node_view.dart b/bluetooth_low_energy_android/example/lib/views/characteristic_tree_node_view.dart new file mode 100644 index 0000000..c57d14a --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/views/characteristic_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_android_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class CharacteristicTreeNodeView extends StatelessWidget { + const CharacteristicTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ); + } +} diff --git a/bluetooth_low_energy_android/example/lib/views/characteristic_view.dart b/bluetooth_low_energy_android/example/lib/views/characteristic_view.dart new file mode 100644 index 0000000..c83f9a3 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/views/characteristic_view.dart @@ -0,0 +1,178 @@ +import 'dart:convert'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_android_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +import 'log_view.dart'; + +class CharacteristicView extends StatefulWidget { + const CharacteristicView({super.key}); + + @override + State createState() => _CharacteristicViewState(); +} + +class _CharacteristicViewState extends State { + late final TextEditingController _textController; + + @override + void initState() { + super.initState(); + _textController = TextEditingController(); + } + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final logs = viewModel.logs; + return Column( + children: [ + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + return OverflowBox( + alignment: Alignment.topCenter, + maxHeight: constraints.maxHeight + 1.0, + child: Row( + children: [ + Expanded( + child: InputDecorator( + decoration: InputDecoration( + contentPadding: const EdgeInsets.all(12.0), + border: OutlineInputBorder( + borderRadius: BorderRadius.vertical( + top: const Radius.circular(12.0), + bottom: viewModel.canWrite || + viewModel.canWriteWithoutResponse + ? Radius.zero + : const Radius.circular(12.0), + ), + ), + ), + child: ListView.builder( + itemBuilder: (context, i) { + final log = logs[i]; + return LogView( + log: log, + ); + }, + itemCount: logs.length, + ), + ), + ), + Column( + children: [ + Visibility( + visible: viewModel.canNotify, + child: viewModel.notifyState + ? IconButton.filled( + onPressed: () async { + await viewModel.setNotifyState(false); + }, + icon: + const Icon(Symbols.notifications_active), + ) + : IconButton.filledTonal( + onPressed: () async { + await viewModel.setNotifyState(true); + }, + icon: const Icon(Symbols.notifications_off), + ), + ), + Visibility( + visible: viewModel.canRead, + child: IconButton.filled( + onPressed: () async { + await viewModel.read(); + }, + icon: const Icon(Symbols.arrow_downward), + ), + ), + Visibility( + visible: viewModel.canWrite || + viewModel.canWriteWithoutResponse, + child: viewModel.writeType == + GATTCharacteristicWriteType.withResponse + ? IconButton.filled( + onPressed: viewModel.canWriteWithoutResponse + ? () { + viewModel.setWriteType( + GATTCharacteristicWriteType + .withoutResponse); + } + : null, + icon: const Icon(Symbols.swap_vert), + ) + : IconButton.filledTonal( + onPressed: viewModel.canWrite + ? () { + viewModel.setWriteType( + GATTCharacteristicWriteType + .withResponse); + } + : null, + icon: const Icon(Symbols.arrow_upward), + ), + ), + IconButton.filled( + onPressed: () => viewModel.clearLogs(), + icon: const Icon(Symbols.delete), + ), + ], + ), + ], + ), + ); + }, + ), + ), + Visibility( + visible: viewModel.canWrite || viewModel.canWriteWithoutResponse, + child: Row( + children: [ + Expanded( + child: TextField( + controller: _textController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.symmetric( + horizontal: 12.0, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12.0), + ), + ), + ), + ), + ), + ValueListenableBuilder( + valueListenable: _textController, + builder: (context, tev, child) { + final text = tev.text; + return IconButton.filled( + onPressed: text.isEmpty + ? null + : () async { + final value = utf8.encode(text); + await viewModel.write(value); + }, + icon: const Icon(Symbols.pets), + ); + }, + ), + ], + ), + ), + ], + ); + } + + @override + void dispose() { + _textController.dispose(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_android/example/lib/views/descriptor_tree_node_view.dart b/bluetooth_low_energy_android/example/lib/views/descriptor_tree_node_view.dart new file mode 100644 index 0000000..7a8cd83 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/views/descriptor_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_android_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class DescriptorTreeNodeView extends StatelessWidget { + const DescriptorTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.tertiary, + ), + ); + } +} diff --git a/bluetooth_low_energy_android/example/lib/views/home_view.dart b/bluetooth_low_energy_android/example/lib/views/home_view.dart new file mode 100644 index 0000000..9b7d146 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/views/home_view.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class HomeView extends StatefulWidget { + final StatefulNavigationShell navigationShell; + final List navigators; + + const HomeView({ + super.key, + required this.navigationShell, + required this.navigators, + }); + + @override + State createState() => _HomeViewState(); +} + +class _HomeViewState extends State { + late final PageController _controller; + + @override + void initState() { + super.initState(); + _controller = PageController( + initialPage: widget.navigationShell.currentIndex, + ); + } + + @override + Widget build(BuildContext context) { + final navigationShell = widget.navigationShell; + final navigators = widget.navigators; + return Scaffold( + // body: navigationShell, + body: PageView.builder( + controller: _controller, + onPageChanged: (index) { + // Ignore tap events. + if (index == navigationShell.currentIndex) { + return; + } + navigationShell.goBranch( + index, + initialLocation: false, + ); + }, + itemBuilder: (context, index) { + return navigators[index]; + }, + itemCount: navigators.length, + ), + bottomNavigationBar: BottomNavigationBar( + onTap: (index) { + navigationShell.goBranch( + index, + initialLocation: index == navigationShell.currentIndex, + ); + }, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.radar), + label: 'Central', + ), + BottomNavigationBarItem( + icon: Icon(Icons.sensors), + label: 'Peripheral', + ), + ], + currentIndex: widget.navigationShell.currentIndex, + ), + ); + } + + @override + void didUpdateWidget(covariant HomeView oldWidget) { + super.didUpdateWidget(oldWidget); + final navigationShell = widget.navigationShell; + final page = _controller.page ?? _controller.initialPage; + final index = page.round(); + // Ignore swipe events. + if (index == navigationShell.currentIndex) { + return; + } + _controller.animateToPage( + navigationShell.currentIndex, + duration: const Duration(milliseconds: 300), + curve: Curves.linear, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_android/example/lib/views/log_view.dart b/bluetooth_low_energy_android/example/lib/views/log_view.dart new file mode 100644 index 0000000..010272c --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/views/log_view.dart @@ -0,0 +1,43 @@ +import 'package:bluetooth_low_energy_android_example/models.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class LogView extends StatelessWidget { + final Log log; + + const LogView({ + super.key, + required this.log, + }); + + @override + Widget build(BuildContext context) { + final formatter = DateFormat.Hms(); + final time = formatter.format(log.time); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich( + TextSpan( + text: '[$time] ', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.error, + ), + children: [ + TextSpan( + text: log.type, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + )), + ], + ), + ), + Text( + log.message, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ); + } +} diff --git a/bluetooth_low_energy_android/example/lib/views/peripheral_manager_view.dart b/bluetooth_low_energy_android/example/lib/views/peripheral_manager_view.dart new file mode 100644 index 0000000..b1a0cb5 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/views/peripheral_manager_view.dart @@ -0,0 +1,76 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_android_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +import 'log_view.dart'; + +class PeripheralManagerView extends StatelessWidget { + const PeripheralManagerView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final advertising = viewModel.advertising; + return Scaffold( + appBar: AppBar( + title: const Text('Peripheral Manager'), + actions: [ + TextButton( + onPressed: state == BluetoothLowEnergyState.poweredOn + ? () async { + if (advertising) { + await viewModel.stopAdvertising(); + } else { + await viewModel.startAdvertising(); + } + } + : null, + child: Text(advertising ? 'END' : 'BEGIN'), + ), + ], + ), + body: buildBody(context), + floatingActionButton: state == BluetoothLowEnergyState.poweredOn + ? FloatingActionButton( + onPressed: () => viewModel.clearLogs(), + child: const Icon(Symbols.delete), + ) + : null, + ); + } + + Widget buildBody(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + if (state == BluetoothLowEnergyState.unauthorized) { + return Center( + child: TextButton( + onPressed: () => viewModel.showAppSettings(), + child: const Text('Go to settings'), + ), + ); + } else if (state == BluetoothLowEnergyState.poweredOn) { + final logs = viewModel.logs; + return ListView.builder( + padding: const EdgeInsets.all(16.0), + itemBuilder: (context, i) { + final log = logs[i]; + return LogView( + log: log, + ); + }, + itemCount: logs.length, + ); + } else { + return Center( + child: Text( + '$state', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + } +} diff --git a/bluetooth_low_energy_android/example/lib/views/peripheral_view.dart b/bluetooth_low_energy_android/example/lib/views/peripheral_view.dart new file mode 100644 index 0000000..5e90fc8 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/views/peripheral_view.dart @@ -0,0 +1,107 @@ +import 'package:bluetooth_low_energy_android_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_simple_treeview/flutter_simple_treeview.dart'; + +import 'characteristic_tree_node_view.dart'; +import 'characteristic_view.dart'; +import 'descriptor_tree_node_view.dart'; +import 'service_tree_node_view.dart'; + +class PeripheralView extends StatelessWidget { + final TreeController _treeController; + + PeripheralView({super.key}) + : _treeController = TreeController( + allNodesExpanded: false, + ); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final connected = viewModel.connected; + final serviceViewModels = viewModel.serviceViewModels; + return Scaffold( + appBar: AppBar( + title: Text(viewModel.name ?? '${viewModel.uuid}'), + actions: [ + TextButton( + onPressed: () async { + if (connected) { + await viewModel.disconnect(); + } else { + await viewModel.connect(); + await viewModel.discoverGATT(); + } + }, + child: Text(connected ? 'DISCONNECT' : 'CONNECT'), + ), + ], + ), + body: SingleChildScrollView( + child: TreeView( + treeController: _treeController, + indent: 0.0, + nodes: _buildServiceTreeNodes(serviceViewModels), + ), + ), + ); + } + + List _buildServiceTreeNodes(List viewModels) { + return viewModels.map((viewModel) { + final includedServiceViewModels = viewModel.includedServiceViewModels; + final characteristicViewModels = viewModel.characteristicViewModels; + return TreeNode( + children: [ + ..._buildServiceTreeNodes(includedServiceViewModels), + ..._buildCharacteristicTreeNodes(characteristicViewModels), + ], + content: InheritedViewModel( + view: const ServiceTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } + + List _buildCharacteristicTreeNodes( + List viewModels) { + return viewModels.map((viewModel) { + final descriptorViewModels = viewModel.descriptorViewModels; + return TreeNode( + children: [ + TreeNode( + content: Expanded( + child: Container( + margin: const EdgeInsets.only(right: 40.0), + height: 360.0, + child: InheritedViewModel( + view: const CharacteristicView(), + viewModel: viewModel, + ), + ), + ), + ), + ..._buildDescriptorTreeNodes(descriptorViewModels), + ], + content: InheritedViewModel( + view: const CharacteristicTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } + + List _buildDescriptorTreeNodes( + List viewModels) { + return viewModels.map((viewModel) { + return TreeNode( + content: InheritedViewModel( + view: const DescriptorTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } +} diff --git a/bluetooth_low_energy_android/example/lib/views/service_tree_node_view.dart b/bluetooth_low_energy_android/example/lib/views/service_tree_node_view.dart new file mode 100644 index 0000000..bd32c5f --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/views/service_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_android_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class ServiceTreeNodeView extends StatelessWidget { + const ServiceTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ); + } +} diff --git a/bluetooth_low_energy_android/example/lib/widgets.dart b/bluetooth_low_energy_android/example/lib/widgets.dart new file mode 100644 index 0000000..baccf61 --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/widgets.dart @@ -0,0 +1 @@ +export 'widgets/rssi_indicator.dart'; diff --git a/bluetooth_low_energy_android/example/lib/widgets/rssi_indicator.dart b/bluetooth_low_energy_android/example/lib/widgets/rssi_indicator.dart new file mode 100644 index 0000000..ddd59df --- /dev/null +++ b/bluetooth_low_energy_android/example/lib/widgets/rssi_indicator.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class RSSIIndicator extends StatelessWidget { + final int rssi; + + const RSSIIndicator( + this.rssi, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final IconData icon; + if (rssi > -70) { + icon = Icons.wifi_rounded; + } else if (rssi > -100) { + icon = Icons.wifi_2_bar_rounded; + } else { + icon = Icons.wifi_1_bar_rounded; + } + return Icon(icon); + } +} diff --git a/bluetooth_low_energy_android/example/pubspec.lock b/bluetooth_low_energy_android/example/pubspec.lock index d519fab..a0a49ea 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: "5.0.4" + version: "6.0.0" bluetooth_low_energy_platform_interface: dependency: "direct main" description: name: bluetooth_low_energy_platform_interface - sha256: "5af74eb8f97a896dfdbcff2da284d4fe5b4e2e49ebb2f46f826ac1810f66e4d7" + sha256: bc2e8d97c141653e5747bcb3cdc9fe956541b6ecc6e5f158b99a2f3abc2d946a url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "6.0.0" boolean_selector: dependency: transitive description: @@ -48,8 +48,16 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + clover: + dependency: "direct main" + description: + name: clover + sha256: eba28e69b32f174a51c093eef098cf5ae646470322727081d5d3d8f66c786487 + url: "https://pub.dev" + source: hosted + version: "3.0.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a @@ -68,10 +76,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" fake_async: dependency: transitive description: @@ -102,7 +110,15 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + flutter_simple_treeview: + dependency: "direct main" + description: + name: flutter_simple_treeview + sha256: ad4978d2668dd078d3a09966832da111bef9102dd636e572c50c80133b7ff4d9 url: "https://pub.dev" source: hosted version: "3.0.2" @@ -111,11 +127,32 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter source: sdk version: "0.0.0" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "6ad5662b014c06c20fa46ab78715c96b2222a7fe4f87bf77e0289592c2539e86" + url: "https://pub.dev" + source: hosted + version: "14.1.3" + hybrid_logging: + dependency: "direct main" + description: + name: hybrid_logging + sha256: "54248d52ce68c14702a42fbc4083bac5c6be30f6afad8a41be4bbadd197b8af5" + url: "https://pub.dev" + source: hosted + version: "1.0.0" integration_test: dependency: "direct dev" description: flutter @@ -133,44 +170,36 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" - log_service: - dependency: transitive - description: - name: log_service - sha256: "21124936899e227d1779268077921d46d57456e2592d1562e455be273594e2e4" - url: "https://pub.dev" - source: hosted - version: "1.0.0" + version: "4.0.0" logging: - dependency: transitive + dependency: "direct main" description: name: logging sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" @@ -193,14 +222,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.0" + material_symbols_icons: + dependency: "direct main" + description: + name: material_symbols_icons + sha256: b2d3cbc3c42b8a217715b0d97ff03aebb14b2b4592875736e5599c603fb2db7e + url: "https://pub.dev" + source: hosted + version: "4.2758.0" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" path: dependency: transitive description: @@ -290,10 +327,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -314,10 +351,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" webdriver: dependency: transitive description: @@ -327,5 +364,5 @@ packages: source: hosted version: "3.0.3" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.0.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/bluetooth_low_energy_android/example/pubspec.yaml b/bluetooth_low_energy_android/example/pubspec.yaml index 28077ca..92cb864 100644 --- a/bluetooth_low_energy_android/example/pubspec.yaml +++ b/bluetooth_low_energy_android/example/pubspec.yaml @@ -1,5 +1,5 @@ -name: bluetooth_low_energy_example -description: Demonstrates how to use the bluetooth_low_energy plugin. +name: bluetooth_low_energy_android_example +description: "Demonstrates how to use the bluetooth_low_energy_android plugin." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev @@ -17,10 +17,10 @@ dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^5.0.0 + bluetooth_low_energy_platform_interface: ^6.0.0 bluetooth_low_energy_android: # When depending on this package from a real application you should use: - # bluetooth_low_energy: ^x.y.z + # bluetooth_low_energy_android: ^x.y.z # See https://dart.dev/tools/pub/dependencies#version-constraints # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. @@ -28,9 +28,16 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - convert: ^3.1.1 + cupertino_icons: ^1.0.6 + clover: ^3.0.0 + go_router: ^14.1.3 + logging: ^1.2.0 + hybrid_logging: ^1.0.0 intl: ^0.19.0 + collection: ^1.18.0 + convert: ^3.1.1 + flutter_simple_treeview: ^3.0.2 + material_symbols_icons: ^4.2744.0 dev_dependencies: integration_test: @@ -43,7 +50,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: ^3.0.0 + flutter_lints: ^4.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_android/example/test/widget_test.dart b/bluetooth_low_energy_android/example/test/widget_test.dart index b15e87d..2a2b819 100644 --- a/bluetooth_low_energy_android/example/test/widget_test.dart +++ b/bluetooth_low_energy_android/example/test/widget_test.dart @@ -5,23 +5,4 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:bluetooth_low_energy_example/main.dart'; - -void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); -} +void main() {} 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 9984ca1..ce7e77f 100644 --- a/bluetooth_low_energy_android/lib/bluetooth_low_energy_android.dart +++ b/bluetooth_low_energy_android/lib/bluetooth_low_energy_android.dart @@ -3,9 +3,9 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla import 'src/my_central_manager.dart'; import 'src/my_peripheral_manager.dart'; -abstract class BluetoothLowEnergyAndroid { +abstract class BluetoothLowEnergyAndroidPlugin { static void registerWith() { - CentralManager.instance = MyCentralManager(); - PeripheralManager.instance = MyPeripheralManager(); + PlatformCentralManager.instance = MyCentralManager(); + PlatformPeripheralManager.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 ebd30ab..c9ec874 100644 --- a/bluetooth_low_energy_android/lib/src/my_api.dart +++ b/bluetooth_low_energy_android/lib/src/my_api.dart @@ -3,15 +3,11 @@ 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'; +import 'my_central.dart'; +import 'my_gatt.dart'; +import 'my_peripheral.dart'; -export 'my_api.g.dart'; - -// ToObject +// ToObj extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { BluetoothLowEnergyState toState() { switch (this) { @@ -31,209 +27,251 @@ extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { } } -extension MyGattCharacteristicPropertyArgsX - on MyGattCharacteristicPropertyArgs { - GattCharacteristicProperty toProperty() { - return GattCharacteristicProperty.values[index]; +extension MyConnectionStateArgsX on MyConnectionStateArgs { + ConnectionState toState() { + switch (this) { + case MyConnectionStateArgs.disconnected: + case MyConnectionStateArgs.connecting: + return ConnectionState.disconnected; + case MyConnectionStateArgs.connected: + case MyConnectionStateArgs.disconnecting: + return ConnectionState.connected; + } + } +} + +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, + id: idArgs, + data: dataArgs, ); } } 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 { - MyCentral2 toCentral() { - return MyCentral2( - address: addressArgs, + name: nameArgs, + serviceUUIDs: serviceUUIDsArgs + .cast() + .map((args) => UUID.fromString(args)) + .toList(), + serviceData: serviceDataArgs.cast().map( + (uuidArgs, dataArgs) { + final uuid = UUID.fromString(uuidArgs); + return MapEntry(uuid, dataArgs); + }, + ), + manufacturerSpecificData: manufacturerSpecificDataArgs + .cast() + .map((args) => args.toManufacturerSpecificData()) + .toList(), ); } } extension MyPeripheralArgsX on MyPeripheralArgs { - MyPeripheral2 toPeripheral() { - return MyPeripheral2( + MyPeripheral toPeripheral() { + return MyPeripheral( + addressArgs: addressArgs, + ); + } +} + +extension MyGATTDescriptorArgsX on MyGATTDescriptorArgs { + MyGATTDescriptor toDescriptor() { + return MyGATTDescriptor( + hashCodeArgs: hashCodeArgs, + uuid: UUID.fromString(uuidArgs), + ); + } +} + +extension MyGATTCharacteristicArgsX on MyGATTCharacteristicArgs { + MyGATTCharacteristic toCharacteristic() { + return MyGATTCharacteristic( + hashCodeArgs: hashCodeArgs, + uuid: UUID.fromString(uuidArgs), + properties: propertyNumbersArgs.cast().map((args) { + final propertyArgs = MyGATTCharacteristicPropertyArgs.values[args]; + return propertyArgs.toProperty(); + }).toList(), + descriptors: descriptorsArgs + .cast() + .map((args) => args.toDescriptor()) + .toList(), + ); + } +} + +extension MyGATTServiceArgsX on MyGATTServiceArgs { + MyGATTService toService() { + return MyGATTService( + hashCodeArgs: hashCodeArgs, + uuid: UUID.fromString(uuidArgs), + isPrimary: isPrimaryArgs, + includedServices: includedServicesArgs + .cast() + .map((args) => args.toService()) + .toList(), + characteristics: characteristicsArgs + .cast() + .map((args) => args.toCharacteristic()) + .toList(), + ); + } +} + +extension MyCentralArgsX on MyCentralArgs { + MyCentral toCentral() { + return MyCentral( address: addressArgs, ); } } -extension MyGattDescriptorArgsX on MyGattDescriptorArgs { - MyGattDescriptor2 toDescriptor2(MyPeripheral2 peripheral) { - final hashCode = hashCodeArgs; - final uuid = uuidArgs.toUUID(); - return MyGattDescriptor2( - peripheral: peripheral, - hashCode: hashCode, - uuid: uuid, - ); - } -} - -extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs { - MyGattCharacteristic2 toCharacteristic2(MyPeripheral2 peripheral) { - final hashCode = hashCodeArgs; - 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, - hashCode: hashCode, - uuid: uuid, - properties: properties, - descriptors: descriptors, - ); - } -} - -extension MyGattServiceArgsX on MyGattServiceArgs { - MyGattService2 toService2(MyPeripheral2 peripheral) { - final hashCode = hashCodeArgs; - 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 MyUuidArgsX on String { - UUID toUUID() { - return UUID.fromString(this); - } -} - // ToArgs -extension GattCharacteristicPropertyX on GattCharacteristicProperty { - MyGattCharacteristicPropertyArgs toArgs() { - return MyGattCharacteristicPropertyArgs.values[index]; +extension UUIDX on UUID { + String toArgs() { + return toString(); } } -extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType { - MyGattCharacteristicWriteTypeArgs toArgs() { - return MyGattCharacteristicWriteTypeArgs.values[index]; +extension GATTCharacteristicWriteTypeX on GATTCharacteristicWriteType { + MyGATTCharacteristicWriteTypeArgs toArgs() { + return MyGATTCharacteristicWriteTypeArgs.values[index]; + } +} + +extension GATTCharacteristicPropertyX on GATTCharacteristicProperty { + MyGATTCharacteristicPropertyArgs toArgs() { + return MyGATTCharacteristicPropertyArgs.values[index]; + } +} + +extension GATTCharacteristicPermissionX on GATTCharacteristicPermission { + MyGATTCharacteristicPermissionArgs toArgs() { + return MyGATTCharacteristicPermissionArgs.values[index]; + } +} + +extension GATTErrorX on GATTError { + MyGATTStatusArgs toArgs() { + switch (this) { + case GATTError.readNotPermitted: + return MyGATTStatusArgs.readNotPermitted; + case GATTError.writeNotPermitted: + return MyGATTStatusArgs.writeNotPermitted; + case GATTError.insufficientAuthentication: + return MyGATTStatusArgs.insufficientAuthentication; + case GATTError.requestNotSupported: + return MyGATTStatusArgs.requestNotSupported; + case GATTError.invalidOffset: + return MyGATTStatusArgs.invalidOffset; + case GATTError.insufficientAuthorization: + return MyGATTStatusArgs.insufficientAuthorization; + case GATTError.invalidAttributeValueLength: + return MyGATTStatusArgs.invalidAttributeLength; + case GATTError.insufficientEncryption: + return MyGATTStatusArgs.insufficientEncryption; + default: + return MyGATTStatusArgs.failure; + } } } extension ManufacturerSpecificDataX on ManufacturerSpecificData { MyManufacturerSpecificDataArgs toArgs() { - final idArgs = id; - final dataArgs = data; return MyManufacturerSpecificDataArgs( - idArgs: idArgs, - dataArgs: dataArgs, + idArgs: id, + dataArgs: data, ); } } 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, + nameArgs: name, + serviceUUIDsArgs: serviceUUIDs.map((uuid) => uuid.toArgs()).toList(), + serviceDataArgs: serviceData.map((uuid, data) { + final uuidArgs = uuid.toArgs(); + final dataArgs = data; + return MapEntry(uuidArgs, dataArgs); + }), + manufacturerSpecificDataArgs: + manufacturerSpecificData.map((data) => data.toArgs()).toList(), ); } } -extension MyGattDescriptorX on MyGattDescriptor { - MyGattDescriptorArgs toArgs() { - final hashCodeArgs = hashCode; - final uuidArgs = uuid.toArgs(); - final valueArgs = value; - return MyGattDescriptorArgs( - hashCodeArgs: hashCodeArgs, - uuidArgs: uuidArgs, - valueArgs: valueArgs, +extension MutableGATTDescriptorX on MutableGATTDescriptor { + MyMutableGATTDescriptorArgs toArgs() { + return MyMutableGATTDescriptorArgs( + hashCodeArgs: hashCode, + uuidArgs: uuid.toArgs(), + permissionNumbersArgs: permissions.map((permission) { + final permissionArgs = permission.toArgs(); + return permissionArgs.index; + }).toList(), ); } } -extension MyGattCharacteristicX on MyGattCharacteristic { - MyGattCharacteristicArgs toArgs(List descriptorsArgs) { - final hashCodeArgs = hashCode; - final uuidArgs = uuid.toArgs(); - final propertyNumbersArgs = properties.map((property) { - final propertyArgs = property.toArgs(); - return propertyArgs.index; - }).toList(); - return MyGattCharacteristicArgs( - hashCodeArgs: hashCodeArgs, - uuidArgs: uuidArgs, - propertyNumbersArgs: propertyNumbersArgs, +extension MutableGATTCharacteristicX on MutableGATTCharacteristic { + MyMutableGATTCharacteristicArgs toArgs() { + // Add CCC descriptor. + final cccUUID = UUID.short(0x2902); + final descriptorsArgs = descriptors + .cast() + .where((descriptor) => descriptor.uuid != cccUUID) + .map((descriptor) => descriptor.toArgs()) + .toList(); + final cccDescriptor = MutableGATTDescriptor( + uuid: cccUUID, + permissions: [ + GATTCharacteristicPermission.read, + GATTCharacteristicPermission.write, + ], + ); + final cccDescriptorArgs = cccDescriptor.toArgs(); + descriptorsArgs.add(cccDescriptorArgs); + return MyMutableGATTCharacteristicArgs( + hashCodeArgs: hashCode, + uuidArgs: uuid.toArgs(), + permissionNumbersArgs: permissions.map((permission) { + final permissionArgs = permission.toArgs(); + return permissionArgs.index; + }).toList(), + propertyNumbersArgs: properties.map((property) { + final propertyArgs = property.toArgs(); + return propertyArgs.index; + }).toList(), descriptorsArgs: descriptorsArgs, ); } } -extension MyGattServiceX on MyGattService { - MyGattServiceArgs toArgs(List characteristicsArgs) { - final hashCodeArgs = hashCode; - final uuidArgs = uuid.toArgs(); - return MyGattServiceArgs( - hashCodeArgs: hashCodeArgs, - uuidArgs: uuidArgs, - characteristicsArgs: characteristicsArgs, +extension GATTServiceX on GATTService { + MyMutableGATTServiceArgs toArgs() { + return MyMutableGATTServiceArgs( + hashCodeArgs: hashCode, + uuidArgs: uuid.toArgs(), + isPrimaryArgs: isPrimary, + includedServicesArgs: + includedServices.map((service) => service.toArgs()).toList(), + characteristicsArgs: characteristics + .cast() + .map((characteristic) => characteristic.toArgs()) + .toList(), ); } } - -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 c5adf3d..a497ffd 100644 --- a/bluetooth_low_energy_android/lib/src/my_api.g.dart +++ b/bluetooth_low_energy_android/lib/src/my_api.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v15.0.2), do not edit directly. +// Autogenerated from Pigeon (v19.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -35,7 +35,14 @@ enum MyBluetoothLowEnergyStateArgs { turningOff, } -enum MyGattCharacteristicPropertyArgs { +enum MyConnectionStateArgs { + disconnected, + connecting, + connected, + disconnecting, +} + +enum MyGATTCharacteristicPropertyArgs { read, write, writeWithoutResponse, @@ -43,30 +50,94 @@ enum MyGattCharacteristicPropertyArgs { indicate, } -enum MyGattCharacteristicWriteTypeArgs { +enum MyGATTCharacteristicPermissionArgs { + read, + readEncrypted, + write, + writeEncrypted, +} + +enum MyGATTCharacteristicWriteTypeArgs { withResponse, withoutResponse, } -enum MyGattCharacteristicNotifyStateArgs { - none, - notify, - indicate, -} - -enum MyGattStatusArgs { +enum MyGATTStatusArgs { success, readNotPermitted, writeNotPermitted, - requestNotSupported, - invalidOffset, insufficientAuthentication, + requestNotSupported, insufficientEncryption, + invalidOffset, + insufficientAuthorization, invalidAttributeLength, connectionCongested, failure, } +class MyCentralManagerArgs { + MyCentralManagerArgs({ + required this.enableNotificationValue, + required this.enableIndicationValue, + required this.disableNotificationValue, + }); + + Uint8List enableNotificationValue; + + Uint8List enableIndicationValue; + + Uint8List disableNotificationValue; + + Object encode() { + return [ + enableNotificationValue, + enableIndicationValue, + disableNotificationValue, + ]; + } + + static MyCentralManagerArgs decode(Object result) { + result as List; + return MyCentralManagerArgs( + enableNotificationValue: result[0]! as Uint8List, + enableIndicationValue: result[1]! as Uint8List, + disableNotificationValue: result[2]! as Uint8List, + ); + } +} + +class MyPeripheralManagerArgs { + MyPeripheralManagerArgs({ + required this.enableNotificationValue, + required this.enableIndicationValue, + required this.disableNotificationValue, + }); + + Uint8List enableNotificationValue; + + Uint8List enableIndicationValue; + + Uint8List disableNotificationValue; + + Object encode() { + return [ + enableNotificationValue, + enableIndicationValue, + disableNotificationValue, + ]; + } + + static MyPeripheralManagerArgs decode(Object result) { + result as List; + return MyPeripheralManagerArgs( + enableNotificationValue: result[0]! as Uint8List, + enableIndicationValue: result[1]! as Uint8List, + disableNotificationValue: result[2]! as Uint8List, + ); + } +} + class MyManufacturerSpecificDataArgs { MyManufacturerSpecificDataArgs({ required this.idArgs, @@ -98,7 +169,7 @@ class MyAdvertisementArgs { this.nameArgs, required this.serviceUUIDsArgs, required this.serviceDataArgs, - this.manufacturerSpecificDataArgs, + required this.manufacturerSpecificDataArgs, }); String? nameArgs; @@ -107,14 +178,14 @@ class MyAdvertisementArgs { Map serviceDataArgs; - MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs; + List manufacturerSpecificDataArgs; Object encode() { return [ nameArgs, serviceUUIDsArgs, serviceDataArgs, - manufacturerSpecificDataArgs?.encode(), + manufacturerSpecificDataArgs, ]; } @@ -124,9 +195,7 @@ class 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, + manufacturerSpecificDataArgs: (result[3] as List?)!.cast(), ); } } @@ -173,39 +242,34 @@ class MyPeripheralArgs { } } -class MyGattDescriptorArgs { - MyGattDescriptorArgs({ +class MyGATTDescriptorArgs { + MyGATTDescriptorArgs({ required this.hashCodeArgs, required this.uuidArgs, - this.valueArgs, }); int hashCodeArgs; String uuidArgs; - Uint8List? valueArgs; - Object encode() { return [ hashCodeArgs, uuidArgs, - valueArgs, ]; } - static MyGattDescriptorArgs decode(Object result) { + static MyGATTDescriptorArgs decode(Object result) { result as List; - return MyGattDescriptorArgs( + return MyGATTDescriptorArgs( hashCodeArgs: result[0]! as int, uuidArgs: result[1]! as String, - valueArgs: result[2] as Uint8List?, ); } } -class MyGattCharacteristicArgs { - MyGattCharacteristicArgs({ +class MyGATTCharacteristicArgs { + MyGATTCharacteristicArgs({ required this.hashCodeArgs, required this.uuidArgs, required this.propertyNumbersArgs, @@ -218,7 +282,7 @@ class MyGattCharacteristicArgs { List propertyNumbersArgs; - List descriptorsArgs; + List descriptorsArgs; Object encode() { return [ @@ -229,21 +293,23 @@ class MyGattCharacteristicArgs { ]; } - static MyGattCharacteristicArgs decode(Object result) { + static MyGATTCharacteristicArgs decode(Object result) { result as List; - return MyGattCharacteristicArgs( + return MyGATTCharacteristicArgs( hashCodeArgs: result[0]! as int, uuidArgs: result[1]! as String, propertyNumbersArgs: (result[2] as List?)!.cast(), - descriptorsArgs: (result[3] as List?)!.cast(), + descriptorsArgs: (result[3] as List?)!.cast(), ); } } -class MyGattServiceArgs { - MyGattServiceArgs({ +class MyGATTServiceArgs { + MyGATTServiceArgs({ required this.hashCodeArgs, required this.uuidArgs, + required this.isPrimaryArgs, + required this.includedServicesArgs, required this.characteristicsArgs, }); @@ -251,39 +317,166 @@ class MyGattServiceArgs { String uuidArgs; - List characteristicsArgs; + bool isPrimaryArgs; + + List includedServicesArgs; + + List characteristicsArgs; Object encode() { return [ hashCodeArgs, uuidArgs, + isPrimaryArgs, + includedServicesArgs, characteristicsArgs, ]; } - static MyGattServiceArgs decode(Object result) { + static MyGATTServiceArgs decode(Object result) { result as List; - return MyGattServiceArgs( + return MyGATTServiceArgs( hashCodeArgs: result[0]! as int, uuidArgs: result[1]! as String, - characteristicsArgs: (result[2] as List?)!.cast(), + isPrimaryArgs: result[2]! as bool, + includedServicesArgs: (result[3] as List?)!.cast(), + characteristicsArgs: (result[4] as List?)!.cast(), ); } } -class _MyCentralManagerHostApiCodec extends StandardMessageCodec { - const _MyCentralManagerHostApiCodec(); +class MyMutableGATTDescriptorArgs { + MyMutableGATTDescriptorArgs({ + required this.hashCodeArgs, + required this.uuidArgs, + required this.permissionNumbersArgs, + }); + + int hashCodeArgs; + + String uuidArgs; + + List permissionNumbersArgs; + + Object encode() { + return [ + hashCodeArgs, + uuidArgs, + permissionNumbersArgs, + ]; + } + + static MyMutableGATTDescriptorArgs decode(Object result) { + result as List; + return MyMutableGATTDescriptorArgs( + hashCodeArgs: result[0]! as int, + uuidArgs: result[1]! as String, + permissionNumbersArgs: (result[2] as List?)!.cast(), + ); + } +} + +class MyMutableGATTCharacteristicArgs { + MyMutableGATTCharacteristicArgs({ + required this.hashCodeArgs, + required this.uuidArgs, + required this.permissionNumbersArgs, + required this.propertyNumbersArgs, + required this.descriptorsArgs, + }); + + int hashCodeArgs; + + String uuidArgs; + + List permissionNumbersArgs; + + List propertyNumbersArgs; + + List descriptorsArgs; + + Object encode() { + return [ + hashCodeArgs, + uuidArgs, + permissionNumbersArgs, + propertyNumbersArgs, + descriptorsArgs, + ]; + } + + static MyMutableGATTCharacteristicArgs decode(Object result) { + result as List; + return MyMutableGATTCharacteristicArgs( + hashCodeArgs: result[0]! as int, + uuidArgs: result[1]! as String, + permissionNumbersArgs: (result[2] as List?)!.cast(), + propertyNumbersArgs: (result[3] as List?)!.cast(), + descriptorsArgs: (result[4] as List?)!.cast(), + ); + } +} + +class MyMutableGATTServiceArgs { + MyMutableGATTServiceArgs({ + required this.hashCodeArgs, + required this.uuidArgs, + required this.isPrimaryArgs, + required this.includedServicesArgs, + required this.characteristicsArgs, + }); + + int hashCodeArgs; + + String uuidArgs; + + bool isPrimaryArgs; + + List includedServicesArgs; + + List characteristicsArgs; + + Object encode() { + return [ + hashCodeArgs, + uuidArgs, + isPrimaryArgs, + includedServicesArgs, + characteristicsArgs, + ]; + } + + static MyMutableGATTServiceArgs decode(Object result) { + result as List; + return MyMutableGATTServiceArgs( + hashCodeArgs: result[0]! as int, + uuidArgs: result[1]! as String, + isPrimaryArgs: result[2]! as bool, + includedServicesArgs: (result[3] as List?)!.cast(), + characteristicsArgs: (result[4] as List?)!.cast(), + ); + } +} + +class _MyCentralManagerHostAPICodec extends StandardMessageCodec { + const _MyCentralManagerHostAPICodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is MyGattCharacteristicArgs) { + if (value is MyCentralManagerArgs) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is MyGattDescriptorArgs) { + } else if (value is MyGATTCharacteristicArgs) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is MyGattServiceArgs) { + } 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 MyPeripheralArgs) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -293,29 +486,117 @@ class _MyCentralManagerHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return MyGattCharacteristicArgs.decode(readValue(buffer)!); + return MyCentralManagerArgs.decode(readValue(buffer)!); case 129: - return MyGattDescriptorArgs.decode(readValue(buffer)!); + return MyGATTCharacteristicArgs.decode(readValue(buffer)!); case 130: - return MyGattServiceArgs.decode(readValue(buffer)!); + return MyGATTDescriptorArgs.decode(readValue(buffer)!); + case 131: + return MyGATTServiceArgs.decode(readValue(buffer)!); + case 132: + return MyPeripheralArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } } } -class MyCentralManagerHostApi { - /// Constructor for [MyCentralManagerHostApi]. The [binaryMessenger] named argument is +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; + MyCentralManagerHostAPI({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : __pigeon_binaryMessenger = binaryMessenger, + __pigeon_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec pigeonChannelCodec = _MyCentralManagerHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyCentralManagerHostAPICodec(); - Future setUp() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setUp'; + final String __pigeon_messageChannelSuffix; + + Future initialize() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.initialize$__pigeon_messageChannelSuffix'; + 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 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 MyCentralManagerArgs?)!; + } + } + + Future getState() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.getState$__pigeon_messageChannelSuffix'; + 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 if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return MyBluetoothLowEnergyStateArgs.values[__pigeon_replyList[0]! as int]; + } + } + + Future authorize() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.authorize$__pigeon_messageChannelSuffix'; + 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 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 bool?)!; + } + } + + Future showAppSettings() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.showAppSettings$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -336,15 +617,15 @@ class MyCentralManagerHostApi { } } - Future startDiscovery() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.startDiscovery'; + Future startDiscovery(List serviceUUIDsArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.startDiscovery$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; + await __pigeon_channel.send([serviceUUIDsArgs]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -359,7 +640,7 @@ class MyCentralManagerHostApi { } Future stopDiscovery() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.stopDiscovery'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.stopDiscovery$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -381,7 +662,7 @@ class MyCentralManagerHostApi { } Future connect(String addressArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.connect'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.connect$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -403,7 +684,7 @@ class MyCentralManagerHostApi { } Future disconnect(String addressArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.disconnect'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.disconnect$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -424,8 +705,35 @@ class MyCentralManagerHostApi { } } + Future> retrieveConnectedPeripherals() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.retrieveConnectedPeripherals$__pigeon_messageChannelSuffix'; + 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 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 requestMTU(String addressArgs, int mtuArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.requestMTU'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.requestMTU$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -452,7 +760,7 @@ class MyCentralManagerHostApi { } Future readRSSI(String addressArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readRSSI'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.readRSSI$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -478,8 +786,8 @@ class MyCentralManagerHostApi { } } - Future> discoverServices(String addressArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.discoverServices'; + Future> discoverGATT(String addressArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.discoverGATT$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -501,12 +809,12 @@ class MyCentralManagerHostApi { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)!.cast(); + return (__pigeon_replyList[0] as List?)!.cast(); } } Future readCharacteristic(String addressArgs, int hashCodeArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readCharacteristic'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.readCharacteristic$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -532,15 +840,15 @@ class MyCentralManagerHostApi { } } - Future writeCharacteristic(String addressArgs, int hashCodeArgs, Uint8List valueArgs, int typeNumberArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.writeCharacteristic'; + Future writeCharacteristic(String addressArgs, int hashCodeArgs, Uint8List valueArgs, MyGATTCharacteristicWriteTypeArgs typeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.writeCharacteristic$__pigeon_messageChannelSuffix'; 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?; + await __pigeon_channel.send([addressArgs, hashCodeArgs, valueArgs, typeArgs.index]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -554,15 +862,15 @@ class MyCentralManagerHostApi { } } - Future setCharacteristicNotifyState(String addressArgs, int hashCodeArgs, int stateNumberArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setCharacteristicNotifyState'; + Future setCharacteristicNotification(String addressArgs, int hashCodeArgs, bool enableArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.setCharacteristicNotification$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send([addressArgs, hashCodeArgs, stateNumberArgs]) as List?; + await __pigeon_channel.send([addressArgs, hashCodeArgs, enableArgs]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -577,7 +885,7 @@ class MyCentralManagerHostApi { } Future readDescriptor(String addressArgs, int hashCodeArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.readDescriptor'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.readDescriptor$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -604,7 +912,7 @@ class MyCentralManagerHostApi { } Future writeDescriptor(String addressArgs, int hashCodeArgs, Uint8List valueArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.writeDescriptor'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostAPI.writeDescriptor$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -626,216 +934,23 @@ class MyCentralManagerHostApi { } } -class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { - const _MyCentralManagerFlutterApiCodec(); +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 MyGATTCharacteristicArgs) { + 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(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(String addressArgs, bool stateArgs); - - void onMtuChanged(String addressArgs, int mtuArgs); - - void onCharacteristicNotified(String addressArgs, int hashCodeArgs, Uint8List valueArgs); - - static void setup(MyCentralManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { - { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.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_android.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_android.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_android.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_android.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_android.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_android.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_android.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_android.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_android.MyCentralManagerFlutterApi.onConnectionStateChanged 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.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.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_android.MyCentralManagerFlutterApi.onMtuChanged', 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.onMtuChanged 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.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.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); - } 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) { + } else if (value is MyPeripheralArgs) { buffer.putUint8(132); writeValue(buffer, value.encode()); } else { @@ -849,31 +964,324 @@ class _MyPeripheralManagerHostApiCodec extends StandardMessageCodec { case 128: return MyAdvertisementArgs.decode(readValue(buffer)!); case 129: - return MyGattCharacteristicArgs.decode(readValue(buffer)!); + return MyGATTCharacteristicArgs.decode(readValue(buffer)!); case 130: - return MyGattDescriptorArgs.decode(readValue(buffer)!); + return MyGATTDescriptorArgs.decode(readValue(buffer)!); case 131: - return MyGattServiceArgs.decode(readValue(buffer)!); - case 132: return MyManufacturerSpecificDataArgs.decode(readValue(buffer)!); + case 132: + return MyPeripheralArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } } } -class MyPeripheralManagerHostApi { - /// Constructor for [MyPeripheralManagerHostApi]. The [binaryMessenger] named argument is +abstract class MyCentralManagerFlutterAPI { + static const MessageCodec pigeonChannelCodec = _MyCentralManagerFlutterAPICodec(); + + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); + + void onDiscovered(MyPeripheralArgs peripheralArgs, int rssiArgs, MyAdvertisementArgs advertisementArgs); + + void onConnectionStateChanged(MyPeripheralArgs peripheralArgs, MyConnectionStateArgs stateArgs); + + void onMTUChanged(MyPeripheralArgs peripheralArgs, int mtuArgs); + + void onCharacteristicNotified(MyPeripheralArgs peripheralArgs, MyGATTCharacteristicArgs characteristicArgs, Uint8List valueArgs); + + static void setUp(MyCentralManagerFlutterAPI? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onStateChanged$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onStateChanged was null.'); + final List args = (message as List?)!; + final MyBluetoothLowEnergyStateArgs? arg_stateArgs = args[0] == null ? null : MyBluetoothLowEnergyStateArgs.values[args[0]! as int]; + assert(arg_stateArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onStateChanged was null, expected non-null MyBluetoothLowEnergyStateArgs.'); + try { + api.onStateChanged(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_android.MyCentralManagerFlutterAPI.onDiscovered$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.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_android.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_android.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_android.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_android.MyCentralManagerFlutterAPI.onConnectionStateChanged$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.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.onConnectionStateChanged was null, expected non-null MyPeripheralArgs.'); + final MyConnectionStateArgs? arg_stateArgs = args[1] == null ? null : MyConnectionStateArgs.values[args[1]! as int]; + assert(arg_stateArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onConnectionStateChanged was null, expected non-null MyConnectionStateArgs.'); + try { + api.onConnectionStateChanged(arg_peripheralArgs!, 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_android.MyCentralManagerFlutterAPI.onMTUChanged$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onMTUChanged 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.onMTUChanged was null, expected non-null MyPeripheralArgs.'); + 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.onMTUChanged(arg_peripheralArgs!, 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$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onCharacteristicNotified 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.onCharacteristicNotified was null, expected non-null MyPeripheralArgs.'); + final MyGATTCharacteristicArgs? arg_characteristicArgs = (args[1] as MyGATTCharacteristicArgs?); + assert(arg_characteristicArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterAPI.onCharacteristicNotified was null, expected non-null MyGATTCharacteristicArgs.'); + 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_peripheralArgs!, arg_characteristicArgs!, 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 MyManufacturerSpecificDataArgs) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MyMutableGATTCharacteristicArgs) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is MyMutableGATTDescriptorArgs) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is MyMutableGATTServiceArgs) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is MyPeripheralManagerArgs) { + buffer.putUint8(133); + 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 MyMutableGATTCharacteristicArgs.decode(readValue(buffer)!); + case 131: + return MyMutableGATTDescriptorArgs.decode(readValue(buffer)!); + case 132: + return MyMutableGATTServiceArgs.decode(readValue(buffer)!); + case 133: + return MyPeripheralManagerArgs.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; + MyPeripheralManagerHostAPI({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : __pigeon_binaryMessenger = binaryMessenger, + __pigeon_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerHostAPICodec(); - Future setUp() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.setUp'; + final String __pigeon_messageChannelSuffix; + + Future initialize() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.initialize$__pigeon_messageChannelSuffix'; + 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 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 MyPeripheralManagerArgs?)!; + } + } + + Future getState() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.getState$__pigeon_messageChannelSuffix'; + 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 if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return MyBluetoothLowEnergyStateArgs.values[__pigeon_replyList[0]! as int]; + } + } + + Future authorize() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.authorize$__pigeon_messageChannelSuffix'; + 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 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 bool?)!; + } + } + + Future showAppSettings() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.showAppSettings$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -894,8 +1302,52 @@ class MyPeripheralManagerHostApi { } } - Future addService(MyGattServiceArgs serviceArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.addService'; + Future openGATTServer() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.openGATTServer$__pigeon_messageChannelSuffix'; + 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 closeGATTServer() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.closeGATTServer$__pigeon_messageChannelSuffix'; + 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(MyMutableGATTServiceArgs serviceArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.addService$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -917,7 +1369,7 @@ class MyPeripheralManagerHostApi { } Future removeService(int hashCodeArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.removeService'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.removeService$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -938,8 +1390,8 @@ class MyPeripheralManagerHostApi { } } - Future clearServices() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.clearServices'; + Future removeAllServices() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.removeAllServices$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -961,7 +1413,7 @@ class MyPeripheralManagerHostApi { } Future startAdvertising(MyAdvertisementArgs advertisementArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.startAdvertising'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.startAdvertising$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -983,7 +1435,7 @@ class MyPeripheralManagerHostApi { } Future stopAdvertising() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.stopAdvertising'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.stopAdvertising$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -1004,15 +1456,15 @@ class MyPeripheralManagerHostApi { } } - 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'; + Future sendResponse(String addressArgs, int idArgs, MyGATTStatusArgs statusArgs, int offsetArgs, Uint8List? valueArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.sendResponse$__pigeon_messageChannelSuffix'; 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?; + await __pigeon_channel.send([addressArgs, idArgs, statusArgs.index, offsetArgs, valueArgs]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -1026,15 +1478,15 @@ class MyPeripheralManagerHostApi { } } - Future notifyCharacteristicChanged(int hashCodeArgs, Uint8List valueArgs, bool confirmArgs, String addressArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.notifyCharacteristicChanged'; + Future notifyCharacteristicChanged(String addressArgs, int hashCodeArgs, bool confirmArgs, Uint8List valueArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostAPI.notifyCharacteristicChanged$__pigeon_messageChannelSuffix'; 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?; + await __pigeon_channel.send([addressArgs, hashCodeArgs, confirmArgs, valueArgs]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -1049,8 +1501,8 @@ class MyPeripheralManagerHostApi { } } -class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { - const _MyPeripheralManagerFlutterApiCodec(); +class _MyPeripheralManagerFlutterAPICodec extends StandardMessageCodec { + const _MyPeripheralManagerFlutterAPICodec(); @override void writeValue(WriteBuffer buffer, Object? value) { if (value is MyCentralArgs) { @@ -1072,44 +1524,43 @@ class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { } } -abstract class MyPeripheralManagerFlutterApi { - static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerFlutterApiCodec(); +abstract class MyPeripheralManagerFlutterAPI { + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerFlutterAPICodec(); - void onStateChanged(int stateNumberArgs); + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); - void onConnectionStateChanged(MyCentralArgs centralArgs, bool stateArgs); + void onConnectionStateChanged(MyCentralArgs centralArgs, int statusArgs, MyConnectionStateArgs stateArgs); - void onMtuChanged(String addressArgs, int mtuArgs); + void onMTUChanged(MyCentralArgs centralArgs, int mtuArgs); - void onCharacteristicReadRequest(String addressArgs, int hashCodeArgs, int idArgs, int offsetArgs); + void onCharacteristicReadRequest(MyCentralArgs centralArgs, int idArgs, int offsetArgs, int hashCodeArgs); - void onCharacteristicWriteRequest(String addressArgs, int hashCodeArgs, int idArgs, int offsetArgs, Uint8List valueArgs, bool preparedWriteArgs, bool responseNeededArgs); + void onCharacteristicWriteRequest(MyCentralArgs centralArgs, int idArgs, int hashCodeArgs, bool preparedWriteArgs, bool responseNeededArgs, int offsetArgs, Uint8List valueArgs); - void onCharacteristicNotifyStateChanged(String addressArgs, int hashCodeArgs, int stateNumberArgs); + void onDescriptorReadRequest(MyCentralArgs centralArgs, int idArgs, int offsetArgs, int hashCodeArgs); - void onDescriptorReadRequest(String addressArgs, int hashCodeArgs, int idArgs, int offsetArgs); + void onDescriptorWriteRequest(MyCentralArgs centralArgs, int idArgs, int hashCodeArgs, bool preparedWriteArgs, bool responseNeededArgs, int offsetArgs, Uint8List valueArgs); - void onDescriptorWriteRequest(String addressArgs, int hashCodeArgs, int idArgs, int offsetArgs, Uint8List valueArgs, bool preparedWriteArgs, bool responseNeededArgs); + void onExecuteWrite(MyCentralArgs centralArgs, int idArgs, bool executeArgs); - void onExecuteWrite(String addressArgs, int idArgs, bool executeArgs); - - static void setup(MyPeripheralManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + static void setUp(MyPeripheralManagerFlutterAPI? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onStateChanged', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onStateChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onStateChanged was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.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_android.MyPeripheralManagerFlutterApi.onStateChanged was null, expected non-null int.'); + final MyBluetoothLowEnergyStateArgs? arg_stateArgs = args[0] == null ? null : MyBluetoothLowEnergyStateArgs.values[args[0]! as int]; + assert(arg_stateArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onStateChanged was null, expected non-null MyBluetoothLowEnergyStateArgs.'); try { - api.onStateChanged(arg_stateNumberArgs!); + api.onStateChanged(arg_stateArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1121,23 +1572,26 @@ abstract class MyPeripheralManagerFlutterApi { } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onConnectionStateChanged', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onConnectionStateChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onConnectionStateChanged 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.onConnectionStateChanged was null, expected non-null MyCentralArgs.'); - final bool? arg_stateArgs = (args[1] as bool?); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onConnectionStateChanged was null, expected non-null MyCentralArgs.'); + final int? arg_statusArgs = (args[1] as int?); + assert(arg_statusArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onConnectionStateChanged was null, expected non-null int.'); + final MyConnectionStateArgs? arg_stateArgs = args[2] == null ? null : MyConnectionStateArgs.values[args[2]! as int]; assert(arg_stateArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onConnectionStateChanged was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onConnectionStateChanged was null, expected non-null MyConnectionStateArgs.'); try { - api.onConnectionStateChanged(arg_centralArgs!, arg_stateArgs!); + api.onConnectionStateChanged(arg_centralArgs!, arg_statusArgs!, arg_stateArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1149,23 +1603,23 @@ abstract class MyPeripheralManagerFlutterApi { } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onMtuChanged', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onMTUChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onMtuChanged was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onMTUChanged 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.onMtuChanged was null, expected non-null String.'); + final MyCentralArgs? arg_centralArgs = (args[0] as MyCentralArgs?); + assert(arg_centralArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onMTUChanged was null, expected non-null MyCentralArgs.'); 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.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onMTUChanged was null, expected non-null int.'); try { - api.onMtuChanged(arg_addressArgs!, arg_mtuArgs!); + api.onMTUChanged(arg_centralArgs!, arg_mtuArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1177,211 +1631,180 @@ abstract class MyPeripheralManagerFlutterApi { } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onCharacteristicReadRequest$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was 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.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.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.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.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); - } 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); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onDescriptorReadRequest', 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.onDescriptorReadRequest 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.onDescriptorReadRequest 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.onDescriptorReadRequest 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.onDescriptorReadRequest 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.onDescriptorReadRequest was null, expected non-null int.'); - try { - api.onDescriptorReadRequest(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.onDescriptorWriteRequest', 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.onDescriptorWriteRequest 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.onDescriptorWriteRequest 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.onDescriptorWriteRequest 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.onDescriptorWriteRequest 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.onDescriptorWriteRequest 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.onDescriptorWriteRequest 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.onDescriptorWriteRequest 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.onDescriptorWriteRequest was null, expected non-null bool.'); - try { - api.onDescriptorWriteRequest(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); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); - } - }); - } - } - { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onExecuteWrite', 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.onExecuteWrite 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.onExecuteWrite was null, expected non-null String.'); + final MyCentralArgs? arg_centralArgs = (args[0] as MyCentralArgs?); + assert(arg_centralArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onCharacteristicReadRequest was null, expected non-null MyCentralArgs.'); 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.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onCharacteristicReadRequest was null, expected non-null int.'); + final int? arg_offsetArgs = (args[2] as int?); + assert(arg_offsetArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onCharacteristicReadRequest was null, expected non-null int.'); + final int? arg_hashCodeArgs = (args[3] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onCharacteristicReadRequest was null, expected non-null int.'); + try { + api.onCharacteristicReadRequest(arg_centralArgs!, arg_idArgs!, arg_offsetArgs!, arg_hashCodeArgs!); + 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$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.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_android.MyPeripheralManagerFlutterAPI.onCharacteristicWriteRequest was null, expected non-null MyCentralArgs.'); + final int? arg_idArgs = (args[1] 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_hashCodeArgs = (args[2] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onCharacteristicWriteRequest was null, expected non-null int.'); + final bool? arg_preparedWriteArgs = (args[3] 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[4] as bool?); + assert(arg_responseNeededArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onCharacteristicWriteRequest was null, expected non-null bool.'); + final int? arg_offsetArgs = (args[5] 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[6] as Uint8List?); + assert(arg_valueArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onCharacteristicWriteRequest was null, expected non-null Uint8List.'); + try { + api.onCharacteristicWriteRequest(arg_centralArgs!, arg_idArgs!, arg_hashCodeArgs!, arg_preparedWriteArgs!, arg_responseNeededArgs!, arg_offsetArgs!, 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_android.MyPeripheralManagerFlutterAPI.onDescriptorReadRequest$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorReadRequest 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.onDescriptorReadRequest was null, expected non-null MyCentralArgs.'); + final int? arg_idArgs = (args[1] as int?); + assert(arg_idArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorReadRequest was null, expected non-null int.'); + final int? arg_offsetArgs = (args[2] as int?); + assert(arg_offsetArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorReadRequest was null, expected non-null int.'); + final int? arg_hashCodeArgs = (args[3] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorReadRequest was null, expected non-null int.'); + try { + api.onDescriptorReadRequest(arg_centralArgs!, arg_idArgs!, arg_offsetArgs!, arg_hashCodeArgs!); + 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.onDescriptorWriteRequest$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest 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.onDescriptorWriteRequest was null, expected non-null MyCentralArgs.'); + final int? arg_idArgs = (args[1] as int?); + assert(arg_idArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest was null, expected non-null int.'); + final int? arg_hashCodeArgs = (args[2] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest was null, expected non-null int.'); + final bool? arg_preparedWriteArgs = (args[3] as bool?); + assert(arg_preparedWriteArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest was null, expected non-null bool.'); + final bool? arg_responseNeededArgs = (args[4] as bool?); + assert(arg_responseNeededArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest was null, expected non-null bool.'); + final int? arg_offsetArgs = (args[5] as int?); + assert(arg_offsetArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest was null, expected non-null int.'); + final Uint8List? arg_valueArgs = (args[6] as Uint8List?); + assert(arg_valueArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest was null, expected non-null Uint8List.'); + try { + api.onDescriptorWriteRequest(arg_centralArgs!, arg_idArgs!, arg_hashCodeArgs!, arg_preparedWriteArgs!, arg_responseNeededArgs!, arg_offsetArgs!, 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_android.MyPeripheralManagerFlutterAPI.onExecuteWrite$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.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.onExecuteWrite was null, expected non-null MyCentralArgs.'); + 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.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterAPI.onExecuteWrite was null, expected non-null bool.'); try { - api.onExecuteWrite(arg_addressArgs!, arg_idArgs!, arg_executeArgs!); + api.onExecuteWrite(arg_centralArgs!, arg_idArgs!, arg_executeArgs!); 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_central.dart similarity index 80% rename from bluetooth_low_energy_android/lib/src/my_central2.dart rename to bluetooth_low_energy_android/lib/src/my_central.dart index e569b98..abedeca 100644 --- a/bluetooth_low_energy_android/lib/src/my_central2.dart +++ b/bluetooth_low_energy_android/lib/src/my_central.dart @@ -1,9 +1,9 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -class MyCentral2 extends MyCentral { +final class MyCentral extends Central { final String address; - MyCentral2({ + MyCentral({ 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 index a9c285e..e810aa9 100644 --- a/bluetooth_low_energy_android/lib/src/my_central_manager.dart +++ b/bluetooth_low_energy_android/lib/src/my_central_manager.dart @@ -1,70 +1,100 @@ 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 'package:flutter/widgets.dart' hide ConnectionState; import 'my_api.dart'; -import 'my_gatt_characteristic2.dart'; -import 'my_gatt_descriptor2.dart'; -import 'my_peripheral2.dart'; +import 'my_api.g.dart'; +import 'my_gatt.dart'; +import 'my_peripheral.dart'; -class MyCentralManager extends CentralManager - implements MyCentralManagerFlutterApi { - final MyCentralManagerHostApi _api; +final class MyCentralManager extends PlatformCentralManager + with WidgetsBindingObserver + implements MyCentralManagerFlutterAPI { + final MyCentralManagerHostAPI _api; final StreamController _stateChangedController; final StreamController _discoveredController; - final StreamController + final StreamController _connectionStateChangedController; - final StreamController + final StreamController _mtuChangedController; + final StreamController _characteristicNotifiedController; - final Map _peripherals; - final Map> _characteristics; final Map _mtus; + late final MyCentralManagerArgs _args; + BluetoothLowEnergyState _state; MyCentralManager() - : _api = MyCentralManagerHostApi(), + : _api = MyCentralManagerHostAPI(), _stateChangedController = StreamController.broadcast(), _discoveredController = StreamController.broadcast(), _connectionStateChangedController = StreamController.broadcast(), + _mtuChangedController = StreamController.broadcast(), _characteristicNotifiedController = StreamController.broadcast(), - _peripherals = {}, - _characteristics = {}, _mtus = {}, _state = BluetoothLowEnergyState.unknown; + UUID get cccUUID => UUID.short(0x2902); + @override + BluetoothLowEnergyState get state => _state; @override Stream get stateChanged => _stateChangedController.stream; @override Stream get discovered => _discoveredController.stream; @override - Stream get connectionStateChanged => - _connectionStateChangedController.stream; + Stream + get connectionStateChanged => _connectionStateChangedController.stream; @override - Stream get characteristicNotified => + Stream get mtuChanged => + _mtuChangedController.stream; + @override + Stream get characteristicNotified => _characteristicNotifiedController.stream; @override - Future setUp() async { - logger.info('setUp'); - await _api.setUp(); - MyCentralManagerFlutterApi.setup(this); + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + logger.info('didChangeAppLifecycleState: $state'); + if (state != AppLifecycleState.resumed) { + return; + } + _getState(); } @override - Future getState() { - logger.info('getState'); - return Future.value(_state); + void initialize() { + MyCentralManagerFlutterAPI.setUp(this); + final binding = WidgetsFlutterBinding.ensureInitialized(); + binding.addObserver(this); + _initialize(); } @override - Future startDiscovery() async { - logger.info('startDiscovery'); - await _api.startDiscovery(); + Future authorize() async { + logger.info('authorize'); + final authorized = await _api.authorize(); + return authorized; + } + + @override + Future showAppSettings() async { + logger.info('showAppSettings'); + await _api.showAppSettings(); + } + + @override + Future startDiscovery({ + List? serviceUUIDs, + }) async { + final serviceUUIDsArgs = + serviceUUIDs?.map((uuid) => uuid.toArgs()).toList() ?? []; + logger.info('startDiscovery: $serviceUUIDsArgs'); + await _api.startDiscovery(serviceUUIDsArgs); } @override @@ -73,74 +103,103 @@ class MyCentralManager extends CentralManager await _api.stopDiscovery(); } + @override + Future> retrieveConnectedPeripherals() async { + logger.info('retrieveConnectedPeripherals'); + final peripheralsArgs = await _api.retrieveConnectedPeripherals(); + final peripherals = peripheralsArgs + .cast() + .map((args) => args.toPeripheral()) + .toList(); + return peripherals; + } + @override Future connect(Peripheral peripheral) async { - if (peripheral is! MyPeripheral2) { + if (peripheral is! MyPeripheral) { throw TypeError(); } - final addressArgs = peripheral.address; + final addressArgs = peripheral.addressArgs; 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) { + if (peripheral is! MyPeripheral) { throw TypeError(); } - final addressArgs = peripheral.address; + final addressArgs = peripheral.addressArgs; logger.info('disconnect: $addressArgs'); await _api.disconnect(addressArgs); } @override - Future readRSSI(Peripheral peripheral) async { - if (peripheral is! MyPeripheral2) { + Future requestMTU( + Peripheral peripheral, { + required int mtu, + }) async { + if (peripheral is! MyPeripheral) { throw TypeError(); } - final addressArgs = peripheral.address; + final addressArgs = peripheral.addressArgs; + final mtuArgs = mtu; + logger.info('requestMTU: $addressArgs - $mtuArgs'); + final size = await _api.requestMTU(addressArgs, mtuArgs); + return size; + } + + @override + Future getMaximumWriteLength( + Peripheral peripheral, { + required GATTCharacteristicWriteType type, + }) { + if (peripheral is! MyPeripheral) { + throw TypeError(); + } + final addressArgs = peripheral.addressArgs; + final mtu = _mtus[addressArgs] ?? 23; + final maximumWriteLength = (mtu - 3).clamp(20, 512); + return Future.value(maximumWriteLength); + } + + @override + Future readRSSI(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { + throw TypeError(); + } + final addressArgs = peripheral.addressArgs; logger.info('readRSSI: $addressArgs'); final rssi = await _api.readRSSI(addressArgs); return rssi; } @override - Future> discoverGATT(Peripheral peripheral) async { - if (peripheral is! MyPeripheral2) { + Future> discoverGATT(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { throw TypeError(); } - final addressArgs = peripheral.address; - logger.info('discoverServices: $addressArgs'); - final servicesArgs = await _api.discoverServices(addressArgs); + final addressArgs = peripheral.addressArgs; + logger.info('discoverGATT: $addressArgs'); + final servicesArgs = await _api.discoverGATT(addressArgs); final services = servicesArgs - .cast() - .map((args) => args.toService2(peripheral)) + .cast() + .map((args) => args.toService()) .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, + Peripheral peripheral, + GATTCharacteristic characteristic, ) async { - if (characteristic is! MyGattCharacteristic2) { + if (peripheral is! MyPeripheral || + characteristic is! MyGATTCharacteristic) { throw TypeError(); } - final peripheral = characteristic.peripheral; - final addressArgs = peripheral.address; - final hashCodeArgs = characteristic.hashCode; + final addressArgs = peripheral.addressArgs; + final hashCodeArgs = characteristic.hashCodeArgs; logger.info('readCharacteristic: $addressArgs.$hashCodeArgs'); final value = await _api.readCharacteristic(addressArgs, hashCodeArgs); return value; @@ -148,86 +207,75 @@ class MyCentralManager extends CentralManager @override Future writeCharacteristic( - GattCharacteristic characteristic, { + Peripheral peripheral, + GATTCharacteristic characteristic, { required Uint8List value, - required GattCharacteristicWriteType type, + required GATTCharacteristicWriteType type, }) async { - if (characteristic is! MyGattCharacteristic2) { + if (peripheral is! MyPeripheral || + characteristic is! MyGATTCharacteristic) { throw TypeError(); } - final peripheral = characteristic.peripheral; - final addressArgs = peripheral.address; - final hashCodeArgs = characteristic.hashCode; - final trimmedValueArgs = value.trimGATT(); + final addressArgs = peripheral.addressArgs; + final hashCodeArgs = characteristic.hashCodeArgs; + final valueArgs = value; 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( + 'writeCharacteristic: $addressArgs.$hashCodeArgs - $valueArgs, $typeArgs'); + await _api.writeCharacteristic( addressArgs, hashCodeArgs, - stateNumberArgs, + valueArgs, + typeArgs, ); } @override - Future readDescriptor(GattDescriptor descriptor) async { - if (descriptor is! MyGattDescriptor2) { + Future setCharacteristicNotifyState( + Peripheral peripheral, + GATTCharacteristic characteristic, { + required bool state, + }) async { + if (peripheral is! MyPeripheral || + characteristic is! MyGATTCharacteristic) { throw TypeError(); } - final peripheral = descriptor.peripheral; - final addressArgs = peripheral.address; - final hashCodeArgs = descriptor.hashCode; + final addressArgs = peripheral.addressArgs; + final hashCodeArgs = characteristic.hashCodeArgs; + final enableArgs = state; + logger.info( + 'setCharacteristicNotification: $addressArgs.$hashCodeArgs - $enableArgs'); + await _api.setCharacteristicNotification( + addressArgs, + hashCodeArgs, + enableArgs, + ); + // 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 + final descriptor = characteristic.descriptors + .firstWhere((descriptor) => descriptor.uuid == cccUUID); + final value = state + ? characteristic.properties.contains(GATTCharacteristicProperty.notify) + ? _args.enableNotificationValue + : _args.enableIndicationValue + : _args.disableNotificationValue; + await writeDescriptor( + peripheral, + descriptor, + value: value, + ); + } + + @override + Future readDescriptor( + Peripheral peripheral, + GATTDescriptor descriptor, + ) async { + if (peripheral is! MyPeripheral || descriptor is! MyGATTDescriptor) { + throw TypeError(); + } + final addressArgs = peripheral.addressArgs; + final hashCodeArgs = descriptor.hashCodeArgs; logger.info('readDescriptor: $addressArgs.$hashCodeArgs'); final value = await _api.readDescriptor(addressArgs, hashCodeArgs); return value; @@ -235,24 +283,22 @@ class MyCentralManager extends CentralManager @override Future writeDescriptor( - GattDescriptor descriptor, { + Peripheral peripheral, + GATTDescriptor descriptor, { required Uint8List value, }) async { - if (descriptor is! MyGattDescriptor2) { + if (peripheral is! MyPeripheral || descriptor is! MyGATTDescriptor) { 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); + final addressArgs = peripheral.addressArgs; + final hashCodeArgs = descriptor.hashCodeArgs; + final valueArgs = value; + logger.info('writeDescriptor: $addressArgs.$hashCodeArgs - $valueArgs'); + await _api.writeDescriptor(addressArgs, hashCodeArgs, valueArgs); } @override - void onStateChanged(int stateNumberArgs) { - final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs) { logger.info('onStateChanged: $stateArgs'); final state = stateArgs.toState(); if (_state == state) { @@ -271,10 +317,7 @@ class MyCentralManager extends CentralManager ) { final addressArgs = peripheralArgs.addressArgs; logger.info('onDiscovered: $addressArgs - $rssiArgs, $advertisementArgs'); - final peripheral = _peripherals.putIfAbsent( - peripheralArgs.addressArgs, - () => peripheralArgs.toPeripheral(), - ); + final peripheral = peripheralArgs.toPeripheral(); final rssi = rssiArgs; final advertisement = advertisementArgs.toAdvertisement(); final eventArgs = DiscoveredEventArgs( @@ -286,56 +329,76 @@ class MyCentralManager extends CentralManager } @override - void onConnectionStateChanged(String addressArgs, bool stateArgs) { + void onConnectionStateChanged( + MyPeripheralArgs peripheralArgs, + MyConnectionStateArgs stateArgs, + ) { + final addressArgs = peripheralArgs.addressArgs; 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); + final peripheral = peripheralArgs.toPeripheral(); + final state = stateArgs.toState(); + if (state == ConnectionState.disconnected) { _mtus.remove(addressArgs); } + final eventArgs = PeripheralConnectionStateChangedEventArgs( + peripheral, + state, + ); + _connectionStateChangedController.add(eventArgs); } @override - void onMtuChanged(String addressArgs, int mtuArgs) { - logger.info('onMtuChanged: $addressArgs - $mtuArgs'); + void onMTUChanged(MyPeripheralArgs peripheralArgs, int mtuArgs) { + final addressArgs = peripheralArgs.addressArgs; + logger.info('onMTUChanged: $addressArgs - $mtuArgs'); + final peripheral = peripheralArgs.toPeripheral(); final mtu = mtuArgs; _mtus[addressArgs] = mtu; + final eventArgs = PeripheralMTUChangedEventArgs(peripheral, mtu); + _mtuChangedController.add(eventArgs); } @override void onCharacteristicNotified( - String addressArgs, - int hashCodeArgs, + MyPeripheralArgs peripheralArgs, + MyGATTCharacteristicArgs characteristicArgs, Uint8List valueArgs, ) { + final addressArgs = peripheralArgs.addressArgs; + final hashCodeArgs = characteristicArgs.hashCodeArgs; logger.info( 'onCharacteristicNotified: $addressArgs.$hashCodeArgs - $valueArgs'); - final characteristic = _retrieveCharacteristic(addressArgs, hashCodeArgs); - if (characteristic == null) { - return; - } + final peripheral = peripheralArgs.toPeripheral(); + final characteristic = characteristicArgs.toCharacteristic(); final value = valueArgs; - final eventArgs = GattCharacteristicNotifiedEventArgs( + final eventArgs = GATTCharacteristicNotifiedEventArgs( + peripheral, characteristic, value, ); _characteristicNotifiedController.add(eventArgs); } - MyGattCharacteristic2? _retrieveCharacteristic( - String addressArgs, - int hashCodeArgs, - ) { - final characteristics = _characteristics[addressArgs]; - if (characteristics == null) { - return null; + Future _initialize() async { + // Here we use `Future()` to make it possible to change the `logLevel` before `initialize()`. + await Future(() async { + try { + logger.info('initialize'); + _args = await _api.initialize(); + _getState(); + } catch (e) { + logger.severe('initialize failed.', e); + } + }); + } + + Future _getState() async { + try { + logger.info('getState'); + final stateArgs = await _api.getState(); + onStateChanged(stateArgs); + } catch (e) { + logger.severe('getState failed.', e); } - return characteristics[hashCodeArgs]; } } diff --git a/bluetooth_low_energy_android/lib/src/my_gatt.dart b/bluetooth_low_energy_android/lib/src/my_gatt.dart new file mode 100644 index 0000000..6520dda --- /dev/null +++ b/bluetooth_low_energy_android/lib/src/my_gatt.dart @@ -0,0 +1,95 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +final class MyGATTDescriptor extends GATTDescriptor { + final int hashCodeArgs; + + MyGATTDescriptor({ + required this.hashCodeArgs, + required super.uuid, + }); + + @override + int get hashCode => hashCodeArgs; + + @override + bool operator ==(Object other) { + return other is MyGATTDescriptor && other.hashCodeArgs == hashCodeArgs; + } +} + +final class MyGATTCharacteristic extends GATTCharacteristic { + final int hashCodeArgs; + + MyGATTCharacteristic({ + required this.hashCodeArgs, + required super.uuid, + required super.properties, + required List descriptors, + }) : super( + descriptors: descriptors, + ); + + @override + List get descriptors => + super.descriptors.cast(); + + @override + int get hashCode => hashCodeArgs; + + @override + bool operator ==(Object other) { + return other is MyGATTCharacteristic && other.hashCodeArgs == hashCodeArgs; + } +} + +final class MyGATTService extends GATTService { + final int hashCodeArgs; + + MyGATTService({ + required this.hashCodeArgs, + required super.uuid, + required super.isPrimary, + required List includedServices, + required List characteristics, + }) : super( + includedServices: includedServices, + characteristics: characteristics, + ); + + @override + List get characteristics => + super.characteristics.cast(); + + @override + int get hashCode => hashCodeArgs; + + @override + bool operator ==(Object other) { + return other is MyGATTService && other.hashCodeArgs == hashCodeArgs; + } +} + +final class MyGATTReadRequest extends GATTReadRequest { + final String addressArgs; + final int idArgs; + + MyGATTReadRequest({ + required this.addressArgs, + required this.idArgs, + required super.offset, + }); +} + +final class MyGATTWriteRequest extends GATTWriteRequest { + final String addressArgs; + final int idArgs; + final bool responseNeededArgs; + + MyGATTWriteRequest({ + required this.addressArgs, + required this.idArgs, + required this.responseNeededArgs, + required super.offset, + required super.value, + }); +} diff --git a/bluetooth_low_energy_android/lib/src/my_gatt_characteristic2.dart b/bluetooth_low_energy_android/lib/src/my_gatt_characteristic2.dart deleted file mode 100644 index 5de360d..0000000 --- a/bluetooth_low_energy_android/lib/src/my_gatt_characteristic2.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; - -import 'my_gatt_descriptor2.dart'; -import 'my_peripheral2.dart'; - -class MyGattCharacteristic2 extends MyGattCharacteristic { - final MyPeripheral2 peripheral; - @override - final int hashCode; - - MyGattCharacteristic2({ - required this.peripheral, - required this.hashCode, - required super.uuid, - required super.properties, - required List descriptors, - }) : super(descriptors: descriptors); - - @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 deleted file mode 100644 index 30bc7d7..0000000 --- a/bluetooth_low_energy_android/lib/src/my_gatt_descriptor2.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; - -import 'my_peripheral2.dart'; - -class MyGattDescriptor2 extends MyGattDescriptor { - final MyPeripheral2 peripheral; - @override - final int hashCode; - - MyGattDescriptor2({ - 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 deleted file mode 100644 index e1d365f..0000000 --- a/bluetooth_low_energy_android/lib/src/my_gatt_service2.dart +++ /dev/null @@ -1,28 +0,0 @@ -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 { - final MyPeripheral2 peripheral; - @override - final int hashCode; - - MyGattService2({ - required this.peripheral, - required this.hashCode, - required super.uuid, - required List characteristics, - }) : super(characteristics: characteristics); - - @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_peripheral.dart b/bluetooth_low_energy_android/lib/src/my_peripheral.dart new file mode 100644 index 0000000..6bcacab --- /dev/null +++ b/bluetooth_low_energy_android/lib/src/my_peripheral.dart @@ -0,0 +1,11 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +final class MyPeripheral extends Peripheral { + final String addressArgs; + + MyPeripheral({ + required this.addressArgs, + }) : super( + uuid: UUID.fromAddress(addressArgs), + ); +} diff --git a/bluetooth_low_energy_android/lib/src/my_peripheral2.dart b/bluetooth_low_energy_android/lib/src/my_peripheral2.dart deleted file mode 100644 index 60faa38..0000000 --- a/bluetooth_low_energy_android/lib/src/my_peripheral2.dart +++ /dev/null @@ -1,11 +0,0 @@ -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 index 4280b13..82f622e 100644 --- a/bluetooth_low_energy_android/lib/src/my_peripheral_manager.dart +++ b/bluetooth_low_energy_android/lib/src/my_peripheral_manager.dart @@ -2,133 +2,156 @@ 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 'package:collection/collection.dart'; +import 'package:flutter/widgets.dart' hide ConnectionState; import 'my_api.dart'; -import 'my_central2.dart'; +import 'my_api.g.dart'; +import 'my_central.dart'; +import 'my_gatt.dart'; -class MyPeripheralManager extends PeripheralManager - implements MyPeripheralManagerFlutterApi { - final MyPeripheralManagerHostApi _api; +final class MyPeripheralManager extends PlatformPeripheralManager + with WidgetsBindingObserver + implements MyPeripheralManagerFlutterAPI { + final MyPeripheralManagerHostAPI _api; + final ListEquality _valueEquality; final StreamController _stateChangedController; - final StreamController - _characteristicReadController; - final StreamController - _characteristicWrittenController; - final StreamController + final StreamController + _connnectionStateChangedController; + final StreamController _mtuChangedController; + final StreamController + _characteristicReadRequestedController; + final StreamController + _characteristicWriteRequestedController; + final StreamController _characteristicNotifyStateChangedController; + final StreamController + _descriptorReadRequestedController; + final StreamController + _descriptorWriteRequestedController; - final Map _centrals; - final Map> _characteristics; - final Map> _descriptors; + final Map _characteristics; + final Map _descriptors; + // CCC descriptor hashCodeArgs -> characteristic + final Map _cccdCharacteristics; + // central addressArgs -> characteristic hashCodeArgs -> CCC descriptor Value + final Map> _cccdValues; final Map _mtus; - final Map> _confirms; - final Map _preparedCharacteristics; - final Map _preparedDescriptors; + final Map _preparedHashCodeArgs; final Map> _preparedValue; + late final MyPeripheralManagerArgs _args; + BluetoothLowEnergyState _state; MyPeripheralManager() - : _api = MyPeripheralManagerHostApi(), + : _api = MyPeripheralManagerHostAPI(), + _valueEquality = const ListEquality(), _stateChangedController = StreamController.broadcast(), - _characteristicReadController = StreamController.broadcast(), - _characteristicWrittenController = StreamController.broadcast(), + _connnectionStateChangedController = StreamController.broadcast(), + _mtuChangedController = StreamController.broadcast(), + _characteristicReadRequestedController = StreamController.broadcast(), + _characteristicWriteRequestedController = StreamController.broadcast(), _characteristicNotifyStateChangedController = StreamController.broadcast(), - _centrals = {}, + _descriptorReadRequestedController = StreamController.broadcast(), + _descriptorWriteRequestedController = StreamController.broadcast(), _characteristics = {}, _descriptors = {}, + _cccdCharacteristics = {}, + _cccdValues = {}, _mtus = {}, - _confirms = {}, - _preparedCharacteristics = {}, - _preparedDescriptors = {}, + _preparedHashCodeArgs = {}, _preparedValue = {}, _state = BluetoothLowEnergyState.unknown; + UUID get cccUUID => UUID.short(0x2902); + @override + BluetoothLowEnergyState get state => _state; @override Stream get stateChanged => _stateChangedController.stream; @override - Stream get characteristicRead => - _characteristicReadController.stream; + Stream get connectionStateChanged => + _connnectionStateChangedController.stream; @override - Stream get characteristicWritten => - _characteristicWrittenController.stream; + Stream get mtuChanged => + _mtuChangedController.stream; @override - Stream + Stream + get characteristicReadRequested => + _characteristicReadRequestedController.stream; + @override + Stream + get characteristicWriteRequested => + _characteristicWriteRequestedController.stream; + @override + Stream get characteristicNotifyStateChanged => _characteristicNotifyStateChangedController.stream; + @override + Stream get descriptorReadRequested => + _descriptorReadRequestedController.stream; + @override + Stream get descriptorWriteRequested => + _descriptorWriteRequestedController.stream; @override - Future setUp() async { - logger.info('setUp'); - await _api.setUp(); - MyPeripheralManagerFlutterApi.setup(this); + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + logger.info('didChangeAppLifecycleState: $state'); + if (state != AppLifecycleState.resumed) { + return; + } + _getState(); } @override - Future getState() { - logger.info('getState'); - return Future.value(_state); + void initialize() { + MyPeripheralManagerFlutterAPI.setUp(this); + final binding = WidgetsFlutterBinding.ensureInitialized(); + binding.addObserver(this); + _initialize(); } @override - Future addService(GattService service) async { - if (service is! MyGattService) { - throw TypeError(); - } - final characteristics = {}; - final descriptors = {}; - final characteristicsArgs = []; - for (var characteristic in service.characteristics) { - final descriptorsArgs = []; - final properties = characteristic.properties; - final canNotify = - properties.contains(GattCharacteristicProperty.notify) || - properties.contains(GattCharacteristicProperty.indicate); - if (canNotify) { - // CLIENT_CHARACTERISTIC_CONFIG - final cccDescriptor = MyGattDescriptor( - uuid: UUID.short(0x2902), - value: Uint8List.fromList([0x00, 0x00]), - ); - final cccDescriptorArgs = cccDescriptor.toArgs(); - descriptorsArgs.add(cccDescriptorArgs); - descriptors[cccDescriptorArgs.hashCodeArgs] = cccDescriptor; - } - for (var descriptor in characteristic.descriptors) { - final descriptorArgs = descriptor.toArgs(); - descriptorsArgs.add(descriptorArgs); - descriptors[descriptorArgs.hashCodeArgs] = descriptor; - } - final characteristicArgs = characteristic.toArgs(descriptorsArgs); - characteristicsArgs.add(characteristicArgs); - characteristics[characteristicArgs.hashCodeArgs] = characteristic; - } - final serviceArgs = service.toArgs(characteristicsArgs); + Future authorize() async { + logger.info('authorize'); + final authorized = await _api.authorize(); + return authorized; + } + + @override + Future showAppSettings() async { + logger.info('showAppSettings'); + await _api.showAppSettings(); + } + + @override + Future addService(GATTService service) async { + final serviceArgs = service.toArgs(); logger.info('addService: $serviceArgs'); await _api.addService(serviceArgs); - _characteristics[serviceArgs.hashCodeArgs] = characteristics; - _descriptors[serviceArgs.hashCodeArgs] = descriptors; + _addService(service); + _addServiceArgs(serviceArgs); } @override - Future removeService(GattService service) async { + Future removeService(GATTService service) async { final hashCodeArgs = service.hashCode; logger.info('removeService: $hashCodeArgs'); await _api.removeService(hashCodeArgs); - _characteristics.remove(hashCodeArgs); - _descriptors.remove(hashCodeArgs); + _removeService(service); } @override - Future clearServices() async { - logger.info('clearServices'); - await _api.clearServices(); + Future removeAllServices() async { + logger.info('removeAllServices'); + await _api.removeAllServices(); _characteristics.clear(); _descriptors.clear(); + _cccdCharacteristics.clear(); } @override @@ -145,396 +168,822 @@ class MyPeripheralManager extends PeripheralManager } @override - Future readCharacteristic(GattCharacteristic characteristic) { - if (characteristic is! MyGattCharacteristic) { + Future getMaximumNotifyLength(Central central) { + if (central is! MyCentral) { throw TypeError(); } - final hashCodeArgs = characteristic.hashCode; - logger.info('readCharacteristic: $hashCodeArgs'); - final value = characteristic.value; - return Future.value(value); + final addressArgs = central.address; + final mtu = _mtus[addressArgs] ?? 23; + final maximumNotifyLength = (mtu - 3).clamp(20, 512); + return Future.value(maximumNotifyLength); } @override - Future writeCharacteristic( - GattCharacteristic characteristic, { + Future respondReadRequestWithValue( + GATTReadRequest request, { required Uint8List value, - Central? central, }) async { - if (characteristic is! MyGattCharacteristic) { + if (request is! MyGATTReadRequest) { throw TypeError(); } - characteristic.value = value; - if (central == null) { + final addressArgs = request.addressArgs; + final idArgs = request.idArgs; + const statusArgs = MyGATTStatusArgs.success; + final offsetArgs = request.offset; + final valueArgs = value; + logger.info( + 'sendResponse: $addressArgs - $idArgs, $statusArgs, $offsetArgs, $valueArgs'); + await _api.sendResponse( + addressArgs, + idArgs, + statusArgs, + offsetArgs, + valueArgs, + ); + } + + @override + Future respondReadRequestWithError( + GATTReadRequest request, { + required GATTError error, + }) async { + if (request is! MyGATTReadRequest) { + throw TypeError(); + } + final addressArgs = request.addressArgs; + final idArgs = request.idArgs; + final statusArgs = error.toArgs(); + final offsetArgs = request.offset; + const valueArgs = null; + logger.info( + 'sendResponse: $addressArgs - $idArgs, $statusArgs, $offsetArgs, $valueArgs'); + await _api.sendResponse( + addressArgs, + idArgs, + statusArgs, + offsetArgs, + valueArgs, + ); + } + + @override + Future respondWriteRequest(GATTWriteRequest request) async { + if (request is! MyGATTWriteRequest) { + throw TypeError(); + } + if (!request.responseNeededArgs) { return; } - if (central is! MyCentral2) { + final addressArgs = request.addressArgs; + final idArgs = request.idArgs; + const statusArgs = MyGATTStatusArgs.success; + final offsetArgs = request.offset; + const valueArgs = null; + logger.info( + 'sendResponse: $addressArgs - $idArgs, $statusArgs, $offsetArgs, $valueArgs'); + await _api.sendResponse( + addressArgs, + idArgs, + statusArgs, + offsetArgs, + valueArgs, + ); + } + + @override + Future respondWriteRequestWithError( + GATTWriteRequest request, { + required GATTError error, + }) async { + if (request is! MyGATTWriteRequest) { + throw TypeError(); + } + if (!request.responseNeededArgs) { + return; + } + final addressArgs = request.addressArgs; + final idArgs = request.idArgs; + final statusArgs = error.toArgs(); + final offsetArgs = request.offset; + const valueArgs = null; + logger.info( + 'sendResponse: $addressArgs - $idArgs, $statusArgs, $offsetArgs, $valueArgs'); + await _api.sendResponse( + addressArgs, + idArgs, + statusArgs, + offsetArgs, + valueArgs, + ); + } + + @override + Future notifyCharacteristic( + Central central, + GATTCharacteristic characteristic, { + required Uint8List value, + }) async { + if (central is! MyCentral || characteristic is! MutableGATTCharacteristic) { 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.'); + final cccValue = _retrieveCCCValue(addressArgs, hashCodeArgs); + final notificationDisabled = _valueEquality.equals( + cccValue, + _args.disableNotificationValue, + ); + if (notificationDisabled) { + logger.warning('Notification of this characteristic is disabled.'); 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; - } + final confirmArgs = _valueEquality.equals( + cccValue, + _args.enableIndicationValue, + ); + final valueArgs = value; + logger.info( + 'notifyCharacteristicChanged: $addressArgs - $hashCodeArgs, $confirmArgs, $valueArgs'); + await _api.notifyCharacteristicChanged( + addressArgs, + hashCodeArgs, + confirmArgs, + valueArgs, + ); } @override - void onStateChanged(int stateNumberArgs) { - final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs) async { logger.info('onStateChanged: $stateArgs'); final state = stateArgs.toState(); if (_state == state) { return; } + // Renew GATT server when bluetooth adapter state changed. + switch (state) { + case BluetoothLowEnergyState.poweredOn: + await _openGATTServer(); + break; + case BluetoothLowEnergyState.poweredOff: + await _closeGATTServer(); + break; + default: + break; + } _state = state; final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); _stateChangedController.add(eventArgs); } @override - void onConnectionStateChanged(MyCentralArgs centralArgs, bool stateArgs) { + void onConnectionStateChanged( + MyCentralArgs centralArgs, + int statusArgs, + MyConnectionStateArgs stateArgs, + ) { final addressArgs = centralArgs.addressArgs; - logger.info('onConnectionStateChanged: $addressArgs - $stateArgs'); + logger.info( + 'onConnectionStateChanged: $addressArgs - $statusArgs, $stateArgs'); final central = centralArgs.toCentral(); - final state = stateArgs; - if (state) { - _centrals[addressArgs] = central; + final state = stateArgs.toState(); + if (state == ConnectionState.connected) { + _cccdValues[addressArgs] = {}; } else { - _centrals.remove(addressArgs); _mtus.remove(addressArgs); - _confirms.remove(addressArgs); + _cccdValues.remove(addressArgs); + _preparedHashCodeArgs.remove(addressArgs); + _preparedValue.remove(addressArgs); } + final eventArgs = CentralConnectionStateChangedEventArgs(central, state); + _connnectionStateChangedController.add(eventArgs); } @override - void onMtuChanged(String addressArgs, int mtuArgs) { - logger.info('onMtuChanged: $addressArgs - $mtuArgs'); + void onMTUChanged(MyCentralArgs centralArgs, int mtuArgs) { + final addressArgs = centralArgs.addressArgs; + logger.info('onMTUChanged: $addressArgs - $mtuArgs'); + final central = centralArgs.toCentral(); final mtu = mtuArgs; _mtus[addressArgs] = mtu; + final eventArgs = CentralMTUChangedEventArgs(central, mtu); + _mtuChangedController.add(eventArgs); } @override void onCharacteristicReadRequest( - String addressArgs, - int hashCodeArgs, + MyCentralArgs centralArgs, int idArgs, int offsetArgs, + int hashCodeArgs, ) async { + final addressArgs = centralArgs.addressArgs; logger.info( - 'onCharacteristicReadRequest: $addressArgs.$hashCodeArgs - $idArgs, $offsetArgs'); - final central = _centrals[addressArgs]; - if (central == null) { - return; - } - final characteristic = _retrieveCharacteristic(hashCodeArgs); + 'onCharacteristicReadRequest: $addressArgs - $idArgs, $offsetArgs, $hashCodeArgs'); + final central = centralArgs.toCentral(); + final characteristic = _characteristics[hashCodeArgs]; if (characteristic == null) { - return; + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.failure, + offsetArgs, + null, + ); + } else if (characteristic is ImmutableGATTCharacteristic) { + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.success, + offsetArgs, + characteristic.value.sublist(offsetArgs), + ); + } else { + final eventArgs = GATTCharacteristicReadRequestedEventArgs( + central, + characteristic, + MyGATTReadRequest( + addressArgs: addressArgs, + idArgs: idArgs, + offset: offsetArgs, + ), + ); + _characteristicReadRequestedController.add(eventArgs); } - 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, + MyCentralArgs centralArgs, int idArgs, - int offsetArgs, - Uint8List valueArgs, + int hashCodeArgs, bool preparedWriteArgs, bool responseNeededArgs, + int offsetArgs, + Uint8List valueArgs, ) async { + final addressArgs = centralArgs.addressArgs; logger.info( - 'onCharacteristicWriteRequest: $addressArgs.$hashCodeArgs - $idArgs, $offsetArgs, $valueArgs, $preparedWriteArgs, $responseNeededArgs'); - final central = _centrals[addressArgs]; - if (central == null) { - return; - } - final characteristic = _retrieveCharacteristic(hashCodeArgs); + 'onCharacteristicWriteRequest: $addressArgs - $idArgs, $hashCodeArgs, $preparedWriteArgs, $responseNeededArgs, $offsetArgs, $valueArgs'); + final central = centralArgs.toCentral(); + final characteristic = _characteristics[hashCodeArgs]; if (characteristic == null) { - return; - } - final MyGattStatusArgs statusArgs; - if (preparedWriteArgs) { - 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; + if (!responseNeededArgs) { + return; } - } else { - final value = valueArgs; - _onCharacteristicWritten(central, characteristic, value); - statusArgs = MyGattStatusArgs.success; - } - if (responseNeededArgs) { - await _trySendResponse( + await _sendResponse( addressArgs, idArgs, - statusArgs, + MyGATTStatusArgs.failure, + offsetArgs, + null, + ); + } else if (preparedWriteArgs) { + final preparedHashCodeArgs = _preparedHashCodeArgs[addressArgs]; + if (preparedHashCodeArgs != null && + preparedHashCodeArgs != hashCodeArgs) { + if (!responseNeededArgs) { + return; + } + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.connectionCongested, + offsetArgs, + null, + ); + } else { + if (preparedHashCodeArgs == null) { + _preparedHashCodeArgs[addressArgs] = hashCodeArgs; + // Change the immutable Uint8List to mutable. + _preparedValue[addressArgs] = [...valueArgs]; + } else { + final preparedValue = _preparedValue[addressArgs]; + // Here the prepared value must not be null. + if (preparedValue == null) { + throw ArgumentError.notNull(); + } + preparedValue.insertAll(offsetArgs, valueArgs); + } + if (!responseNeededArgs) { + return; + } + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.success, + offsetArgs, + null, + ); + } + } else if (characteristic is ImmutableGATTCharacteristic) { + if (!responseNeededArgs) { + return; + } + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.writeNotPermitted, offsetArgs, 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 = GATTCharacteristicWriteRequestedEventArgs( + central, + characteristic, + MyGATTWriteRequest( + addressArgs: addressArgs, + idArgs: idArgs, + responseNeededArgs: responseNeededArgs, + offset: offsetArgs, + value: valueArgs, + ), + ); + _characteristicWriteRequestedController.add(eventArgs); } - final eventArgs = GattCharacteristicNotifyStateChangedEventArgs( - central, - characteristic, - state, - ); - _characteristicNotifyStateChangedController.add(eventArgs); } @override void onDescriptorReadRequest( - String addressArgs, - int hashCodeArgs, + MyCentralArgs centralArgs, int idArgs, int offsetArgs, + int hashCodeArgs, ) async { + final addressArgs = centralArgs.addressArgs; logger.info( - 'onDescriptorReadRequest: $addressArgs.$hashCodeArgs - $idArgs, $offsetArgs'); - final central = _centrals[addressArgs]; - if (central == null) { - return; + 'onDescriptorReadRequest: $addressArgs - $idArgs, $offsetArgs, $hashCodeArgs'); + final central = centralArgs.toCentral(); + final characteristic = _cccdCharacteristics[hashCodeArgs]; + if (characteristic == null) { + final descriptor = _descriptors[hashCodeArgs]; + if (descriptor == null) { + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.failure, + offsetArgs, + null, + ); + } else if (descriptor is ImmutableGATTDescriptor) { + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.success, + offsetArgs, + descriptor.value.sublist(offsetArgs), + ); + } else { + final eventArgs = GATTDescriptorReadRequestedEventArgs( + central, + descriptor, + MyGATTReadRequest( + addressArgs: addressArgs, + idArgs: idArgs, + offset: offsetArgs, + ), + ); + _descriptorReadRequestedController.add(eventArgs); + } + } else { + final hashCodeArgs = characteristic.hashCode; + final cccValue = _retrieveCCCValue(addressArgs, hashCodeArgs); + final valueArgs = cccValue.sublist(offsetArgs); + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.success, + offsetArgs, + valueArgs, + ); } - final descriptor = _retrieveDescriptor(hashCodeArgs); - if (descriptor == null) { - return; - } - const statusArgs = MyGattStatusArgs.success; - final offset = offsetArgs; - final valueArgs = descriptor.value.sublist(offset); - await _trySendResponse( - addressArgs, - idArgs, - statusArgs, - offsetArgs, - valueArgs, - ); } @override void onDescriptorWriteRequest( - String addressArgs, - int hashCodeArgs, + MyCentralArgs centralArgs, int idArgs, - int offsetArgs, - Uint8List valueArgs, + int hashCodeArgs, bool preparedWriteArgs, bool responseNeededArgs, + int offsetArgs, + Uint8List valueArgs, ) async { + final addressArgs = centralArgs.addressArgs; logger.info( - 'onDescriptorWriteRequest: $addressArgs.$hashCodeArgs - $idArgs, $offsetArgs, $valueArgs, $preparedWriteArgs, $responseNeededArgs'); - final central = _centrals[addressArgs]; - if (central == null) { - return; - } - final descriptor = _retrieveDescriptor(hashCodeArgs); - if (descriptor == null) { - return; - } - final MyGattStatusArgs statusArgs; + 'onDescriptorWriteRequest: $addressArgs - $idArgs, $hashCodeArgs, $preparedWriteArgs, $responseNeededArgs, $offsetArgs, $valueArgs'); + final central = centralArgs.toCentral(); if (preparedWriteArgs) { - final preparedDescriptor = _preparedCharacteristics[addressArgs]; - if (preparedDescriptor != null && preparedDescriptor != descriptor) { - statusArgs = MyGattStatusArgs.connectionCongested; + final preparedHashCodeArgs = _preparedHashCodeArgs[addressArgs]; + if (preparedHashCodeArgs != null && + preparedHashCodeArgs != hashCodeArgs) { + if (!responseNeededArgs) { + return; + } + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.connectionCongested, + offsetArgs, + null, + ); } else { - final preparedValueArgs = _preparedValue[addressArgs]; - if (preparedValueArgs == null) { - _preparedDescriptors[addressArgs] = descriptor; + if (preparedHashCodeArgs == null) { + _preparedHashCodeArgs[addressArgs] = hashCodeArgs; // Change the immutable Uint8List to mutable. _preparedValue[addressArgs] = [...valueArgs]; } else { - preparedValueArgs.insertAll(offsetArgs, valueArgs); + final preparedValue = _preparedValue[addressArgs]; + // Here the prepared value must not be null. + if (preparedValue == null) { + throw ArgumentError.notNull(); + } + preparedValue.insertAll(offsetArgs, valueArgs); } - statusArgs = MyGattStatusArgs.success; + if (!responseNeededArgs) { + return; + } + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.success, + offsetArgs, + null, + ); } } else { - descriptor.value = valueArgs; - statusArgs = MyGattStatusArgs.success; + final characteristic = _cccdCharacteristics[hashCodeArgs]; + if (characteristic == null) { + final descriptor = _descriptors[hashCodeArgs]; + if (descriptor == null) { + if (!responseNeededArgs) { + return; + } + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.failure, + offsetArgs, + null, + ); + } else if (descriptor is ImmutableGATTDescriptor) { + if (!responseNeededArgs) { + return; + } + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.writeNotPermitted, + offsetArgs, + null, + ); + } else { + final eventArgs = GATTDescriptorWriteRequestedEventArgs( + central, + descriptor, + MyGATTWriteRequest( + addressArgs: addressArgs, + idArgs: idArgs, + responseNeededArgs: responseNeededArgs, + offset: offsetArgs, + value: valueArgs, + ), + ); + _descriptorWriteRequestedController.add(eventArgs); + } + } else { + final notificationDisabled = _valueEquality.equals( + valueArgs, + _args.disableNotificationValue, + ); + final notificationEnabled = _valueEquality.equals( + valueArgs, + _args.enableNotificationValue, + ); + final indicationEnabled = _valueEquality.equals( + valueArgs, + _args.enableIndicationValue, + ); + if (!notificationDisabled && + !notificationEnabled && + !indicationEnabled) { + if (!responseNeededArgs) { + return; + } + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.requestNotSupported, + offsetArgs, + null, + ); + } else { + final values = _cccdValues[addressArgs]; + // Here the CCC values must not be null. + if (values == null) { + throw ArgumentError.notNull(); + } + final hashCodeArgs = characteristic.hashCode; + final state = notificationEnabled || indicationEnabled; + if (state) { + values[hashCodeArgs] = valueArgs; + } else { + values.remove(hashCodeArgs); + } + final eventArgs = GATTCharacteristicNotifyStateChangedEventArgs( + central, + characteristic, + state, + ); + _characteristicNotifyStateChangedController.add(eventArgs); + if (!responseNeededArgs) { + return; + } + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.success, + offsetArgs, + null, + ); + } + } } - if (responseNeededArgs) { - await _trySendResponse( + } + + @override + void onExecuteWrite( + MyCentralArgs centralArgs, + int idArgs, + bool executeArgs, + ) async { + final addressArgs = centralArgs.addressArgs; + logger.info('onExecuteWrite: $addressArgs - $idArgs, $executeArgs'); + final central = centralArgs.toCentral(); + final hashCodeArgs = _preparedHashCodeArgs.remove(addressArgs); + final elements = _preparedValue.remove(addressArgs); + if (hashCodeArgs == null || elements == null) { + await _sendResponse( addressArgs, idArgs, - statusArgs, - offsetArgs, + MyGATTStatusArgs.failure, + 0, + null, + ); + } else if (executeArgs) { + final characteristic = _characteristics[hashCodeArgs]; + final descriptor = _descriptors[hashCodeArgs]; + final cccCharacteristic = _cccdCharacteristics[hashCodeArgs]; + final value = Uint8List.fromList(elements); + if (characteristic != null && + descriptor == null && + cccCharacteristic == null) { + // Characteristic write request. + if (characteristic is ImmutableGATTCharacteristic) { + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.writeNotPermitted, + 0, + null, + ); + } else { + final eventArgs = GATTCharacteristicWriteRequestedEventArgs( + central, + characteristic, + MyGATTWriteRequest( + addressArgs: addressArgs, + idArgs: idArgs, + responseNeededArgs: true, + offset: 0, + value: value, + ), + ); + _characteristicWriteRequestedController.add(eventArgs); + } + } else if (descriptor != null && + characteristic == null && + cccCharacteristic == null) { + // Descriptor write request. + if (descriptor is ImmutableGATTDescriptor) { + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.writeNotPermitted, + 0, + null, + ); + } else { + final eventArgs = GATTDescriptorWriteRequestedEventArgs( + central, + descriptor, + MyGATTWriteRequest( + addressArgs: addressArgs, + idArgs: idArgs, + responseNeededArgs: true, + offset: 0, + value: value, + ), + ); + _descriptorWriteRequestedController.add(eventArgs); + } + } else if (cccCharacteristic != null && + characteristic == null && + descriptor == null) { + // CCC descriptor write request. + final notificationDisabled = _valueEquality.equals( + value, + _args.disableNotificationValue, + ); + final notificationEnabled = _valueEquality.equals( + value, + _args.enableNotificationValue, + ); + final indicationEnabled = _valueEquality.equals( + value, + _args.enableIndicationValue, + ); + if (!notificationDisabled && + !notificationEnabled && + !indicationEnabled) { + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.requestNotSupported, + 0, + null, + ); + } else { + final values = _cccdValues[addressArgs]; + // Here the CCC values must not be null. + if (values == null) { + throw ArgumentError.notNull(); + } + final hashCodeArgs = cccCharacteristic.hashCode; + final state = notificationEnabled || indicationEnabled; + if (state) { + values[hashCodeArgs] = value; + } else { + values.remove(hashCodeArgs); + } + final eventArgs = GATTCharacteristicNotifyStateChangedEventArgs( + central, + cccCharacteristic, + state, + ); + _characteristicNotifyStateChangedController.add(eventArgs); + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.success, + 0, + null, + ); + } + } else { + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.failure, + 0, + null, + ); + } + } else { + await _sendResponse( + addressArgs, + idArgs, + MyGATTStatusArgs.success, + 0, 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 descriptor = _preparedDescriptors.remove(addressArgs); - final elements = _preparedValue.remove(addressArgs); - if (central == null || elements == null) { - return; + void _addService(GATTService service) { + for (var includedService in service.includedServices) { + _addService(includedService); } - final value = Uint8List.fromList(elements); - final execute = executeArgs; - if (execute) { - if (characteristic == null && descriptor == null) { - return; - } - if (characteristic != null) { - _onCharacteristicWritten(central, characteristic, value); - } - if (descriptor != null) { - descriptor.value = value; + final characteristics = + service.characteristics.cast(); + for (var characteristic in characteristics) { + final descriptors = + characteristic.descriptors.cast(); + for (var descriptor in descriptors) { + // Jump CCC descriptors. + if (descriptor.uuid == cccUUID) { + continue; + } + _descriptors[descriptor.hashCode] = descriptor; } + _characteristics[characteristic.hashCode] = characteristic; } - await _trySendResponse( - addressArgs, - idArgs, - MyGattStatusArgs.success, - 0, - null, - ); } - MyGattCharacteristic? _retrieveCharacteristic(int hashCodeArgs) { - final characteristics = _characteristics.values - .reduce((value, element) => value..addAll(element)); - return characteristics[hashCodeArgs]; - } - - MyGattDescriptor? _retrieveDescriptor(int hashCodeArgs) { - final descriptors = - _descriptors.values.reduce((value, element) => value..addAll(element)); - return descriptors[hashCodeArgs]; - } - - bool? _retrieveConfirm(String addressArgs, int hashCodeArgs) { - final confirms = _confirms[addressArgs]; - if (confirms == null) { - return null; + void _addServiceArgs(MyMutableGATTServiceArgs serviceArgs) { + final includedServicesArgs = + serviceArgs.includedServicesArgs.cast(); + for (var includedServiceArgs in includedServicesArgs) { + _addServiceArgs(includedServiceArgs); + } + final characteristicsArgs = + serviceArgs.characteristicsArgs.cast(); + final cccUUIDArgs = cccUUID.toArgs(); + for (var characteristicArgs in characteristicsArgs) { + final characteristic = _characteristics[characteristicArgs.hashCodeArgs]; + if (characteristic == null) { + throw ArgumentError.notNull(); + } + final cccDescriptorArgs = characteristicArgs.descriptorsArgs + .cast() + .firstWhere((args) => args.uuidArgs == cccUUIDArgs); + _cccdCharacteristics[cccDescriptorArgs.hashCodeArgs] = characteristic; } - return confirms[hashCodeArgs]; } - Future _trySendResponse( + void _removeService(GATTService service) { + for (var includedService in service.includedServices) { + _removeService(includedService); + } + for (var characteristic in service.characteristics) { + for (var descriptor in characteristic.descriptors) { + final hashCodeArgs = descriptor.hashCode; + _descriptors.remove(hashCodeArgs); + _cccdCharacteristics.remove(hashCodeArgs); + } + final hashCodeArgs = characteristic.hashCode; + _characteristics.remove(hashCodeArgs); + } + } + + Uint8List _retrieveCCCValue(String addressArgs, int hashCodeArgs) { + final values = _cccdValues[addressArgs]; + if (values == null) { + throw ArgumentError.notNull(); + } + final value = values[hashCodeArgs]; + return value ?? _args.disableNotificationValue; + } + + Future _initialize() async { + // Here we use `Future()` to make it possible to change the `logLevel` before `initialize()`. + await Future(() async { + try { + logger.info('initialize'); + _args = await _api.initialize(); + _getState(); + } catch (e) { + logger.severe('initialize failed.', e); + } + }); + } + + Future _getState() async { + try { + logger.info('getState'); + final stateArgs = await _api.getState(); + onStateChanged(stateArgs); + } catch (e) { + logger.severe('getState failed.', e); + } + } + + Future _openGATTServer() async { + try { + logger.info('openGATTServer'); + await _api.openGATTServer(); + } catch (e) { + logger.severe('openGATTServer failed.', e); + } + } + + Future _closeGATTServer() async { + try { + logger.info('closeGATTServer'); + await _api.closeGATTServer(); + } catch (e) { + logger.severe('closeGATTServer failed.', e); + } + } + + Future _sendResponse( String addressArgs, int idArgs, - MyGattStatusArgs statusArgs, + MyGATTStatusArgs statusArgs, int offsetArgs, Uint8List? valueArgs, ) async { - final statusNumberArgs = statusArgs.index; try { - _api.sendResponse( + logger.info( + 'sendResponse: $addressArgs - $idArgs, $statusArgs, $offsetArgs, $valueArgs'); + await _api.sendResponse( addressArgs, idArgs, - statusNumberArgs, + statusArgs, offsetArgs, valueArgs, ); - } catch (e, stack) { - logger.shout('Send response failed.', e, stack); + } catch (e) { + logger.severe('sendResponse failed.', e); } } - - 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 { - characteristic.value = value; - final trimmedValue = characteristic.value; - final eventArgs = GattCharacteristicWrittenEventArgs( - central, - characteristic, - trimmedValue, - ); - _characteristicWrittenController.add(eventArgs); - } } diff --git a/bluetooth_low_energy_android/my_api.dart b/bluetooth_low_energy_android/my_api.dart index f3ebee6..cb00b4b 100644 --- a/bluetooth_low_energy_android/my_api.dart +++ b/bluetooth_low_energy_android/my_api.dart @@ -1,13 +1,16 @@ +// Run with `dart run pigeon --input my_api.dart`. import 'package:pigeon/pigeon.dart'; +// TODO: Use `@ProxyApi` to manage instancs when this feature released: +// https://github.com/flutter/flutter/issues/147486 @ConfigurePigeon( PigeonOptions( dartOut: 'lib/src/my_api.g.dart', dartOptions: DartOptions(), kotlinOut: - 'android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy_android/MyApi.g.kt', + 'android/src/main/kotlin/dev/hebei/bluetooth_low_energy_android/MyAPI.g.kt', kotlinOptions: KotlinOptions( - package: 'dev.yanshouwang.bluetooth_low_energy_android', + package: 'dev.hebei.bluetooth_low_energy_android', ), ), ) @@ -21,7 +24,14 @@ enum MyBluetoothLowEnergyStateArgs { turningOff, } -enum MyGattCharacteristicPropertyArgs { +enum MyConnectionStateArgs { + disconnected, + connecting, + connected, + disconnecting, +} + +enum MyGATTCharacteristicPropertyArgs { read, write, writeWithoutResponse, @@ -29,30 +39,56 @@ enum MyGattCharacteristicPropertyArgs { indicate, } -enum MyGattCharacteristicWriteTypeArgs { +enum MyGATTCharacteristicPermissionArgs { + read, + readEncrypted, + write, + writeEncrypted, +} + +enum MyGATTCharacteristicWriteTypeArgs { withResponse, withoutResponse, } -enum MyGattCharacteristicNotifyStateArgs { - none, - notify, - indicate, -} - -enum MyGattStatusArgs { +enum MyGATTStatusArgs { success, readNotPermitted, writeNotPermitted, - requestNotSupported, - invalidOffset, insufficientAuthentication, + requestNotSupported, insufficientEncryption, + invalidOffset, + insufficientAuthorization, invalidAttributeLength, connectionCongested, failure, } +class MyCentralManagerArgs { + final Uint8List enableNotificationValue; + final Uint8List enableIndicationValue; + final Uint8List disableNotificationValue; + + MyCentralManagerArgs( + this.enableNotificationValue, + this.enableIndicationValue, + this.disableNotificationValue, + ); +} + +class MyPeripheralManagerArgs { + final Uint8List enableNotificationValue; + final Uint8List enableIndicationValue; + final Uint8List disableNotificationValue; + + MyPeripheralManagerArgs( + this.enableNotificationValue, + this.enableIndicationValue, + this.disableNotificationValue, + ); +} + class MyManufacturerSpecificDataArgs { final int idArgs; final Uint8List dataArgs; @@ -64,7 +100,7 @@ class MyAdvertisementArgs { final String? nameArgs; final List serviceUUIDsArgs; final Map serviceDataArgs; - final MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs; + final List manufacturerSpecificDataArgs; MyAdvertisementArgs( this.nameArgs, @@ -86,25 +122,23 @@ class MyPeripheralArgs { MyPeripheralArgs(this.addressArgs); } -class MyGattDescriptorArgs { +class MyGATTDescriptorArgs { final int hashCodeArgs; final String uuidArgs; - final Uint8List? valueArgs; - MyGattDescriptorArgs( + MyGATTDescriptorArgs( this.hashCodeArgs, this.uuidArgs, - this.valueArgs, ); } -class MyGattCharacteristicArgs { +class MyGATTCharacteristicArgs { final int hashCodeArgs; final String uuidArgs; final List propertyNumbersArgs; - final List descriptorsArgs; + final List descriptorsArgs; - MyGattCharacteristicArgs( + MyGATTCharacteristicArgs( this.hashCodeArgs, this.uuidArgs, this.propertyNumbersArgs, @@ -112,35 +146,88 @@ class MyGattCharacteristicArgs { ); } -class MyGattServiceArgs { +class MyGATTServiceArgs { final int hashCodeArgs; final String uuidArgs; - final List characteristicsArgs; + final bool isPrimaryArgs; + final List includedServicesArgs; + final List characteristicsArgs; - MyGattServiceArgs( + MyGATTServiceArgs( this.hashCodeArgs, this.uuidArgs, + this.isPrimaryArgs, + this.includedServicesArgs, + this.characteristicsArgs, + ); +} + +class MyMutableGATTDescriptorArgs { + final int hashCodeArgs; + final String uuidArgs; + final List permissionNumbersArgs; + + MyMutableGATTDescriptorArgs( + this.hashCodeArgs, + this.uuidArgs, + this.permissionNumbersArgs, + ); +} + +class MyMutableGATTCharacteristicArgs { + final int hashCodeArgs; + final String uuidArgs; + final List permissionNumbersArgs; + final List propertyNumbersArgs; + final List descriptorsArgs; + + MyMutableGATTCharacteristicArgs( + this.hashCodeArgs, + this.uuidArgs, + this.permissionNumbersArgs, + this.propertyNumbersArgs, + this.descriptorsArgs, + ); +} + +class MyMutableGATTServiceArgs { + final int hashCodeArgs; + final String uuidArgs; + final bool isPrimaryArgs; + final List includedServicesArgs; + final List characteristicsArgs; + + MyMutableGATTServiceArgs( + this.hashCodeArgs, + this.uuidArgs, + this.isPrimaryArgs, + this.includedServicesArgs, this.characteristicsArgs, ); } @HostApi() -abstract class MyCentralManagerHostApi { +abstract class MyCentralManagerHostAPI { + MyCentralManagerArgs initialize(); + MyBluetoothLowEnergyStateArgs getState(); @async - void setUp(); + bool authorize(); @async - void startDiscovery(); + void showAppSettings(); + @async + void startDiscovery(List serviceUUIDsArgs); void stopDiscovery(); @async void connect(String addressArgs); @async void disconnect(String addressArgs); + List retrieveConnectedPeripherals(); @async int requestMTU(String addressArgs, int mtuArgs); @async int readRSSI(String addressArgs); @async - List discoverServices(String addressArgs); + List discoverGATT(String addressArgs); @async Uint8List readCharacteristic(String addressArgs, int hashCodeArgs); @async @@ -148,13 +235,12 @@ abstract class MyCentralManagerHostApi { String addressArgs, int hashCodeArgs, Uint8List valueArgs, - int typeNumberArgs, + MyGATTCharacteristicWriteTypeArgs typeArgs, ); - @async - void setCharacteristicNotifyState( + void setCharacteristicNotification( String addressArgs, int hashCodeArgs, - int stateNumberArgs, + bool enableArgs, ); @async Uint8List readDescriptor(String addressArgs, int hashCodeArgs); @@ -167,91 +253,99 @@ abstract class MyCentralManagerHostApi { } @FlutterApi() -abstract class MyCentralManagerFlutterApi { - void onStateChanged(int stateNumberArgs); +abstract class MyCentralManagerFlutterAPI { + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); void onDiscovered( MyPeripheralArgs peripheralArgs, int rssiArgs, MyAdvertisementArgs advertisementArgs, ); - void onConnectionStateChanged(String addressArgs, bool stateArgs); - void onMtuChanged(String addressArgs, int mtuArgs); + void onConnectionStateChanged( + MyPeripheralArgs peripheralArgs, + MyConnectionStateArgs stateArgs, + ); + void onMTUChanged(MyPeripheralArgs peripheralArgs, int mtuArgs); void onCharacteristicNotified( - String addressArgs, - int hashCodeArgs, + MyPeripheralArgs peripheralArgs, + MyGATTCharacteristicArgs characteristicArgs, Uint8List valueArgs, ); } @HostApi() -abstract class MyPeripheralManagerHostApi { +abstract class MyPeripheralManagerHostAPI { + MyPeripheralManagerArgs initialize(); + MyBluetoothLowEnergyStateArgs getState(); @async - void setUp(); + bool authorize(); @async - void addService(MyGattServiceArgs serviceArgs); + void showAppSettings(); + void openGATTServer(); + void closeGATTServer(); + @async + void addService(MyMutableGATTServiceArgs serviceArgs); void removeService(int hashCodeArgs); - void clearServices(); + void removeAllServices(); @async void startAdvertising(MyAdvertisementArgs advertisementArgs); void stopAdvertising(); void sendResponse( String addressArgs, int idArgs, - int statusNumberArgs, + MyGATTStatusArgs statusArgs, int offsetArgs, Uint8List? valueArgs, ); @async void notifyCharacteristicChanged( - int hashCodeArgs, - Uint8List valueArgs, - bool confirmArgs, String addressArgs, + int hashCodeArgs, + bool confirmArgs, + Uint8List valueArgs, ); } @FlutterApi() -abstract class MyPeripheralManagerFlutterApi { - void onStateChanged(int stateNumberArgs); - void onConnectionStateChanged(MyCentralArgs centralArgs, bool stateArgs); - void onMtuChanged(String addressArgs, int mtuArgs); +abstract class MyPeripheralManagerFlutterAPI { + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); + void onConnectionStateChanged( + MyCentralArgs centralArgs, + int statusArgs, + MyConnectionStateArgs stateArgs, + ); + void onMTUChanged(MyCentralArgs centralArgs, int mtuArgs); void onCharacteristicReadRequest( - String addressArgs, - int hashCodeArgs, + MyCentralArgs centralArgs, int idArgs, int offsetArgs, + int hashCodeArgs, ); void onCharacteristicWriteRequest( - String addressArgs, - int hashCodeArgs, + MyCentralArgs centralArgs, int idArgs, - int offsetArgs, - Uint8List valueArgs, + int hashCodeArgs, bool preparedWriteArgs, bool responseNeededArgs, - ); - void onCharacteristicNotifyStateChanged( - String addressArgs, - int hashCodeArgs, - int stateNumberArgs, + int offsetArgs, + Uint8List valueArgs, ); void onDescriptorReadRequest( - String addressArgs, - int hashCodeArgs, + MyCentralArgs centralArgs, int idArgs, int offsetArgs, + int hashCodeArgs, ); void onDescriptorWriteRequest( - String addressArgs, - int hashCodeArgs, + MyCentralArgs centralArgs, int idArgs, - int offsetArgs, - Uint8List valueArgs, + int hashCodeArgs, bool preparedWriteArgs, bool responseNeededArgs, + int offsetArgs, + Uint8List valueArgs, ); void onExecuteWrite( - String addressArgs, + MyCentralArgs centralArgs, int idArgs, bool executeArgs, ); diff --git a/bluetooth_low_energy_android/pubspec.yaml b/bluetooth_low_energy_android/pubspec.yaml index f7dd66f..86c758f 100644 --- a/bluetooth_low_energy_android/pubspec.yaml +++ b/bluetooth_low_energy_android/pubspec.yaml @@ -1,28 +1,38 @@ name: bluetooth_low_energy_android -description: Android implementation of the bluetooth_low_energy plugin. -version: 5.0.5 +description: "Android implementation of the bluetooth_low_energy plugin." +version: 6.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy +repository: https://github.com/yanshouwang/bluetooth_low_energy +issue_tracker: https://github.com/yanshouwang/bluetooth_low_energy/issues +topics: + - bluetooth + - bluetooth-low-energy + - ble +funding: + - https://paypal.me/yanshouwang5112 + - https://afdian.net/a/yanshouwang environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.0.0" + sdk: '>=3.0.0 <4.0.0' + flutter: '>=3.0.0' dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^5.0.2 + bluetooth_low_energy_platform_interface: ^6.0.0 + collection: ^1.18.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.0 - pigeon: ^15.0.2 + flutter_lints: ^4.0.0 + pigeon: ^19.0.0 flutter: plugin: implements: bluetooth_low_energy platforms: android: - package: dev.yanshouwang.bluetooth_low_energy_android - pluginClass: BluetoothLowEnergyAndroid - dartPluginClass: BluetoothLowEnergyAndroid + package: dev.hebei.bluetooth_low_energy_android + pluginClass: BluetoothLowEnergyAndroidPlugin + dartPluginClass: BluetoothLowEnergyAndroidPlugin diff --git a/bluetooth_low_energy_android/test/bluetooth_low_energy_android_test.dart b/bluetooth_low_energy_android/test/bluetooth_low_energy_android_test.dart new file mode 100644 index 0000000..ab73b3a --- /dev/null +++ b/bluetooth_low_energy_android/test/bluetooth_low_energy_android_test.dart @@ -0,0 +1 @@ +void main() {} diff --git a/bluetooth_low_energy_darwin/.gitignore b/bluetooth_low_energy_darwin/.gitignore index 96486fd..ac5aa98 100644 --- a/bluetooth_low_energy_darwin/.gitignore +++ b/bluetooth_low_energy_darwin/.gitignore @@ -26,5 +26,4 @@ migrate_working_dir/ /pubspec.lock **/doc/api/ .dart_tool/ -.packages build/ diff --git a/bluetooth_low_energy_darwin/.metadata b/bluetooth_low_energy_darwin/.metadata index fe217dd..1ed6bc0 100644 --- a/bluetooth_low_energy_darwin/.metadata +++ b/bluetooth_low_energy_darwin/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "efbf63d9c66b9f6ec30e9ad4611189aa80003d31" + revision: "5dcb86f68f239346676ceb1ed1ea385bd215fba1" channel: "stable" project_type: plugin @@ -13,14 +13,14 @@ project_type: plugin migration: platforms: - platform: root - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 - platform: ios - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 - platform: macos - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 # User provided section diff --git a/bluetooth_low_energy_darwin/CHANGELOG.md b/bluetooth_low_energy_darwin/CHANGELOG.md index 85f4e2d..c754fa1 100644 --- a/bluetooth_low_energy_darwin/CHANGELOG.md +++ b/bluetooth_low_energy_darwin/CHANGELOG.md @@ -1,3 +1,38 @@ +## 6.0.0 + +* Add `serviceUUIDs` argument to `CentralManager#startDiscovery` method. +* Add `CentralManager#retrieveConnectedPeripherals` method. +* Move `BluetoothLowEnergyManager#getState` to `BluetoothLowEnergyManager#state`. +* Move `CentralManger.instance` to factory constructor. +* Move `PeripheralManager.instance` to factory constructor. +* Remove `BluetoothLowEnergyManager#setUp` method. +* Implement `CentralMananger#showAppSettings` on iOS. +* Implement `PeripheralManager#showAppSettings` on iOS. +* Rewrite example with MVVM. +* Fix known issues. + +## 6.0.0-dev.4 + +* Implement `CentralMananger#showAppSettings` on iOS. +* Implement `PeripheralManager#showAppSettings` on iOS. + +## 6.0.0-dev.3 + +* Rewrite example with MVVM. +* Fix known issues. + +## 6.0.0-dev.2 + +* Implement new APIs. + +## 6.0.0-dev.1 + +* Move organization. + +## 6.0.0-dev.0 + +* Implement new APIs. + ## 5.0.5 * Change flutter minimum version to 3.0.0. diff --git a/bluetooth_low_energy_darwin/LICENSE b/bluetooth_low_energy_darwin/LICENSE index 752d28b..3977562 100644 --- a/bluetooth_low_energy_darwin/LICENSE +++ b/bluetooth_low_energy_darwin/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 yanshouwang +Copyright (c) 2024 hebei.dev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/bluetooth_low_energy_darwin/README.md b/bluetooth_low_energy_darwin/README.md index 6d1bd0d..90cb38e 100644 --- a/bluetooth_low_energy_darwin/README.md +++ b/bluetooth_low_energy_darwin/README.md @@ -1,15 +1,15 @@ # bluetooth_low_energy_darwin -The iOS and macOS implementation of [`bluetooth_low_energy`][1]. +A new Flutter plugin project. -## Usage +## Getting Started -This package is [endorsed][2], which means you can simply use `bluetooth_low_energy` -normally. This package will be automatically included in your app when you do, -so you do not need to add it to your `pubspec.yaml`. +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. -However, if you `import` this package to use any of its APIs directly, you -should add it to your `pubspec.yaml` as usual. +For help getting started with Flutter development, view the +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. -[1]: https://pub.dev/packages/bluetooth_low_energy -[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin diff --git a/bluetooth_low_energy_darwin/darwin/.gitignore b/bluetooth_low_energy_darwin/darwin/.gitignore new file mode 100644 index 0000000..034771f --- /dev/null +++ b/bluetooth_low_energy_darwin/darwin/.gitignore @@ -0,0 +1,38 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh diff --git a/bluetooth_low_energy_darwin/darwin/Assets/.gitkeep b/bluetooth_low_energy_darwin/darwin/Assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/bluetooth_low_energy_darwin/darwin/Classes/BluetoothLowEnergyDarwin.swift b/bluetooth_low_energy_darwin/darwin/Classes/BluetoothLowEnergyDarwinPlugin.swift similarity index 74% rename from bluetooth_low_energy_darwin/darwin/Classes/BluetoothLowEnergyDarwin.swift rename to bluetooth_low_energy_darwin/darwin/Classes/BluetoothLowEnergyDarwinPlugin.swift index 23a4891..1b6d615 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/BluetoothLowEnergyDarwin.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/BluetoothLowEnergyDarwinPlugin.swift @@ -8,7 +8,7 @@ import FlutterMacOS #error("Unsupported platform.") #endif -public class BluetoothLowEnergyDarwin: NSObject, FlutterPlugin { +public class BluetoothLowEnergyDarwinPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { #if os(iOS) let messenger = registrar.messenger() @@ -19,7 +19,7 @@ public class BluetoothLowEnergyDarwin: NSObject, FlutterPlugin { #endif let centralManager = MyCentralManager(messenger: messenger) let peripheralManager = MyPeripheralManager(messenger: messenger) - MyCentralManagerHostApiSetup.setUp(binaryMessenger: messenger, api: centralManager) - MyPeripheralManagerHostApiSetup.setUp(binaryMessenger: messenger, api: peripheralManager) + 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 new file mode 100644 index 0000000..cec82e9 --- /dev/null +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyAPI.g.swift @@ -0,0 +1,1352 @@ +// Autogenerated from Pigeon (v19.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +enum MyBluetoothLowEnergyStateArgs: Int { + case unknown = 0 + case resetting = 1 + case unsupported = 2 + case unauthorized = 3 + case poweredOff = 4 + case poweredOn = 5 +} + +enum MyConnectionStateArgs: Int { + case disconnected = 0 + case connected = 1 +} + +enum MyGATTCharacteristicPropertyArgs: Int { + case read = 0 + case write = 1 + case writeWithoutResponse = 2 + case notify = 3 + case indicate = 4 +} + +enum MyGATTCharacteristicPermissionArgs: Int { + case read = 0 + case readEncrypted = 1 + case write = 2 + case writeEncrypted = 3 +} + +enum MyGATTCharacteristicWriteTypeArgs: Int { + case withResponse = 0 + case withoutResponse = 1 +} + +enum MyATTErrorArgs: 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 MyAdvertisementArgs { + var nameArgs: String? = nil + var serviceUUIDsArgs: [String?] + var serviceDataArgs: [String?: FlutterStandardTypedData?] + var manufacturerSpecificDataArgs: FlutterStandardTypedData? = nil + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ __pigeon_list: [Any?]) -> MyAdvertisementArgs? { + let nameArgs: String? = nilOrValue(__pigeon_list[0]) + let serviceUUIDsArgs = __pigeon_list[1] as! [String?] + let serviceDataArgs = __pigeon_list[2] as! [String?: FlutterStandardTypedData?] + let manufacturerSpecificDataArgs: FlutterStandardTypedData? = nilOrValue(__pigeon_list[3]) + + return MyAdvertisementArgs( + nameArgs: nameArgs, + serviceUUIDsArgs: serviceUUIDsArgs, + serviceDataArgs: serviceDataArgs, + manufacturerSpecificDataArgs: manufacturerSpecificDataArgs + ) + } + func toList() -> [Any?] { + return [ + nameArgs, + serviceUUIDsArgs, + serviceDataArgs, + manufacturerSpecificDataArgs, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct MyCentralArgs { + var uuidArgs: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ __pigeon_list: [Any?]) -> MyCentralArgs? { + let uuidArgs = __pigeon_list[0] as! String + + return MyCentralArgs( + uuidArgs: uuidArgs + ) + } + func toList() -> [Any?] { + return [ + uuidArgs + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct MyPeripheralArgs { + var uuidArgs: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ __pigeon_list: [Any?]) -> MyPeripheralArgs? { + let uuidArgs = __pigeon_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 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ __pigeon_list: [Any?]) -> MyGATTDescriptorArgs? { + let hashCodeArgs = __pigeon_list[0] is Int64 ? __pigeon_list[0] as! Int64 : Int64(__pigeon_list[0] as! Int32) + let uuidArgs = __pigeon_list[1] as! String + + return MyGATTDescriptorArgs( + hashCodeArgs: hashCodeArgs, + uuidArgs: uuidArgs + ) + } + func toList() -> [Any?] { + return [ + hashCodeArgs, + uuidArgs, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct MyGATTCharacteristicArgs { + var hashCodeArgs: Int64 + var uuidArgs: String + var propertyNumbersArgs: [Int64?] + var descriptorsArgs: [MyGATTDescriptorArgs?] + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ __pigeon_list: [Any?]) -> MyGATTCharacteristicArgs? { + let hashCodeArgs = __pigeon_list[0] is Int64 ? __pigeon_list[0] as! Int64 : Int64(__pigeon_list[0] as! Int32) + let uuidArgs = __pigeon_list[1] as! String + let propertyNumbersArgs = __pigeon_list[2] as! [Int64?] + let descriptorsArgs = __pigeon_list[3] as! [MyGATTDescriptorArgs?] + + return MyGATTCharacteristicArgs( + hashCodeArgs: hashCodeArgs, + uuidArgs: uuidArgs, + propertyNumbersArgs: propertyNumbersArgs, + descriptorsArgs: descriptorsArgs + ) + } + func toList() -> [Any?] { + return [ + hashCodeArgs, + uuidArgs, + propertyNumbersArgs, + descriptorsArgs, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct MyGATTServiceArgs { + var hashCodeArgs: Int64 + var uuidArgs: String + var isPrimaryArgs: Bool + var includedServicesArgs: [MyGATTServiceArgs?] + var characteristicsArgs: [MyGATTCharacteristicArgs?] + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ __pigeon_list: [Any?]) -> MyGATTServiceArgs? { + let hashCodeArgs = __pigeon_list[0] is Int64 ? __pigeon_list[0] as! Int64 : Int64(__pigeon_list[0] as! Int32) + let uuidArgs = __pigeon_list[1] as! String + let isPrimaryArgs = __pigeon_list[2] as! Bool + let includedServicesArgs = __pigeon_list[3] as! [MyGATTServiceArgs?] + let characteristicsArgs = __pigeon_list[4] as! [MyGATTCharacteristicArgs?] + + return MyGATTServiceArgs( + hashCodeArgs: hashCodeArgs, + uuidArgs: uuidArgs, + isPrimaryArgs: isPrimaryArgs, + includedServicesArgs: includedServicesArgs, + characteristicsArgs: characteristicsArgs + ) + } + func toList() -> [Any?] { + return [ + hashCodeArgs, + uuidArgs, + isPrimaryArgs, + includedServicesArgs, + characteristicsArgs, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct MyMutableGATTDescriptorArgs { + var hashCodeArgs: Int64 + var uuidArgs: String + var valueArgs: FlutterStandardTypedData? = nil + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ __pigeon_list: [Any?]) -> MyMutableGATTDescriptorArgs? { + let hashCodeArgs = __pigeon_list[0] is Int64 ? __pigeon_list[0] as! Int64 : Int64(__pigeon_list[0] as! Int32) + let uuidArgs = __pigeon_list[1] as! String + let valueArgs: FlutterStandardTypedData? = nilOrValue(__pigeon_list[2]) + + return MyMutableGATTDescriptorArgs( + hashCodeArgs: hashCodeArgs, + uuidArgs: uuidArgs, + valueArgs: valueArgs + ) + } + func toList() -> [Any?] { + return [ + hashCodeArgs, + uuidArgs, + valueArgs, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct MyMutableGATTCharacteristicArgs { + var hashCodeArgs: Int64 + var uuidArgs: String + var propertyNumbersArgs: [Int64?] + var permissionNumbersArgs: [Int64?] + var valueArgs: FlutterStandardTypedData? = nil + var descriptorsArgs: [MyMutableGATTDescriptorArgs?] + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ __pigeon_list: [Any?]) -> MyMutableGATTCharacteristicArgs? { + let hashCodeArgs = __pigeon_list[0] is Int64 ? __pigeon_list[0] as! Int64 : Int64(__pigeon_list[0] as! Int32) + let uuidArgs = __pigeon_list[1] as! String + let propertyNumbersArgs = __pigeon_list[2] as! [Int64?] + let permissionNumbersArgs = __pigeon_list[3] as! [Int64?] + let valueArgs: FlutterStandardTypedData? = nilOrValue(__pigeon_list[4]) + let descriptorsArgs = __pigeon_list[5] as! [MyMutableGATTDescriptorArgs?] + + return MyMutableGATTCharacteristicArgs( + hashCodeArgs: hashCodeArgs, + uuidArgs: uuidArgs, + propertyNumbersArgs: propertyNumbersArgs, + permissionNumbersArgs: permissionNumbersArgs, + valueArgs: valueArgs, + descriptorsArgs: descriptorsArgs + ) + } + func toList() -> [Any?] { + return [ + hashCodeArgs, + uuidArgs, + propertyNumbersArgs, + permissionNumbersArgs, + valueArgs, + descriptorsArgs, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct MyMutableGATTServiceArgs { + var hashCodeArgs: Int64 + var uuidArgs: String + var isPrimaryArgs: Bool + var includedServicesArgs: [MyMutableGATTServiceArgs?] + var characteristicsArgs: [MyMutableGATTCharacteristicArgs?] + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ __pigeon_list: [Any?]) -> MyMutableGATTServiceArgs? { + let hashCodeArgs = __pigeon_list[0] is Int64 ? __pigeon_list[0] as! Int64 : Int64(__pigeon_list[0] as! Int32) + let uuidArgs = __pigeon_list[1] as! String + let isPrimaryArgs = __pigeon_list[2] as! Bool + let includedServicesArgs = __pigeon_list[3] as! [MyMutableGATTServiceArgs?] + let characteristicsArgs = __pigeon_list[4] as! [MyMutableGATTCharacteristicArgs?] + + return MyMutableGATTServiceArgs( + hashCodeArgs: hashCodeArgs, + uuidArgs: uuidArgs, + isPrimaryArgs: isPrimaryArgs, + includedServicesArgs: includedServicesArgs, + characteristicsArgs: characteristicsArgs + ) + } + func toList() -> [Any?] { + return [ + hashCodeArgs, + uuidArgs, + isPrimaryArgs, + includedServicesArgs, + characteristicsArgs, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct MyATTRequestArgs { + var hashCodeArgs: Int64 + var centralArgs: MyCentralArgs + var characteristicHashCodeArgs: Int64 + var valueArgs: FlutterStandardTypedData? = nil + var offsetArgs: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ __pigeon_list: [Any?]) -> MyATTRequestArgs? { + let hashCodeArgs = __pigeon_list[0] is Int64 ? __pigeon_list[0] as! Int64 : Int64(__pigeon_list[0] as! Int32) + let centralArgs = __pigeon_list[1] as! MyCentralArgs + let characteristicHashCodeArgs = __pigeon_list[2] is Int64 ? __pigeon_list[2] as! Int64 : Int64(__pigeon_list[2] as! Int32) + let valueArgs: FlutterStandardTypedData? = nilOrValue(__pigeon_list[3]) + let offsetArgs = __pigeon_list[4] is Int64 ? __pigeon_list[4] as! Int64 : Int64(__pigeon_list[4] as! Int32) + + return MyATTRequestArgs( + hashCodeArgs: hashCodeArgs, + centralArgs: centralArgs, + characteristicHashCodeArgs: characteristicHashCodeArgs, + valueArgs: valueArgs, + offsetArgs: offsetArgs + ) + } + func toList() -> [Any?] { + return [ + hashCodeArgs, + centralArgs, + characteristicHashCodeArgs, + valueArgs, + offsetArgs, + ] + } +} + +private class MyCentralManagerHostAPICodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 128: + return MyGATTCharacteristicArgs.fromList(self.readValue() as! [Any?]) + case 129: + return MyGATTDescriptorArgs.fromList(self.readValue() as! [Any?]) + case 130: + return MyGATTServiceArgs.fromList(self.readValue() as! [Any?]) + case 131: + return MyPeripheralArgs.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class MyCentralManagerHostAPICodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? MyGATTCharacteristicArgs { + super.writeByte(128) + super.writeValue(value.toList()) + } else if let value = value as? MyGATTDescriptorArgs { + super.writeByte(129) + super.writeValue(value.toList()) + } else if let value = value as? MyGATTServiceArgs { + super.writeByte(130) + super.writeValue(value.toList()) + } else if let value = value as? MyPeripheralArgs { + super.writeByte(131) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class MyCentralManagerHostAPICodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return MyCentralManagerHostAPICodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return MyCentralManagerHostAPICodecWriter(data: data) + } +} + +class MyCentralManagerHostAPICodec: FlutterStandardMessageCodec { + static let shared = MyCentralManagerHostAPICodec(readerWriter: MyCentralManagerHostAPICodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol MyCentralManagerHostAPI { + func initialize() throws + func getState() throws -> MyBluetoothLowEnergyStateArgs + func showAppSettings(completion: @escaping (Result) -> Void) + func startDiscovery(serviceUUIDsArgs: [String]) throws + func stopDiscovery() throws + func retrieveConnectedPeripherals() throws -> [MyPeripheralArgs] + func connect(uuidArgs: String, completion: @escaping (Result) -> Void) + func disconnect(uuidArgs: String, completion: @escaping (Result) -> Void) + func getMaximumWriteLength(uuidArgs: String, typeArgs: MyGATTCharacteristicWriteTypeArgs) throws -> Int64 + func readRSSI(uuidArgs: String, completion: @escaping (Result) -> Void) + func discoverServices(uuidArgs: String, completion: @escaping (Result<[MyGATTServiceArgs], Error>) -> Void) + func discoverIncludedServices(uuidArgs: String, hashCodeArgs: Int64, 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, typeArgs: MyGATTCharacteristicWriteTypeArgs, 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`. +class MyCentralManagerHostAPISetup { + /// The codec used by MyCentralManagerHostAPI. + static var codec: FlutterStandardMessageCodec { MyCentralManagerHostAPICodec.shared } + /// Sets up an instance of `MyCentralManagerHostAPI` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: MyCentralManagerHostAPI?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let initializeChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.initialize\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + initializeChannel.setMessageHandler { _, reply in + do { + try api.initialize() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + initializeChannel.setMessageHandler(nil) + } + let getStateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.getState\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getStateChannel.setMessageHandler { _, reply in + do { + let result = try api.getState() + reply(wrapResult(result.rawValue)) + } catch { + reply(wrapError(error)) + } + } + } else { + getStateChannel.setMessageHandler(nil) + } + let showAppSettingsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.showAppSettings\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + showAppSettingsChannel.setMessageHandler { _, reply in + api.showAppSettings { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + showAppSettingsChannel.setMessageHandler(nil) + } + let startDiscoveryChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.startDiscovery\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + startDiscoveryChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let serviceUUIDsArgsArg = args[0] as! [String] + do { + try api.startDiscovery(serviceUUIDsArgs: serviceUUIDsArgsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + startDiscoveryChannel.setMessageHandler(nil) + } + let stopDiscoveryChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.stopDiscovery\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + stopDiscoveryChannel.setMessageHandler { _, reply in + do { + try api.stopDiscovery() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + stopDiscoveryChannel.setMessageHandler(nil) + } + let retrieveConnectedPeripheralsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.retrieveConnectedPeripherals\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + retrieveConnectedPeripheralsChannel.setMessageHandler { _, reply in + do { + let result = try api.retrieveConnectedPeripherals() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + retrieveConnectedPeripheralsChannel.setMessageHandler(nil) + } + let connectChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.connect\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + connectChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let uuidArgsArg = args[0] as! String + api.connect(uuidArgs: uuidArgsArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + connectChannel.setMessageHandler(nil) + } + let disconnectChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.disconnect\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + disconnectChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let uuidArgsArg = args[0] as! String + api.disconnect(uuidArgs: uuidArgsArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + disconnectChannel.setMessageHandler(nil) + } + let getMaximumWriteLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.getMaximumWriteLength\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getMaximumWriteLengthChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let uuidArgsArg = args[0] as! String + let typeArgsArg = MyGATTCharacteristicWriteTypeArgs(rawValue: args[1] as! Int)! + do { + let result = try api.getMaximumWriteLength(uuidArgs: uuidArgsArg, typeArgs: typeArgsArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getMaximumWriteLengthChannel.setMessageHandler(nil) + } + let readRSSIChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.readRSSI\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + readRSSIChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let uuidArgsArg = args[0] as! String + api.readRSSI(uuidArgs: uuidArgsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + readRSSIChannel.setMessageHandler(nil) + } + let discoverServicesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.discoverServices\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + discoverServicesChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let uuidArgsArg = args[0] as! String + api.discoverServices(uuidArgs: uuidArgsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + discoverServicesChannel.setMessageHandler(nil) + } + let discoverIncludedServicesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.discoverIncludedServices\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + discoverIncludedServicesChannel.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.discoverIncludedServices(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + discoverIncludedServicesChannel.setMessageHandler(nil) + } + let discoverCharacteristicsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.discoverCharacteristics\(channelSuffix)", 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\(channelSuffix)", 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\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + readCharacteristicChannel.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.readCharacteristic(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + readCharacteristicChannel.setMessageHandler(nil) + } + let writeCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.writeCharacteristic\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + writeCharacteristicChannel.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) + let valueArgsArg = args[2] as! FlutterStandardTypedData + let typeArgsArg = MyGATTCharacteristicWriteTypeArgs(rawValue: args[3] as! Int)! + api.writeCharacteristic(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg, typeArgs: typeArgsArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + writeCharacteristicChannel.setMessageHandler(nil) + } + let setCharacteristicNotifyStateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.setCharacteristicNotifyState\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setCharacteristicNotifyStateChannel.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) + let stateArgsArg = args[2] as! Bool + api.setCharacteristicNotifyState(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, stateArgs: stateArgsArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + setCharacteristicNotifyStateChannel.setMessageHandler(nil) + } + let readDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.readDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + readDescriptorChannel.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.readDescriptor(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + readDescriptorChannel.setMessageHandler(nil) + } + let writeDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.writeDescriptor\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + writeDescriptorChannel.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) + let valueArgsArg = args[2] as! FlutterStandardTypedData + api.writeDescriptor(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + writeDescriptorChannel.setMessageHandler(nil) + } + } +} +private class MyCentralManagerFlutterAPICodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + 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 MyPeripheralArgs.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class MyCentralManagerFlutterAPICodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? MyAdvertisementArgs { + 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 if let value = value as? MyPeripheralArgs { + super.writeByte(131) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class MyCentralManagerFlutterAPICodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return MyCentralManagerFlutterAPICodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return MyCentralManagerFlutterAPICodecWriter(data: data) + } +} + +class MyCentralManagerFlutterAPICodec: FlutterStandardMessageCodec { + static let shared = MyCentralManagerFlutterAPICodec(readerWriter: MyCentralManagerFlutterAPICodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. +protocol MyCentralManagerFlutterAPIProtocol { + func onStateChanged(stateArgs stateArgsArg: MyBluetoothLowEnergyStateArgs, completion: @escaping (Result) -> Void) + func onDiscovered(peripheralArgs peripheralArgsArg: MyPeripheralArgs, rssiArgs rssiArgsArg: Int64, advertisementArgs advertisementArgsArg: MyAdvertisementArgs, completion: @escaping (Result) -> Void) + func onConnectionStateChanged(peripheralArgs peripheralArgsArg: MyPeripheralArgs, stateArgs stateArgsArg: MyConnectionStateArgs, completion: @escaping (Result) -> Void) + func onCharacteristicNotified(peripheralArgs peripheralArgsArg: MyPeripheralArgs, characteristicArgs characteristicArgsArg: MyGATTCharacteristicArgs, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) +} +class MyCentralManagerFlutterAPI: MyCentralManagerFlutterAPIProtocol { + private let binaryMessenger: FlutterBinaryMessenger + private let messageChannelSuffix: String + init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") { + self.binaryMessenger = binaryMessenger + self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + } + var codec: FlutterStandardMessageCodec { + return MyCentralManagerFlutterAPICodec.shared + } + func onStateChanged(stateArgs stateArgsArg: MyBluetoothLowEnergyStateArgs, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onStateChanged\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([stateArgsArg.rawValue] 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(PigeonError(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 channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onDiscovered\(messageChannelSuffix)" + 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(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } + func onConnectionStateChanged(peripheralArgs peripheralArgsArg: MyPeripheralArgs, stateArgs stateArgsArg: MyConnectionStateArgs, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onConnectionStateChanged\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([peripheralArgsArg, stateArgsArg.rawValue] 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(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } + func onCharacteristicNotified(peripheralArgs peripheralArgsArg: MyPeripheralArgs, characteristicArgs characteristicArgsArg: MyGATTCharacteristicArgs, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onCharacteristicNotified\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([peripheralArgsArg, characteristicArgsArg, 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(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } +} +private class MyPeripheralManagerHostAPICodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 128: + return MyAdvertisementArgs.fromList(self.readValue() as! [Any?]) + case 129: + return MyMutableGATTCharacteristicArgs.fromList(self.readValue() as! [Any?]) + case 130: + return MyMutableGATTDescriptorArgs.fromList(self.readValue() as! [Any?]) + case 131: + return MyMutableGATTServiceArgs.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class MyPeripheralManagerHostAPICodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? MyAdvertisementArgs { + super.writeByte(128) + super.writeValue(value.toList()) + } else if let value = value as? MyMutableGATTCharacteristicArgs { + super.writeByte(129) + super.writeValue(value.toList()) + } else if let value = value as? MyMutableGATTDescriptorArgs { + super.writeByte(130) + super.writeValue(value.toList()) + } else if let value = value as? MyMutableGATTServiceArgs { + super.writeByte(131) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class MyPeripheralManagerHostAPICodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return MyPeripheralManagerHostAPICodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return MyPeripheralManagerHostAPICodecWriter(data: data) + } +} + +class MyPeripheralManagerHostAPICodec: FlutterStandardMessageCodec { + static let shared = MyPeripheralManagerHostAPICodec(readerWriter: MyPeripheralManagerHostAPICodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol MyPeripheralManagerHostAPI { + func initialize() throws + func getState() throws -> MyBluetoothLowEnergyStateArgs + func showAppSettings(completion: @escaping (Result) -> Void) + func addService(serviceArgs: MyMutableGATTServiceArgs, completion: @escaping (Result) -> Void) + func removeService(hashCodeArgs: Int64) throws + func removeAllServices() throws + func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result) -> Void) + func stopAdvertising() throws + func getMaximumNotifyLength(uuidArgs: String) throws -> Int64 + func respond(hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData?, errorArgs: MyATTErrorArgs) throws + func updateValue(hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, uuidsArgs: [String]?) throws -> Bool +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class MyPeripheralManagerHostAPISetup { + /// The codec used by MyPeripheralManagerHostAPI. + static var codec: FlutterStandardMessageCodec { MyPeripheralManagerHostAPICodec.shared } + /// Sets up an instance of `MyPeripheralManagerHostAPI` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: MyPeripheralManagerHostAPI?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let initializeChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.initialize\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + initializeChannel.setMessageHandler { _, reply in + do { + try api.initialize() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + initializeChannel.setMessageHandler(nil) + } + let getStateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.getState\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getStateChannel.setMessageHandler { _, reply in + do { + let result = try api.getState() + reply(wrapResult(result.rawValue)) + } catch { + reply(wrapError(error)) + } + } + } else { + getStateChannel.setMessageHandler(nil) + } + let showAppSettingsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.showAppSettings\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + showAppSettingsChannel.setMessageHandler { _, reply in + api.showAppSettings { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + showAppSettingsChannel.setMessageHandler(nil) + } + let addServiceChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.addService\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + addServiceChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let serviceArgsArg = args[0] as! MyMutableGATTServiceArgs + api.addService(serviceArgs: serviceArgsArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + addServiceChannel.setMessageHandler(nil) + } + let removeServiceChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.removeService\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + removeServiceChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let hashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + do { + try api.removeService(hashCodeArgs: hashCodeArgsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + removeServiceChannel.setMessageHandler(nil) + } + let removeAllServicesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.removeAllServices\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + removeAllServicesChannel.setMessageHandler { _, reply in + do { + try api.removeAllServices() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + removeAllServicesChannel.setMessageHandler(nil) + } + let startAdvertisingChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.startAdvertising\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + startAdvertisingChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let advertisementArgsArg = args[0] as! MyAdvertisementArgs + api.startAdvertising(advertisementArgs: advertisementArgsArg) { result in + switch result { + case .success: + reply(wrapResult(nil)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + startAdvertisingChannel.setMessageHandler(nil) + } + let stopAdvertisingChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.stopAdvertising\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + stopAdvertisingChannel.setMessageHandler { _, reply in + do { + try api.stopAdvertising() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + stopAdvertisingChannel.setMessageHandler(nil) + } + let getMaximumNotifyLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.getMaximumNotifyLength\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getMaximumNotifyLengthChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let uuidArgsArg = args[0] as! String + do { + let result = try api.getMaximumNotifyLength(uuidArgs: uuidArgsArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getMaximumNotifyLengthChannel.setMessageHandler(nil) + } + let respondChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.respond\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + respondChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let hashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) + let valueArgsArg: FlutterStandardTypedData? = nilOrValue(args[1]) + let errorArgsArg = MyATTErrorArgs(rawValue: args[2] as! Int)! + do { + try api.respond(hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg, errorArgs: errorArgsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + respondChannel.setMessageHandler(nil) + } + let updateValueChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.updateValue\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + updateValueChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + 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]) + do { + let result = try api.updateValue(hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg, uuidsArgs: uuidsArgsArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + updateValueChannel.setMessageHandler(nil) + } + } +} +private class MyPeripheralManagerFlutterAPICodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 128: + return MyATTRequestArgs.fromList(self.readValue() as! [Any?]) + case 129: + return MyATTRequestArgs.fromList(self.readValue() as! [Any?]) + case 130: + return MyCentralArgs.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class MyPeripheralManagerFlutterAPICodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? MyATTRequestArgs { + super.writeByte(128) + super.writeValue(value.toList()) + } else if let value = value as? MyATTRequestArgs { + super.writeByte(129) + super.writeValue(value.toList()) + } else if let value = value as? MyCentralArgs { + super.writeByte(130) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class MyPeripheralManagerFlutterAPICodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return MyPeripheralManagerFlutterAPICodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return MyPeripheralManagerFlutterAPICodecWriter(data: data) + } +} + +class MyPeripheralManagerFlutterAPICodec: FlutterStandardMessageCodec { + static let shared = MyPeripheralManagerFlutterAPICodec(readerWriter: MyPeripheralManagerFlutterAPICodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. +protocol MyPeripheralManagerFlutterAPIProtocol { + func onStateChanged(stateArgs stateArgsArg: MyBluetoothLowEnergyStateArgs, completion: @escaping (Result) -> Void) + func didReceiveRead(requestArgs requestArgsArg: MyATTRequestArgs, completion: @escaping (Result) -> Void) + func didReceiveWrite(requestsArgs requestsArgsArg: [MyATTRequestArgs], completion: @escaping (Result) -> Void) + func isReady(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 + private let messageChannelSuffix: String + init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") { + self.binaryMessenger = binaryMessenger + self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + } + var codec: FlutterStandardMessageCodec { + return MyPeripheralManagerFlutterAPICodec.shared + } + func onStateChanged(stateArgs stateArgsArg: MyBluetoothLowEnergyStateArgs, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.onStateChanged\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([stateArgsArg.rawValue] 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(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } + func didReceiveRead(requestArgs requestArgsArg: MyATTRequestArgs, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.didReceiveRead\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([requestArgsArg] 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(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } + func didReceiveWrite(requestsArgs requestsArgsArg: [MyATTRequestArgs], completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.didReceiveWrite\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([requestsArgsArg] 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(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } + func isReady(completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.isReady\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage(nil) { 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(PigeonError(code: code, message: message, details: details))) + } else { + 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\(messageChannelSuffix)" + 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(PigeonError(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 similarity index 72% rename from bluetooth_low_energy_darwin/darwin/Classes/MyApi.swift rename to bluetooth_low_energy_darwin/darwin/Classes/MyAPI.swift index 5044078..0208a23 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyApi.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyAPI.swift @@ -17,7 +17,7 @@ import FlutterMacOS #endif // ToObj -extension [MyGattCharacteristicPropertyArgs] { +extension [MyGATTCharacteristicPropertyArgs] { func toProperties() -> CBCharacteristicProperties { var properties: CBCharacteristicProperties = [] for args in self { @@ -36,24 +36,28 @@ extension [MyGattCharacteristicPropertyArgs] { } return properties } - +} + +extension [MyGATTCharacteristicPermissionArgs] { func toPermissions() -> CBAttributePermissions { var permissions: CBAttributePermissions = [] for args in self { switch args { case .read: permissions.insert(.readable) - case .write, .writeWithoutResponse: + case .readEncrypted: + permissions.insert(.readEncryptionRequired) + case .write: permissions.insert(.writeable) - default: - continue + case .writeEncrypted: + permissions.insert(.writeEncryptionRequired) } } return permissions } } -extension MyGattCharacteristicWriteTypeArgs { +extension MyGATTCharacteristicWriteTypeArgs { func toWriteType() -> CBCharacteristicWriteType { switch self { case .withResponse: @@ -64,7 +68,7 @@ extension MyGattCharacteristicWriteTypeArgs { } } -extension MyGattErrorArgs { +extension MyATTErrorArgs { func toError() -> CBATTError.Code { switch self { case .success: @@ -116,22 +120,15 @@ extension MyAdvertisementArgs { 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) - } + if !serviceUUIDsArgs.isEmpty { + let serviceUUIDs = serviceUUIDsArgs.map { serviceUUIDArgs in serviceUUIDArgs!.toCBUUID() } advertisement[CBAdvertisementDataServiceUUIDsKey] = serviceUUIDs } return advertisement } } -extension MyGattDescriptorArgs { +extension MyMutableGATTDescriptorArgs { func toDescriptor() -> CBMutableDescriptor { let type = uuidArgs.toCBUUID() let value = valueArgs?.data @@ -139,30 +136,28 @@ extension MyGattDescriptorArgs { } } -extension MyGattCharacteristicArgs { +extension MyMutableGATTCharacteristicArgs { 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 propertiesArgs = propertyNumbersArgs.map { propertyNumberArgs in + let propertyNumber = propertyNumberArgs!.toInt() + return MyGATTCharacteristicPropertyArgs(rawValue: propertyNumber)! } let properties = propertiesArgs.toProperties() - let permissions = propertiesArgs.toPermissions() - return CBMutableCharacteristic(type: type, properties: properties, value: nil, permissions: permissions) + let value = valueArgs?.data + let permissionsArgs = permissionNumbersArgs.map { permissionNumberArgs in + let permissionNumber = permissionNumberArgs!.toInt() + return MyGATTCharacteristicPermissionArgs(rawValue: permissionNumber)! + } + let permissions = permissionsArgs.toPermissions() + return CBMutableCharacteristic(type: type, properties: properties, value: value, permissions: permissions) } } -extension MyGattServiceArgs { +extension MyMutableGATTServiceArgs { func toService() -> CBMutableService { let type = uuidArgs.toCBUUID() - let primary = true + let primary = isPrimaryArgs return CBMutableService(type: type, primary: primary) } } @@ -199,19 +194,6 @@ extension CBManagerState { } } -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 [String: Any] { func toAdvertisementArgs() -> MyAdvertisementArgs { let nameArgs = self[CBAdvertisementDataLocalNameKey] as? String @@ -224,8 +206,8 @@ extension [String: Any] { return (uuidArgs, dataArgs) } let serviceDataArgs = [String?: FlutterStandardTypedData?](uniqueKeysWithValues: serviceDataArgsKeyWithValues) - let manufacturerSpecificData = self[CBAdvertisementDataManufacturerDataKey] as? Data - let manufacturerSpecificDataArgs = manufacturerSpecificData?.toManufacturerSpecificDataArgs() + let manufacturerData = self[CBAdvertisementDataManufacturerDataKey] as? Data + let manufacturerSpecificDataArgs = manufacturerData == nil ? nil : FlutterStandardTypedData(bytes: manufacturerData!) return MyAdvertisementArgs(nameArgs: nameArgs, serviceUUIDsArgs: serviceUUIDsArgs, serviceDataArgs: serviceDataArgs, manufacturerSpecificDataArgs: manufacturerSpecificDataArgs) } } @@ -245,26 +227,26 @@ extension CBPeripheral { } extension CBDescriptor { - func toArgs() -> MyGattDescriptorArgs { + func toArgs() -> MyGATTDescriptorArgs { let hashCodeArgs = hash.toInt64() let uuidArgs = uuid.toArgs() - return MyGattDescriptorArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs) + return MyGATTDescriptorArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs) } } extension CBCharacteristic { - func toArgs() -> MyGattCharacteristicArgs { + 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) + return MyGATTCharacteristicArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, propertyNumbersArgs: propertyNumbersArgs, descriptorsArgs: descriptorsArgs) } } extension CBCharacteristicProperties { - func toArgs() -> [MyGattCharacteristicPropertyArgs] { - var args = [MyGattCharacteristicPropertyArgs]() + func toArgs() -> [MyGATTCharacteristicPropertyArgs] { + var args = [MyGATTCharacteristicPropertyArgs]() if contains(.read) { args.append(.read) } @@ -285,11 +267,13 @@ extension CBCharacteristicProperties { } extension CBService { - func toArgs() -> MyGattServiceArgs { + func toArgs() -> MyGATTServiceArgs { let hashCodeArgs = hash.toInt64() let uuidArgs = uuid.toArgs() + let isPrimaryArgs = isPrimary + let includedServicesArgs = includedServices?.map { includedService in includedService.toArgs() } ?? [] let characteristicsArgs = characteristics?.map { characteristic in characteristic.toArgs() } ?? [] - return MyGattServiceArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, characteristicsArgs: characteristicsArgs) + return MyGATTServiceArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, isPrimaryArgs: isPrimaryArgs, includedServicesArgs: includedServicesArgs, characteristicsArgs: characteristicsArgs) } } @@ -311,24 +295,9 @@ extension CBUUID { } } -// 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) + var bytes = self + return Data(bytes: &bytes, count: MemoryLayout.size) } } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyApi.g.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyApi.g.swift deleted file mode 100644 index e3f119c..0000000 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyApi.g.swift +++ /dev/null @@ -1,1060 +0,0 @@ -// Autogenerated from Pigeon (v15.0.2), do not edit directly. -// See also: https://pub.dev/packages/pigeon - -import Foundation -#if os(iOS) -import Flutter -#elseif os(macOS) -import FlutterMacOS -#else -#error("Unsupported platform.") -#endif - -private func wrapResult(_ result: Any?) -> [Any?] { - return [result] -} - -private func wrapError(_ error: Any) -> [Any?] { - if let flutterError = error as? FlutterError { - return [ - flutterError.code, - flutterError.message, - flutterError.details - ] - } - return [ - "\(error)", - "\(type(of: error))", - "Stacktrace: \(Thread.callStackSymbols)" - ] -} - -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? -} - -enum MyBluetoothLowEnergyStateArgs: Int { - case unknown = 0 - case resetting = 1 - case unsupported = 2 - case unauthorized = 3 - case poweredOff = 4 - case poweredOn = 5 -} - -enum MyGattCharacteristicPropertyArgs: Int { - case read = 0 - case write = 1 - case writeWithoutResponse = 2 - case notify = 3 - case indicate = 4 -} - -enum MyGattCharacteristicWriteTypeArgs: Int { - case withResponse = 0 - case withoutResponse = 1 -} - -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 MyManufacturerSpecificDataArgs { - var idArgs: Int64 - var dataArgs: FlutterStandardTypedData - - 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 MyManufacturerSpecificDataArgs( - idArgs: idArgs, - dataArgs: dataArgs - ) - } - func toList() -> [Any?] { - return [ - idArgs, - dataArgs, - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct MyAdvertisementArgs { - var nameArgs: String? = nil - var serviceUUIDsArgs: [String?] - var serviceDataArgs: [String?: FlutterStandardTypedData?] - var manufacturerSpecificDataArgs: MyManufacturerSpecificDataArgs? = nil - - static func fromList(_ list: [Any?]) -> MyAdvertisementArgs? { - let nameArgs: String? = nilOrValue(list[0]) - let serviceUUIDsArgs = list[1] as! [String?] - let serviceDataArgs = list[2] as! [String?: FlutterStandardTypedData?] - var manufacturerSpecificDataArgs: MyManufacturerSpecificDataArgs? = nil - if let manufacturerSpecificDataArgsList: [Any?] = nilOrValue(list[3]) { - manufacturerSpecificDataArgs = MyManufacturerSpecificDataArgs.fromList(manufacturerSpecificDataArgsList) - } - - return MyAdvertisementArgs( - nameArgs: nameArgs, - serviceUUIDsArgs: serviceUUIDsArgs, - serviceDataArgs: serviceDataArgs, - manufacturerSpecificDataArgs: manufacturerSpecificDataArgs - ) - } - func toList() -> [Any?] { - return [ - nameArgs, - serviceUUIDsArgs, - serviceDataArgs, - manufacturerSpecificDataArgs?.toList(), - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct MyCentralArgs { - var uuidArgs: String - - static func fromList(_ list: [Any?]) -> MyCentralArgs? { - let uuidArgs = list[0] as! String - - return MyCentralArgs( - uuidArgs: uuidArgs - ) - } - func toList() -> [Any?] { - return [ - uuidArgs, - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -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 valueArgs: FlutterStandardTypedData? = nil - - 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 valueArgs: FlutterStandardTypedData? = nilOrValue(list[2]) - - return MyGattDescriptorArgs( - hashCodeArgs: hashCodeArgs, - uuidArgs: uuidArgs, - valueArgs: valueArgs - ) - } - func toList() -> [Any?] { - return [ - hashCodeArgs, - uuidArgs, - valueArgs, - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct MyGattCharacteristicArgs { - var hashCodeArgs: Int64 - var uuidArgs: String - var propertyNumbersArgs: [Int64?] - var descriptorsArgs: [MyGattDescriptorArgs?] - - static func fromList(_ list: [Any?]) -> MyGattCharacteristicArgs? { - let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) - let uuidArgs = list[1] as! String - let propertyNumbersArgs = list[2] as! [Int64?] - let descriptorsArgs = list[3] as! [MyGattDescriptorArgs?] - - return MyGattCharacteristicArgs( - hashCodeArgs: hashCodeArgs, - uuidArgs: uuidArgs, - propertyNumbersArgs: propertyNumbersArgs, - descriptorsArgs: descriptorsArgs - ) - } - func toList() -> [Any?] { - return [ - hashCodeArgs, - uuidArgs, - propertyNumbersArgs, - descriptorsArgs, - ] - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct MyGattServiceArgs { - var hashCodeArgs: Int64 - var uuidArgs: String - var characteristicsArgs: [MyGattCharacteristicArgs?] - - 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 characteristicsArgs = list[2] as! [MyGattCharacteristicArgs?] - - return MyGattServiceArgs( - hashCodeArgs: hashCodeArgs, - uuidArgs: uuidArgs, - characteristicsArgs: characteristicsArgs - ) - } - func toList() -> [Any?] { - return [ - hashCodeArgs, - uuidArgs, - characteristicsArgs, - ] - } -} - -private class MyCentralManagerHostApiCodecReader: FlutterStandardReader { - override func readValue(ofType type: UInt8) -> Any? { - switch type { - case 128: - return MyGattCharacteristicArgs.fromList(self.readValue() as! [Any?]) - case 129: - return MyGattDescriptorArgs.fromList(self.readValue() as! [Any?]) - case 130: - return MyGattServiceArgs.fromList(self.readValue() as! [Any?]) - default: - return super.readValue(ofType: type) - } - } -} - -private class MyCentralManagerHostApiCodecWriter: FlutterStandardWriter { - override func writeValue(_ value: Any) { - if let value = value as? MyGattCharacteristicArgs { - super.writeByte(128) - super.writeValue(value.toList()) - } else if let value = value as? MyGattDescriptorArgs { - super.writeByte(129) - super.writeValue(value.toList()) - } else if let value = value as? MyGattServiceArgs { - super.writeByte(130) - super.writeValue(value.toList()) - } else { - super.writeValue(value) - } - } -} - -private class MyCentralManagerHostApiCodecReaderWriter: FlutterStandardReaderWriter { - override func reader(with data: Data) -> FlutterStandardReader { - return MyCentralManagerHostApiCodecReader(data: data) - } - - override func writer(with data: NSMutableData) -> FlutterStandardWriter { - return MyCentralManagerHostApiCodecWriter(data: data) - } -} - -class MyCentralManagerHostApiCodec: FlutterStandardMessageCodec { - static let shared = MyCentralManagerHostApiCodec(readerWriter: MyCentralManagerHostApiCodecReaderWriter()) -} - -/// Generated protocol from Pigeon that represents a handler of messages from Flutter. -protocol MyCentralManagerHostApi { - func setUp() throws - func startDiscovery() throws - func stopDiscovery() throws - 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`. -class MyCentralManagerHostApiSetup { - /// The codec used by MyCentralManagerHostApi. - static var codec: FlutterStandardMessageCodec { MyCentralManagerHostApiCodec.shared } - /// Sets up an instance of `MyCentralManagerHostApi` to handle messages through the `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: MyCentralManagerHostApi?) { - 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 - do { - try api.setUp() - reply(wrapResult(nil)) - } catch { - reply(wrapError(error)) - } - } - } else { - setUpChannel.setMessageHandler(nil) - } - let startDiscoveryChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.startDiscovery", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - startDiscoveryChannel.setMessageHandler { _, reply in - do { - try api.startDiscovery() - reply(wrapResult(nil)) - } catch { - reply(wrapError(error)) - } - } - } else { - startDiscoveryChannel.setMessageHandler(nil) - } - let stopDiscoveryChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.stopDiscovery", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - stopDiscoveryChannel.setMessageHandler { _, reply in - do { - try api.stopDiscovery() - reply(wrapResult(nil)) - } catch { - reply(wrapError(error)) - } - } - } else { - stopDiscoveryChannel.setMessageHandler(nil) - } - let connectChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.connect", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - connectChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let uuidArgsArg = args[0] as! String - api.connect(uuidArgs: uuidArgsArg) { result in - switch result { - case .success: - reply(wrapResult(nil)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - connectChannel.setMessageHandler(nil) - } - let disconnectChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.disconnect", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - disconnectChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let uuidArgsArg = args[0] as! String - api.disconnect(uuidArgs: uuidArgsArg) { result in - switch result { - case .success: - reply(wrapResult(nil)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - disconnectChannel.setMessageHandler(nil) - } - let getMaximumWriteValueLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.getMaximumWriteValueLength", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - getMaximumWriteValueLengthChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - 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.getMaximumWriteValueLength(uuidArgs: uuidArgsArg, typeNumberArgs: typeNumberArgsArg) - reply(wrapResult(result)) - } catch { - reply(wrapError(error)) - } - } - } else { - 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 uuidArgsArg = args[0] as! String - api.readRSSI(uuidArgs: uuidArgsArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - readRSSIChannel.setMessageHandler(nil) - } - let discoverServicesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverServices", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - discoverServicesChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let uuidArgsArg = args[0] as! String - api.discoverServices(uuidArgs: uuidArgsArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - 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 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)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - readCharacteristicChannel.setMessageHandler(nil) - } - let writeCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.writeCharacteristic", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - writeCharacteristicChannel.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) - let valueArgsArg = args[2] as! FlutterStandardTypedData - let typeNumberArgsArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32) - api.writeCharacteristic(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg, typeNumberArgs: typeNumberArgsArg) { result in - switch result { - case .success: - reply(wrapResult(nil)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - writeCharacteristicChannel.setMessageHandler(nil) - } - let setCharacteristicNotifyStateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setCharacteristicNotifyState", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - setCharacteristicNotifyStateChannel.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) - let stateArgsArg = args[2] as! Bool - api.setCharacteristicNotifyState(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, stateArgs: stateArgsArg) { result in - switch result { - case .success: - reply(wrapResult(nil)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - 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 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)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - readDescriptorChannel.setMessageHandler(nil) - } - let writeDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.writeDescriptor", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - writeDescriptorChannel.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) - let valueArgsArg = args[2] as! FlutterStandardTypedData - api.writeDescriptor(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg) { result in - switch result { - case .success: - reply(wrapResult(nil)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - writeDescriptorChannel.setMessageHandler(nil) - } - } -} -private class MyCentralManagerFlutterApiCodecReader: FlutterStandardReader { - override func readValue(ofType type: UInt8) -> Any? { - switch type { - case 128: - return MyAdvertisementArgs.fromList(self.readValue() as! [Any?]) - case 129: - return MyManufacturerSpecificDataArgs.fromList(self.readValue() as! [Any?]) - case 130: - return MyPeripheralArgs.fromList(self.readValue() as! [Any?]) - default: - return super.readValue(ofType: type) - } - } -} - -private class MyCentralManagerFlutterApiCodecWriter: FlutterStandardWriter { - override func writeValue(_ value: Any) { - if let value = value as? MyAdvertisementArgs { - super.writeByte(128) - super.writeValue(value.toList()) - } else if let value = value as? MyManufacturerSpecificDataArgs { - super.writeByte(129) - super.writeValue(value.toList()) - } else if let value = value as? MyPeripheralArgs { - super.writeByte(130) - super.writeValue(value.toList()) - } else { - super.writeValue(value) - } - } -} - -private class MyCentralManagerFlutterApiCodecReaderWriter: FlutterStandardReaderWriter { - override func reader(with data: Data) -> FlutterStandardReader { - return MyCentralManagerFlutterApiCodecReader(data: data) - } - - override func writer(with data: NSMutableData) -> FlutterStandardWriter { - return MyCentralManagerFlutterApiCodecWriter(data: data) - } -} - -class MyCentralManagerFlutterApiCodec: FlutterStandardMessageCodec { - static let shared = MyCentralManagerFlutterApiCodec(readerWriter: MyCentralManagerFlutterApiCodecReaderWriter()) -} - -/// 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 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 - init(binaryMessenger: FlutterBinaryMessenger){ - self.binaryMessenger = binaryMessenger - } - var codec: FlutterStandardMessageCodec { - return MyCentralManagerFlutterApiCodec.shared - } - 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 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 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 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())) - } - } - } -} -private class MyPeripheralManagerHostApiCodecReader: FlutterStandardReader { - override func readValue(ofType type: UInt8) -> Any? { - switch type { - 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 MyGattServiceArgs.fromList(self.readValue() as! [Any?]) - case 132: - return MyManufacturerSpecificDataArgs.fromList(self.readValue() as! [Any?]) - default: - return super.readValue(ofType: type) - } - } -} - -private class MyPeripheralManagerHostApiCodecWriter: FlutterStandardWriter { - override func writeValue(_ value: Any) { - if let value = value as? MyAdvertisementArgs { - 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 if let value = value as? MyGattServiceArgs { - super.writeByte(131) - super.writeValue(value.toList()) - } else if let value = value as? MyManufacturerSpecificDataArgs { - super.writeByte(132) - super.writeValue(value.toList()) - } else { - super.writeValue(value) - } - } -} - -private class MyPeripheralManagerHostApiCodecReaderWriter: FlutterStandardReaderWriter { - override func reader(with data: Data) -> FlutterStandardReader { - return MyPeripheralManagerHostApiCodecReader(data: data) - } - - override func writer(with data: NSMutableData) -> FlutterStandardWriter { - return MyPeripheralManagerHostApiCodecWriter(data: data) - } -} - -class MyPeripheralManagerHostApiCodec: FlutterStandardMessageCodec { - static let shared = MyPeripheralManagerHostApiCodec(readerWriter: MyPeripheralManagerHostApiCodecReaderWriter()) -} - -/// Generated protocol from Pigeon that represents a handler of messages from Flutter. -protocol MyPeripheralManagerHostApi { - func setUp() throws - func addService(serviceArgs: MyGattServiceArgs, completion: @escaping (Result) -> Void) - func removeService(hashCodeArgs: Int64) throws - func clearServices() throws - func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result) -> Void) - func stopAdvertising() throws - 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`. -class MyPeripheralManagerHostApiSetup { - /// The codec used by MyPeripheralManagerHostApi. - static var codec: FlutterStandardMessageCodec { MyPeripheralManagerHostApiCodec.shared } - /// Sets up an instance of `MyPeripheralManagerHostApi` to handle messages through the `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: MyPeripheralManagerHostApi?) { - 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 - do { - try api.setUp() - reply(wrapResult(nil)) - } catch { - reply(wrapError(error)) - } - } - } else { - setUpChannel.setMessageHandler(nil) - } - let addServiceChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.addService", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - addServiceChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let serviceArgsArg = args[0] as! MyGattServiceArgs - api.addService(serviceArgs: serviceArgsArg) { result in - switch result { - case .success: - reply(wrapResult(nil)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - addServiceChannel.setMessageHandler(nil) - } - let removeServiceChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.removeService", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - removeServiceChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let hashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) - do { - try api.removeService(hashCodeArgs: hashCodeArgsArg) - reply(wrapResult(nil)) - } catch { - reply(wrapError(error)) - } - } - } else { - removeServiceChannel.setMessageHandler(nil) - } - let clearServicesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.clearServices", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - clearServicesChannel.setMessageHandler { _, reply in - do { - try api.clearServices() - reply(wrapResult(nil)) - } catch { - reply(wrapError(error)) - } - } - } else { - clearServicesChannel.setMessageHandler(nil) - } - let startAdvertisingChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.startAdvertising", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - startAdvertisingChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let advertisementArgsArg = args[0] as! MyAdvertisementArgs - api.startAdvertising(advertisementArgs: advertisementArgsArg) { result in - switch result { - case .success: - reply(wrapResult(nil)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - startAdvertisingChannel.setMessageHandler(nil) - } - let stopAdvertisingChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.stopAdvertising", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - stopAdvertisingChannel.setMessageHandler { _, reply in - do { - try api.stopAdvertising() - reply(wrapResult(nil)) - } catch { - reply(wrapError(error)) - } - } - } else { - stopAdvertisingChannel.setMessageHandler(nil) - } - let getMaximumUpdateValueLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.getMaximumUpdateValueLength", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - getMaximumUpdateValueLengthChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let uuidArgsArg = args[0] as! String - do { - let result = try api.getMaximumUpdateValueLength(uuidArgs: uuidArgsArg) - reply(wrapResult(result)) - } catch { - reply(wrapError(error)) - } - } - } else { - getMaximumUpdateValueLengthChannel.setMessageHandler(nil) - } - let respondChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.respond", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - respondChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - 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.respond(idArgs: idArgsArg, errorNumberArgs: errorNumberArgsArg, valueArgs: valueArgsArg) - reply(wrapResult(nil)) - } catch { - reply(wrapError(error)) - } - } - } else { - respondChannel.setMessageHandler(nil) - } - let updateCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.updateCharacteristic", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - updateCharacteristicChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - 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)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - updateCharacteristicChannel.setMessageHandler(nil) - } - } -} -private class MyPeripheralManagerFlutterApiCodecReader: FlutterStandardReader { - override func readValue(ofType type: UInt8) -> Any? { - switch type { - case 128: - return MyCentralArgs.fromList(self.readValue() as! [Any?]) - default: - return super.readValue(ofType: type) - } - } -} - -private class MyPeripheralManagerFlutterApiCodecWriter: FlutterStandardWriter { - override func writeValue(_ value: Any) { - if let value = value as? MyCentralArgs { - super.writeByte(128) - super.writeValue(value.toList()) - } else { - super.writeValue(value) - } - } -} - -private class MyPeripheralManagerFlutterApiCodecReaderWriter: FlutterStandardReaderWriter { - override func reader(with data: Data) -> FlutterStandardReader { - return MyPeripheralManagerFlutterApiCodecReader(data: data) - } - - override func writer(with data: NSMutableData) -> FlutterStandardWriter { - return MyPeripheralManagerFlutterApiCodecWriter(data: data) - } -} - -class MyPeripheralManagerFlutterApiCodec: FlutterStandardMessageCodec { - static let shared = MyPeripheralManagerFlutterApiCodec(readerWriter: MyPeripheralManagerFlutterApiCodecReaderWriter()) -} - -/// 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 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 - init(binaryMessenger: FlutterBinaryMessenger){ - self.binaryMessenger = binaryMessenger - } - var codec: FlutterStandardMessageCodec { - return MyPeripheralManagerFlutterApiCodec.shared - } - 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 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 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 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/MyCentralManager.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManager.swift index fc931aa..7a07784 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManager.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManager.swift @@ -16,76 +16,142 @@ import FlutterMacOS #error("Unsupported platform.") #endif -class MyCentralManager: MyCentralManagerHostApi { - private let _api: MyCentralManagerFlutterApi - private let _centralManager: CBCentralManager +class MyCentralManager: MyCentralManagerHostAPI { + private let mAPI: MyCentralManagerFlutterAPI + private let mCentralManager: CBCentralManager - private lazy var _centralManagerDelegate = MyCentralManagerDelegate(centralManager: self) - private lazy var _peripheralDelegate = MyPeripheralDelegate(centralManager: self) + private lazy var mCentralManagerDelegate = 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 mPeripherals: [String: CBPeripheral] + private var mServices: [String: [Int64: CBService]] + private var mCharacteristics: [String: [Int64: CBCharacteristic]] + private var mDescriptors: [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]] + private var mConnectCompletions: [String: (Result) -> Void] + private var mDisconnectCompletions: [String: (Result) -> Void] + private var mReadRSSICompletions: [String: (Result) -> Void] + private var mDiscoverServicesCompletions: [String: (Result<[MyGATTServiceArgs], Error>) -> Void] + private var mDiscoverIncludedServicesCompletions: [String: [Int64: (Result<[MyGATTServiceArgs], Error>) -> Void]] + private var mDiscoverCharacteristicsCompletions: [String: [Int64: (Result<[MyGATTCharacteristicArgs], Error>) -> Void]] + private var mDiscoverDescriptorsCompletions: [String: [Int64: (Result<[MyGATTDescriptorArgs], Error>) -> Void]] + private var mReadCharacteristicCompletions: [String: [Int64: (Result) -> Void]] + private var mWriteCharacteristicCompletions: [String: [Int64: (Result) -> Void]] + private var mSetCharacteristicNotifyStateCompletions: [String: [Int64: (Result) -> Void]] + private var mReadDescriptorCompletions: [String: [Int64: (Result) -> Void]] + private var mWriteDescriptorCompletions: [String: [Int64: (Result) -> Void]] init(messenger: FlutterBinaryMessenger) { - _api = MyCentralManagerFlutterApi(binaryMessenger: messenger) - _centralManager = CBCentralManager() + mAPI = MyCentralManagerFlutterAPI(binaryMessenger: messenger) + mCentralManager = CBCentralManager() - _peripherals = [:] - _services = [:] - _characteristics = [:] - _descriptors = [:] + mPeripherals = [:] + mServices = [:] + mCharacteristics = [:] + mDescriptors = [:] - _connectCompletions = [:] - _disconnectCompletions = [:] - _readRssiCompletions = [:] - _discoverServicesCompletions = [:] - _discoverCharacteristicsCompletions = [:] - _discoverDescriptorsCompletions = [:] - _readCharacteristicCompletions = [:] - _writeCharacteristicCompletions = [:] - _setCharacteristicNotifyStateCompletions = [:] - _readDescriptorCompletions = [:] - _writeDescriptorCompletions = [:] + mConnectCompletions = [:] + mDisconnectCompletions = [:] + mReadRSSICompletions = [:] + mDiscoverServicesCompletions = [:] + mDiscoverIncludedServicesCompletions = [:] + mDiscoverCharacteristicsCompletions = [:] + mDiscoverDescriptorsCompletions = [:] + mReadCharacteristicCompletions = [:] + mWriteCharacteristicCompletions = [:] + mSetCharacteristicNotifyStateCompletions = [:] + mReadDescriptorCompletions = [:] + mWriteDescriptorCompletions = [:] } - func setUp() throws { - _clearState() - if _centralManager.delegate == nil { - _centralManager.delegate = _centralManagerDelegate + func initialize() throws { + if(mCentralManager.isScanning) { + mCentralManager.stopScan() } - didUpdateState(central: _centralManager) + + for peripheral in mPeripherals.values { + if peripheral.state != .disconnected { + mCentralManager.cancelPeripheralConnection(peripheral) + } + } + + mPeripherals.removeAll() + mServices.removeAll() + mCharacteristics.removeAll() + mDescriptors.removeAll() + + mConnectCompletions.removeAll() + mDisconnectCompletions.removeAll() + mReadRSSICompletions.removeAll() + mDiscoverServicesCompletions.removeAll() + mDiscoverIncludedServicesCompletions.removeAll() + mDiscoverCharacteristicsCompletions.removeAll() + mDiscoverDescriptorsCompletions.removeAll() + mReadCharacteristicCompletions.removeAll() + mWriteCharacteristicCompletions.removeAll() + mSetCharacteristicNotifyStateCompletions.removeAll() + mReadDescriptorCompletions.removeAll() + mWriteDescriptorCompletions.removeAll() + + mCentralManager.delegate = mCentralManagerDelegate } - func startDiscovery() throws { + func getState() throws -> MyBluetoothLowEnergyStateArgs { + let state = mCentralManager.state + let stateArgs = state.toArgs() + return stateArgs + } + + func showAppSettings(completion: @escaping (Result) -> Void) { +#if os(iOS) + do { + guard let url = URL(string: UIApplication.openSettingsURLString) else { + throw MyError.illegalArgument + } + UIApplication.shared.open(url) { success in + if (success) { + completion(.success(())) + } else { + completion(.failure(MyError.unknown)) + } + } + } catch { + completion(.failure(error)) + } +#else + completion(.failure(MyError.unsupported)) +#endif + } + + func startDiscovery(serviceUUIDsArgs: [String]) throws { + let serviceUUIDs = serviceUUIDsArgs.isEmpty ? nil : serviceUUIDsArgs.map { serviceUUIDArgs in serviceUUIDArgs.toCBUUID() } let options = [CBCentralManagerScanOptionAllowDuplicatesKey: true] - _centralManager.scanForPeripherals(withServices: nil, options: options) + mCentralManager.scanForPeripherals(withServices: serviceUUIDs, options: options) } func stopDiscovery() throws { - _centralManager.stopScan() + mCentralManager.stopScan() + } + + func retrieveConnectedPeripherals() throws -> [MyPeripheralArgs] { + let peripherals = mCentralManager.retrieveConnectedPeripherals(withServices: []) + let peripheralsArgs = peripherals.map { peripheral in + let peripheralArgs = peripheral.toArgs() + let uuidArgs = peripheralArgs.uuidArgs + if peripheral.delegate == nil { + peripheral.delegate = peripheralDelegate + } + self.mPeripherals[uuidArgs] = peripheral + return peripheralArgs + } + return peripheralsArgs } func connect(uuidArgs: String, completion: @escaping (Result) -> Void) { do { - guard let peripheral = _peripherals[uuidArgs] else { - throw MyError.illegalArgument - } - _centralManager.connect(peripheral) - _connectCompletions[uuidArgs] = completion + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) + mCentralManager.connect(peripheral) + mConnectCompletions[uuidArgs] = completion } catch { completion(.failure(error)) } @@ -93,79 +159,70 @@ class MyCentralManager: MyCentralManagerHostApi { func disconnect(uuidArgs: String, completion: @escaping (Result) -> Void) { do { - guard let peripheral = _peripherals[uuidArgs] else { - throw MyError.illegalArgument - } - _centralManager.cancelPeripheralConnection(peripheral) - _disconnectCompletions[uuidArgs] = completion + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) + mCentralManager.cancelPeripheralConnection(peripheral) + mDisconnectCompletions[uuidArgs] = completion } catch { completion(.failure(error)) } } - func getMaximumWriteValueLength(uuidArgs: String, typeNumberArgs: Int64) throws -> Int64 { - guard let peripheral = _peripherals[uuidArgs] else { - throw MyError.illegalArgument - } - let typeNumber = typeNumberArgs.toInt() - guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeNumber) else { - throw MyError.illegalArgument - } + func getMaximumWriteLength(uuidArgs: String, typeArgs: MyGATTCharacteristicWriteTypeArgs) throws -> Int64 { + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) let type = typeArgs.toWriteType() - let maximumWriteValueLength = peripheral.maximumWriteValueLength(for: type) - let maximumWriteValueLengthArgs = maximumWriteValueLength.toInt64() - return maximumWriteValueLengthArgs + let maximumWriteLength = peripheral.maximumWriteValueLength(for: type) + let maximumWriteLengthArgs = maximumWriteLength.toInt64() + return maximumWriteLengthArgs } func readRSSI(uuidArgs: String, completion: @escaping (Result) -> Void) { do { - guard let peripheral = _peripherals[uuidArgs] else { - throw MyError.illegalArgument - } + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) peripheral.readRSSI() - _readRssiCompletions[uuidArgs] = completion + mReadRSSICompletions[uuidArgs] = completion } catch { completion(.failure(error)) } } - func discoverServices(uuidArgs: String, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void) { + func discoverServices(uuidArgs: String, completion: @escaping (Result<[MyGATTServiceArgs], Error>) -> Void) { do { - guard let peripheral = _peripherals[uuidArgs] else { - throw MyError.illegalArgument - } + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) peripheral.discoverServices(nil) - _discoverServicesCompletions[uuidArgs] = completion + mDiscoverServicesCompletions[uuidArgs] = completion } catch { completion(.failure(error)) } } - func discoverCharacteristics(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattCharacteristicArgs], Error>) -> Void) { + func discoverIncludedServices(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGATTServiceArgs], Error>) -> Void) { do { - guard let peripheral = _peripherals[uuidArgs] else { - throw MyError.illegalArgument - } - guard let service = _retrieveService(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else { - throw MyError.illegalArgument - } + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) + let service = try retrieveService(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) + peripheral.discoverIncludedServices(nil, for: service) + mDiscoverIncludedServicesCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion + } catch { + completion(.failure(error)) + } + } + + func discoverCharacteristics(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGATTCharacteristicArgs], Error>) -> Void) { + do { + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) + let service = try retrieveService(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) peripheral.discoverCharacteristics(nil, for: service) - _discoverCharacteristicsCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion + mDiscoverCharacteristicsCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } catch { completion(.failure(error)) } } - func discoverDescriptors(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattDescriptorArgs], Error>) -> Void){ + 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 - } + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) + let characteristic = try retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) peripheral.discoverDescriptors(for: characteristic) - _discoverDescriptorsCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion + mDiscoverDescriptorsCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } catch { completion(.failure(error)) } @@ -173,36 +230,24 @@ class MyCentralManager: MyCentralManagerHostApi { 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 - } + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) + let characteristic = try retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) peripheral.readValue(for: characteristic) - _readCharacteristicCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion + mReadCharacteristicCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } catch { completion(.failure(error)) } } - func writeCharacteristic(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result) -> Void) { + func writeCharacteristic(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeArgs: MyGATTCharacteristicWriteTypeArgs, 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 - } + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) + let characteristic = try retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) let data = valueArgs.data - 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[uuidArgs, default: [:]][hashCodeArgs] = completion + mWriteCharacteristicCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } else { completion(.success(())) } @@ -213,15 +258,11 @@ class MyCentralManager: MyCentralManagerHostApi { func setCharacteristicNotifyState(uuidArgs: String, hashCodeArgs: Int64, stateArgs: Bool, 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 - } + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) + let characteristic = try retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) let enabled = stateArgs peripheral.setNotifyValue(enabled, for: characteristic) - _setCharacteristicNotifyStateCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion + mSetCharacteristicNotifyStateCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } catch { completion(.failure(error)) } @@ -229,14 +270,10 @@ class MyCentralManager: MyCentralManagerHostApi { func readDescriptor(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result) -> Void) { do { - guard let peripheral = _peripherals[uuidArgs] else { - throw MyError.illegalArgument - } - guard let descriptor = _retrieveDescriptor(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else { - throw MyError.illegalArgument - } + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) + let descriptor = try retrieveDescriptor(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) peripheral.readValue(for: descriptor) - _readDescriptorCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion + mReadDescriptorCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } catch { completion(.failure(error)) } @@ -244,15 +281,11 @@ class MyCentralManager: MyCentralManagerHostApi { func writeDescriptor(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result) -> Void) { do { - guard let peripheral = _peripherals[uuidArgs] else { - throw MyError.illegalArgument - } - guard let descriptor = _retrieveDescriptor(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else { - throw MyError.illegalArgument - } + let peripheral = try retrievePeripheral(uuidArgs: uuidArgs) + let descriptor = try retrieveDescriptor(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) let data = valueArgs.data peripheral.writeValue(data, for: descriptor) - _writeDescriptorCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion + mWriteDescriptorCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion } catch { completion(.failure(error)) } @@ -261,8 +294,7 @@ class MyCentralManager: MyCentralManagerHostApi { func didUpdateState(central: CBCentralManager) { let state = central.state let stateArgs = state.toArgs() - let stateNumberArgs = stateArgs.rawValue.toInt64() - _api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in } + mAPI.onStateChanged(stateArgs: stateArgs) { _ in } } func didDiscover(central: CBCentralManager, peripheral: CBPeripheral, advertisementData: [String : Any], rssi: NSNumber) { @@ -271,17 +303,18 @@ class MyCentralManager: MyCentralManagerHostApi { let rssiArgs = rssi.int64Value let advertisementArgs = advertisementData.toAdvertisementArgs() if peripheral.delegate == nil { - peripheral.delegate = _peripheralDelegate + peripheral.delegate = peripheralDelegate } - _peripherals[uuidArgs] = peripheral - _api.onDiscovered(peripheralArgs: peripheralArgs, rssiArgs: rssiArgs, advertisementArgs: advertisementArgs) {_ in } + mPeripherals[uuidArgs] = peripheral + mAPI.onDiscovered(peripheralArgs: peripheralArgs, rssiArgs: rssiArgs, advertisementArgs: advertisementArgs) {_ in } } func didConnect(central: CBCentralManager, peripheral: CBPeripheral) { - let uuidArgs = peripheral.identifier.toArgs() - let stateArgs = true - _api.onConnectionStateChanged(uuidArgs: uuidArgs, stateArgs: stateArgs) {_ in } - guard let completion = _connectCompletions.removeValue(forKey: uuidArgs) else { + let peripheralArgs = peripheral.toArgs() + let uuidArgs = peripheralArgs.uuidArgs + let stateArgs = MyConnectionStateArgs.connected + mAPI.onConnectionStateChanged(peripheralArgs: peripheralArgs, stateArgs: stateArgs) { _ in } + guard let completion = mConnectCompletions.removeValue(forKey: uuidArgs) else { return } completion(.success(())) @@ -289,74 +322,82 @@ class MyCentralManager: MyCentralManagerHostApi { func didFailToConnect(central: CBCentralManager, peripheral: CBPeripheral, error: Error?) { let uuidArgs = peripheral.identifier.toArgs() - guard let completion = _connectCompletions.removeValue(forKey: uuidArgs) else { + guard let completion = mConnectCompletions.removeValue(forKey: uuidArgs) else { return } completion(.failure(error ?? MyError.unknown)) } 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 peripheralArgs = peripheral.toArgs() + let uuidArgs = peripheralArgs.uuidArgs + mServices.removeValue(forKey: uuidArgs) + mCharacteristics.removeValue(forKey: uuidArgs) + mDescriptors.removeValue(forKey: uuidArgs) let errorNotNil = error ?? MyError.unknown - let readRssiCompletion = _readRssiCompletions.removeValue(forKey: uuidArgs) + let readRssiCompletion = mReadRSSICompletions.removeValue(forKey: uuidArgs) readRssiCompletion?(.failure(errorNotNil)) - let discoverServicesCompletion = _discoverServicesCompletions.removeValue(forKey: uuidArgs) + let discoverServicesCompletion = mDiscoverServicesCompletions.removeValue(forKey: uuidArgs) discoverServicesCompletion?(.failure(errorNotNil)) - let discoverCharacteristicsCompletions = _discoverCharacteristicsCompletions.removeValue(forKey: uuidArgs) + let discoverIncludedServicesCompletions = self.mDiscoverIncludedServicesCompletions.removeValue(forKey: uuidArgs) + if discoverIncludedServicesCompletions != nil { + let completions = discoverIncludedServicesCompletions!.values + for completion in completions { + completion(.failure(errorNotNil)) + } + } + let discoverCharacteristicsCompletions = self.mDiscoverCharacteristicsCompletions.removeValue(forKey: uuidArgs) if discoverCharacteristicsCompletions != nil { let completions = discoverCharacteristicsCompletions!.values for completion in completions { completion(.failure(errorNotNil)) } } - let discoverDescriptorsCompletions = _discoverDescriptorsCompletions.removeValue(forKey: uuidArgs) + let discoverDescriptorsCompletions = self.mDiscoverDescriptorsCompletions.removeValue(forKey: uuidArgs) if discoverDescriptorsCompletions != nil { let completions = discoverDescriptorsCompletions!.values for completion in completions { completion(.failure(errorNotNil)) } } - let readCharacteristicCompletions = _readCharacteristicCompletions.removeValue(forKey: uuidArgs) + let readCharacteristicCompletions = self.mReadCharacteristicCompletions.removeValue(forKey: uuidArgs) if readCharacteristicCompletions != nil { let completions = readCharacteristicCompletions!.values for completion in completions { completion(.failure(errorNotNil)) } } - let writeCharacteristicCompletions = _writeCharacteristicCompletions.removeValue(forKey: uuidArgs) + let writeCharacteristicCompletions = self.mWriteCharacteristicCompletions.removeValue(forKey: uuidArgs) if writeCharacteristicCompletions != nil { let completions = writeCharacteristicCompletions!.values for completion in completions { completion(.failure(errorNotNil)) } } - let notifyCharacteristicCompletions = _setCharacteristicNotifyStateCompletions.removeValue(forKey: uuidArgs) + let notifyCharacteristicCompletions = self.mSetCharacteristicNotifyStateCompletions.removeValue(forKey: uuidArgs) if notifyCharacteristicCompletions != nil { let completions = notifyCharacteristicCompletions!.values for completioin in completions { completioin(.failure(errorNotNil)) } } - let readDescriptorCompletions = _readDescriptorCompletions.removeValue(forKey: uuidArgs) + let readDescriptorCompletions = self.mReadDescriptorCompletions.removeValue(forKey: uuidArgs) if readDescriptorCompletions != nil { let completions = readDescriptorCompletions!.values for completioin in completions { completioin(.failure(errorNotNil)) } } - let writeDescriptorCompletions = _writeDescriptorCompletions.removeValue(forKey: uuidArgs) + let writeDescriptorCompletions = self.mWriteDescriptorCompletions.removeValue(forKey: uuidArgs) if writeDescriptorCompletions != nil { let completions = writeDescriptorCompletions!.values for completion in completions { completion(.failure(errorNotNil)) } } - let stateArgs = false - _api.onConnectionStateChanged(uuidArgs: uuidArgs, stateArgs: stateArgs) {_ in } - guard let completion = _disconnectCompletions.removeValue(forKey: uuidArgs) else { + let stateArgs = MyConnectionStateArgs.disconnected + mAPI.onConnectionStateChanged(peripheralArgs: peripheralArgs, stateArgs: stateArgs) { _ in } + guard let completion = mDisconnectCompletions.removeValue(forKey: uuidArgs) else { return } if error == nil { @@ -368,7 +409,7 @@ class MyCentralManager: MyCentralManagerHostApi { func didReadRSSI(peripheral: CBPeripheral, rssi: NSNumber, error: Error?) { let uuidArgs = peripheral.identifier.toArgs() - guard let completion = _readRssiCompletions.removeValue(forKey: uuidArgs) else { + guard let completion = mReadRSSICompletions.removeValue(forKey: uuidArgs) else { return } if error == nil { @@ -381,37 +422,57 @@ class MyCentralManager: MyCentralManagerHostApi { func didDiscoverServices(peripheral: CBPeripheral, error: Error?) { let uuidArgs = peripheral.identifier.toArgs() - guard let completion = _discoverServicesCompletions.removeValue(forKey: uuidArgs) else { + guard let completion = mDiscoverServicesCompletions.removeValue(forKey: uuidArgs) else { return } if error == nil { let services = peripheral.services ?? [] - let servicesArgs = services.map { service in service.toArgs() } - let values = services.flatMap { service in - let hashCodeArgs = service.hash.toInt64() - return [hashCodeArgs: service] + var servicesArgs = [MyGATTServiceArgs]() + for service in services { + let serviceArgs = service.toArgs() + self.mServices[uuidArgs, default: [:]][serviceArgs.hashCodeArgs] = service + servicesArgs.append(serviceArgs) } - _services[uuidArgs] = Dictionary(uniqueKeysWithValues: values) completion(.success(servicesArgs)) } else { completion(.failure(error!)) } } + func didDiscoverIncludedServices(peripheral: CBPeripheral, service: CBService, error: Error?) { + let uuidArgs = peripheral.identifier.toArgs() + let hashCodeArgs = service.hash.toInt64() + guard let completion = mDiscoverIncludedServicesCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { + return + } + if error == nil { + let includedServices = service.includedServices ?? [] + var includedServicesArgs = [MyGATTServiceArgs]() + for includedService in includedServices { + let includedServiceArgs = includedService.toArgs() + self.mServices[uuidArgs, default: [:]][includedServiceArgs.hashCodeArgs] = includedService + includedServicesArgs.append(includedServiceArgs) + } + completion(.success(includedServicesArgs)) + } else { + completion(.failure(error!)) + } + } + func didDiscoverCharacteristics(peripheral: CBPeripheral, service: CBService, error: Error?) { let uuidArgs = peripheral.identifier.toArgs() let hashCodeArgs = service.hash.toInt64() - guard let completion = _discoverCharacteristicsCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { + guard let completion = mDiscoverCharacteristicsCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { return } if error == nil { let characteristics = service.characteristics ?? [] - let characteristicsArgs = characteristics.map { characteristic in characteristic.toArgs() } - let values = characteristics.flatMap { characteristic in - let hashCodeArgs = characteristic.hash.toInt64() - return [hashCodeArgs: characteristic] + var characteristicsArgs = [MyGATTCharacteristicArgs]() + for characteristic in characteristics { + let characteristicArgs = characteristic.toArgs() + self.mCharacteristics[uuidArgs, default: [:]][characteristicArgs.hashCodeArgs] = characteristic + characteristicsArgs.append(characteristicArgs) } - _characteristics[uuidArgs, default: [:]].merge(values) { value1, value2 in value2 } completion(.success(characteristicsArgs)) } else { completion(.failure(error!)) @@ -421,17 +482,17 @@ class MyCentralManager: MyCentralManagerHostApi { func didDiscoverDescriptors(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) { let uuidArgs = peripheral.identifier.toArgs() let hashCodeArgs = characteristic.hash.toInt64() - guard let completion = _discoverDescriptorsCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { + guard let completion = mDiscoverDescriptorsCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { return } if error == nil { let descriptors = characteristic.descriptors ?? [] - let descriptorsArgs = descriptors.map { descriptor in descriptor.toArgs() } - let values = descriptors.flatMap { descriptor in - let hashCodeArgs = descriptor.hash.toInt64() - return [hashCodeArgs: descriptor] + var descriptorsArgs = [MyGATTDescriptorArgs]() + for descriptor in descriptors { + let descriptorArgs = descriptor.toArgs() + self.mDescriptors[uuidArgs, default: [:]][descriptorArgs.hashCodeArgs] = descriptor + descriptorsArgs.append(descriptorArgs) } - _descriptors[uuidArgs, default: [:]].merge(values) { value1, value2 in value2 } completion(.success(descriptorsArgs)) } else { completion(.failure(error!)) @@ -439,12 +500,14 @@ class MyCentralManager: MyCentralManagerHostApi { } func didUpdateCharacteristicValue(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) { - let uuidArgs = peripheral.identifier.toArgs() - let hashCodeArgs = characteristic.hash.toInt64() + let peripheralArgs = peripheral.toArgs() + let uuidArgs = peripheralArgs.uuidArgs + let characteristicArgs = characteristic.toArgs() + let hashCodeArgs = characteristicArgs.hashCodeArgs let value = characteristic.value ?? Data() let valueArgs = FlutterStandardTypedData(bytes: value) - guard let completion = _readCharacteristicCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { - _api.onCharacteristicNotified(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs, valueArgs: valueArgs) {_ in } + guard let completion = mReadCharacteristicCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { + mAPI.onCharacteristicNotified(peripheralArgs: peripheralArgs, characteristicArgs: characteristicArgs, valueArgs: valueArgs) { _ in } return } if error == nil { @@ -457,7 +520,7 @@ class MyCentralManager: MyCentralManagerHostApi { func didWriteCharacteristicValue(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) { let uuidArgs = peripheral.identifier.toArgs() let hashCodeArgs = characteristic.hash.toInt64() - guard let completion = _writeCharacteristicCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { + guard let completion = mWriteCharacteristicCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { return } if error == nil { @@ -470,7 +533,7 @@ class MyCentralManager: MyCentralManagerHostApi { func didUpdateCharacteristicNotificationState(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) { let uuidArgs = peripheral.identifier.toArgs() let hashCodeArgs = characteristic.hash.toInt64() - guard let completion = _setCharacteristicNotifyStateCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { + guard let completion = mSetCharacteristicNotifyStateCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { return } if error == nil { @@ -483,45 +546,24 @@ class MyCentralManager: MyCentralManagerHostApi { func didUpdateDescriptorValue(peripheral: CBPeripheral, descriptor: CBDescriptor, error: Error?) { let uuidArgs = peripheral.identifier.toArgs() let hashCodeArgs = descriptor.hash.toInt64() - guard let completion = _readDescriptorCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { + guard let completion = mReadDescriptorCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { return } if error == nil { - // TODO: confirm the corresponding descriptor types and values are correct. let valueArgs: FlutterStandardTypedData - let value = descriptor.value - do { - switch descriptor.uuid.uuidString { - case CBUUIDCharacteristicExtendedPropertiesString: - fallthrough - case CBUUIDClientCharacteristicConfigurationString: - fallthrough - case CBUUIDServerCharacteristicConfigurationString: - guard let numberValue = value as? NSNumber else { - throw MyError.illegalArgument - } - valueArgs = FlutterStandardTypedData(bytes: numberValue.data) - case CBUUIDCharacteristicUserDescriptionString: - fallthrough - case CBUUIDCharacteristicAggregateFormatString: - guard let stringValue = value as? String else { - throw MyError.illegalArgument - } - valueArgs = FlutterStandardTypedData(bytes: stringValue.data) - case CBUUIDCharacteristicFormatString: - guard let bytes = value as? Data else { - throw MyError.illegalArgument - } - valueArgs = FlutterStandardTypedData(bytes: bytes) - case CBUUIDL2CAPPSMCharacteristicString: - guard let uint16Value = value as? UInt16 else { - throw MyError.illegalArgument - } - valueArgs = FlutterStandardTypedData(bytes: uint16Value.data) - default: - throw MyError.illegalArgument - } - } catch { + switch descriptor.value { + case let bytes as Data: + valueArgs = FlutterStandardTypedData(bytes: bytes) + case let value as String: + let bytes = value.data(using: .utf8) ?? Data() + valueArgs = FlutterStandardTypedData(bytes: bytes) + case let value as UInt16: + let bytes = value.data + valueArgs = FlutterStandardTypedData(bytes: bytes) + case let value as NSNumber: + let bytes = withUnsafeBytes(of: value) { elements in Data(elements) } + valueArgs = FlutterStandardTypedData(bytes: bytes) + default: valueArgs = FlutterStandardTypedData() } completion(.success((valueArgs))) @@ -533,7 +575,7 @@ class MyCentralManager: MyCentralManagerHostApi { func didWriteDescriptorValue(peripheral: CBPeripheral, descriptor: CBDescriptor, error: Error?) { let uuidArgs = peripheral.identifier.toArgs() let hashCodeArgs = descriptor.hash.toInt64() - guard let completion = _writeDescriptorCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { + guard let completion = mWriteDescriptorCompletions[uuidArgs]?.removeValue(forKey: hashCodeArgs) else { return } if error == nil { @@ -543,52 +585,40 @@ class MyCentralManager: MyCentralManagerHostApi { } } - private func _clearState() { - if(_centralManager.isScanning) { - _centralManager.stopScan() + private func retrievePeripheral(uuidArgs: String) throws -> CBPeripheral { + guard let peripheral = mPeripherals[uuidArgs] else { + throw MyError.illegalArgument } - 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() + return peripheral } - private func _retrieveService(uuidArgs: String, hashCodeArgs: Int64) -> CBService? { - guard let services = _services[uuidArgs] else { - return nil + private func retrieveService(uuidArgs: String, hashCodeArgs: Int64) throws -> CBService { + guard let services = self.mServices[uuidArgs] else { + throw MyError.illegalArgument } - return services[hashCodeArgs] + guard let service = services[hashCodeArgs] else { + throw MyError.illegalArgument + } + return service } - private func _retrieveCharacteristic(uuidArgs: String, hashCodeArgs: Int64) -> CBCharacteristic? { - guard let characteristics = _characteristics[uuidArgs] else { - return nil + private func retrieveCharacteristic(uuidArgs: String, hashCodeArgs: Int64) throws -> CBCharacteristic { + guard let characteristics = self.mCharacteristics[uuidArgs] else { + throw MyError.illegalArgument } - return characteristics[hashCodeArgs] + guard let characteristic = characteristics[hashCodeArgs] else { + throw MyError.illegalArgument + } + return characteristic } - private func _retrieveDescriptor(uuidArgs: String, hashCodeArgs: Int64) -> CBDescriptor? { - guard let descriptors = _descriptors[uuidArgs] else { - return nil + private func retrieveDescriptor(uuidArgs: String, hashCodeArgs: Int64) throws -> CBDescriptor { + guard let descriptors = self.mDescriptors[uuidArgs] else { + throw MyError.illegalArgument } - return descriptors[hashCodeArgs] + guard let descriptor = descriptors[hashCodeArgs] else { + throw MyError.illegalArgument + } + return descriptor } } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManagerDelegate.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyCentralManagerDelegate.swift index 127a74e..6e2d6a9 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 { - private let _centralManager: MyCentralManager + private let mCentralManager: MyCentralManager init(centralManager: MyCentralManager) { - _centralManager = centralManager + self.mCentralManager = centralManager } func centralManagerDidUpdateState(_ central: CBCentralManager) { - _centralManager.didUpdateState(central: central) + mCentralManager.didUpdateState(central: central) } func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { - _centralManager.didDiscover(central: central, peripheral: peripheral, advertisementData: advertisementData, rssi: RSSI) + mCentralManager.didDiscover(central: central, peripheral: peripheral, advertisementData: advertisementData, rssi: RSSI) } func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { - _centralManager.didConnect(central: central, peripheral: peripheral) + mCentralManager.didConnect(central: central, peripheral: peripheral) } func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { - _centralManager.didFailToConnect(central: central, peripheral: peripheral, error: error) + mCentralManager.didFailToConnect(central: central, peripheral: peripheral, error: error) } func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { - _centralManager.didDisconnectPeripheral(central: central, peripheral: peripheral, error: error) + mCentralManager.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 4d46c65..f1e2f11 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyError.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyError.swift @@ -7,8 +7,8 @@ import Foundation -// TODO: 优化错误内容 enum MyError: Error { - case illegalArgument case unknown + case unsupported + case illegalArgument } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralDelegate.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralDelegate.swift index 4c9d497..75f3256 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralDelegate.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralDelegate.swift @@ -9,45 +9,49 @@ import Foundation import CoreBluetooth class MyPeripheralDelegate: NSObject, CBPeripheralDelegate { - private let _centralManager: MyCentralManager + private let mCentralManager: MyCentralManager init(centralManager: MyCentralManager) { - _centralManager = centralManager + self.mCentralManager = centralManager } func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) { - _centralManager.didReadRSSI(peripheral: peripheral, rssi: RSSI, error: error) + mCentralManager.didReadRSSI(peripheral: peripheral, rssi: RSSI, error: error) } func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { - _centralManager.didDiscoverServices(peripheral: peripheral, error: error) + mCentralManager.didDiscoverServices(peripheral: peripheral, error: error) + } + + func peripheral(_ peripheral: CBPeripheral, didDiscoverIncludedServicesFor service: CBService, error: Error?) { + mCentralManager.didDiscoverIncludedServices(peripheral: peripheral, service: service, error: error) } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { - _centralManager.didDiscoverCharacteristics(peripheral: peripheral, service: service, error: error) + mCentralManager.didDiscoverCharacteristics(peripheral: peripheral, service: service, error: error) } func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) { - _centralManager.didDiscoverDescriptors(peripheral: peripheral, characteristic: characteristic, error: error) + mCentralManager.didDiscoverDescriptors(peripheral: peripheral, characteristic: characteristic, error: error) } func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { - _centralManager.didUpdateCharacteristicValue(peripheral: peripheral, characteristic: characteristic, error: error) + mCentralManager.didUpdateCharacteristicValue(peripheral: peripheral, characteristic: characteristic, error: error) } func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { - _centralManager.didWriteCharacteristicValue(peripheral: peripheral, characteristic: characteristic, error: error) + mCentralManager.didWriteCharacteristicValue(peripheral: peripheral, characteristic: characteristic, error: error) } func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) { - _centralManager.didUpdateCharacteristicNotificationState(peripheral: peripheral, characteristic: characteristic, error: error) + mCentralManager.didUpdateCharacteristicNotificationState(peripheral: peripheral, characteristic: characteristic, error: error) } func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?) { - _centralManager.didUpdateDescriptorValue(peripheral: peripheral, descriptor: descriptor, error: error) + mCentralManager.didUpdateDescriptorValue(peripheral: peripheral, descriptor: descriptor, error: error) } func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) { - _centralManager.didWriteDescriptorValue(peripheral: peripheral, descriptor: descriptor, error: error) + mCentralManager.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 e34f8da..4417f35 100644 --- a/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManager.swift +++ b/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManager.swift @@ -16,207 +16,180 @@ import FlutterMacOS #error("Unsupported platform.") #endif -class MyPeripheralManager: MyPeripheralManagerHostApi { - private let _api: MyPeripheralManagerFlutterApi - private let _peripheralManager: CBPeripheralManager +class MyPeripheralManager: MyPeripheralManagerHostAPI { + private let mAPI: MyPeripheralManagerFlutterAPI + private let mPeripheralManager: CBPeripheralManager - private lazy var _peripheralManagerDelegate = MyPeripheralManagerDelegate(peripheralManager: self) + private lazy var mPeripheralManagerDelegate = MyPeripheralManagerDelegate(peripheralManager: self) - private var _servicesArgs: [Int: MyGattServiceArgs] - private var _characteristicsArgs: [Int: MyGattCharacteristicArgs] - private var _descriptorsArgs: [Int: MyGattDescriptorArgs] + private var mServicesArgs: [Int: MyMutableGATTServiceArgs] + private var mCharacteristicsArgs: [Int: MyMutableGATTCharacteristicArgs] + private var mDescriptorsArgs: [Int: MyMutableGATTDescriptorArgs] - 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 mCentrals: [String: CBCentral] + private var mServices: [Int64: CBMutableService] + private var mCharacteristics: [Int64: CBMutableCharacteristic] + private var mDescriptors: [Int64: CBMutableDescriptor] + private var mRequests: [Int64: CBATTRequest] - private var _addServiceCompletion: ((Result) -> Void)? - private var _startAdvertisingCompletion: ((Result) -> Void)? - private var _isReadyToUpdateSubscribersCallbacks: [() -> Void] + private var mAddServiceCompletion: ((Result) -> Void)? + private var mStartAdvertisingCompletion: ((Result) -> Void)? init(messenger: FlutterBinaryMessenger) { - _api = MyPeripheralManagerFlutterApi(binaryMessenger: messenger) - _peripheralManager = CBPeripheralManager() + mAPI = MyPeripheralManagerFlutterAPI(binaryMessenger: messenger) + mPeripheralManager = CBPeripheralManager() - _servicesArgs = [:] - _characteristicsArgs = [:] - _descriptorsArgs = [:] + mServicesArgs = [:] + mCharacteristicsArgs = [:] + mDescriptorsArgs = [:] - _centrals = [:] - _services = [:] - _characteristics = [:] - _descriptors = [:] - _requests = [:] + mCentrals = [:] + mServices = [:] + mCharacteristics = [:] + mDescriptors = [:] + mRequests = [:] - _addServiceCompletion = nil - _startAdvertisingCompletion = nil - _isReadyToUpdateSubscribersCallbacks = [] + mAddServiceCompletion = nil + mStartAdvertisingCompletion = nil } - func setUp() throws { - _clearState() - if _peripheralManager.delegate == nil { - _peripheralManager.delegate = _peripheralManagerDelegate + func initialize() throws { + if(mPeripheralManager.isAdvertising) { + mPeripheralManager.stopAdvertising() } - didUpdateState(peripheral: _peripheralManager) - } - - func addService(serviceArgs: MyGattServiceArgs, completion: @escaping (Result) -> Void) { - let service = serviceArgs.toService() - var characteristics = [CBMutableCharacteristic]() - let characteristicsArgs = serviceArgs.characteristicsArgs - for args in characteristicsArgs { - guard let characteristicArgs = args else { - continue - } - 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 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.removeValue(forKey: hashCodeArgs) else { - throw MyError.illegalArgument - } - _peripheralManager.remove(service) - let hashCode = service.hash - guard let serviceArgs = _servicesArgs.removeValue(forKey: hashCode) else { - throw MyError.illegalArgument - } - for args in serviceArgs.characteristicsArgs { - guard let characteristicArgs = args else { - continue - } - let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs - guard let characteristic = _characteristics.removeValue(forKey: characteristicHashCodeArgs) else { - throw MyError.illegalArgument - } - let characteristicHashCode = characteristic.hash - _characteristicsArgs.removeValue(forKey: characteristicHashCode) - for args in characteristicArgs.descriptorsArgs { - guard let descriptorArgs = args else { - continue - } - let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs - guard let descriptor = _descriptors.removeValue(forKey: descriptorHashCodeArgs) else { - throw MyError.illegalArgument - } - let descriptorHashCode = descriptor.hash - _descriptorsArgs.removeValue(forKey: descriptorHashCode) - } - } - } - - func clearServices() throws { - _peripheralManager.removeAllServices() - _services.removeAll() - _characteristics.removeAll() - _descriptors.removeAll() + mServicesArgs.removeAll() + mCharacteristicsArgs.removeAll() + mDescriptorsArgs.removeAll() - _servicesArgs.removeAll() - _characteristicsArgs.removeAll() - _descriptors.removeAll() + mCentrals.removeAll() + mServices.removeAll() + mCharacteristics.removeAll() + mDescriptors.removeAll() + mRequests.removeAll() + + mAddServiceCompletion = nil + mStartAdvertisingCompletion = nil + + mPeripheralManager.delegate = mPeripheralManagerDelegate + } + + func getState() throws -> MyBluetoothLowEnergyStateArgs { + let state = mPeripheralManager.state + let stateArgs = state.toArgs() + return stateArgs } - 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) { + func showAppSettings(completion: @escaping (Result) -> Void) { +#if os(iOS) 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 { + guard let url = URL(string: UIApplication.openSettingsURLString) 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) + UIApplication.shared.open(url) { success in + if (success) { + completion(.success(())) + } else { + completion(.failure(MyError.unknown)) } } } catch { completion(.failure(error)) } +#else + completion(.failure(MyError.unsupported)) +#endif + } + + func addService(serviceArgs: MyMutableGATTServiceArgs, completion: @escaping (Result) -> Void) { + do { + let service = try addServiceArgs(serviceArgs) + mPeripheralManager.add(service) + mAddServiceCompletion = completion + } catch { + completion(.failure(error)) + } + } + + func removeService(hashCodeArgs: Int64) throws { + guard let service = mServices[hashCodeArgs] else { + throw MyError.illegalArgument + } + guard let serviceArgs = mServicesArgs[service.hash] else { + throw MyError.illegalArgument + } + mPeripheralManager.remove(service) + try removeServiceArgs(serviceArgs) + } + + func removeAllServices() throws { + mPeripheralManager.removeAllServices() + + mServices.removeAll() + mCharacteristics.removeAll() + mDescriptors.removeAll() + + mServicesArgs.removeAll() + mCharacteristicsArgs.removeAll() + mDescriptors.removeAll() + } + + func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result) -> Void) { + let advertisement = advertisementArgs.toAdvertisement() + mPeripheralManager.startAdvertising(advertisement) + mStartAdvertisingCompletion = completion + } + + func stopAdvertising() throws { + mPeripheralManager.stopAdvertising() + } + + func getMaximumNotifyLength(uuidArgs: String) throws -> Int64 { + guard let central = mCentrals[uuidArgs] else { + throw MyError.illegalArgument + } + let maximumNotifyLength = central.maximumUpdateValueLength + let maximumNotifyLengthArgs = maximumNotifyLength.toInt64() + return maximumNotifyLengthArgs + } + + func respond(hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData?, errorArgs: MyATTErrorArgs) throws { + guard let request = mRequests.removeValue(forKey: hashCodeArgs) else { + throw MyError.illegalArgument + } + if valueArgs != nil { + request.value = valueArgs!.data + } + let error = errorArgs.toError() + mPeripheralManager.respond(to: request, withResult: error) + } + + func updateValue(hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, uuidsArgs: [String]?) throws -> Bool { + let centrals = try uuidsArgs?.map { uuidArgs in + guard let central = self.mCentrals[uuidArgs] else { + throw MyError.illegalArgument + } + return central + } + guard let characteristic = mCharacteristics[hashCodeArgs] else { + throw MyError.illegalArgument + } + let value = valueArgs.data + let updated = mPeripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals) + return updated } func didUpdateState(peripheral: CBPeripheralManager) { let state = peripheral.state let stateArgs = state.toArgs() - let stateNumberArgs = stateArgs.rawValue.toInt64() - _api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in } + mAPI.onStateChanged(stateArgs: stateArgs) { _ in } } func didAdd(peripheral: CBPeripheralManager, service: CBService, error: Error?) { - guard let completion = _addServiceCompletion else { + guard let completion = mAddServiceCompletion else { return } - _addServiceCompletion = nil + mAddServiceCompletion = nil if error == nil { completion(.success(())) } else { @@ -225,10 +198,10 @@ class MyPeripheralManager: MyPeripheralManagerHostApi { } func didStartAdvertising(peripheral: CBPeripheralManager, error: Error?) { - guard let completion = _startAdvertisingCompletion else { + guard let completion = mStartAdvertisingCompletion else { return } - _startAdvertisingCompletion = nil + mStartAdvertisingCompletion = nil if error == nil { completion(.success(())) } else { @@ -237,107 +210,152 @@ class MyPeripheralManager: MyPeripheralManagerHostApi { } func didReceiveRead(peripheral: CBPeripheralManager, request: CBATTRequest) { + let hashCodeArgs = request.hash.toInt64() let central = request.central let centralArgs = central.toArgs() - _centrals[centralArgs.uuidArgs] = central + mCentrals[centralArgs.uuidArgs] = central let characteristic = request.characteristic - let hashCode = characteristic.hash - guard let characteristicArgs = _characteristicsArgs[hashCode] else { - _peripheralManager.respond(to: request, withResult: .attributeNotFound) + guard let characteristicArgs = mCharacteristicsArgs[characteristic.hash] else { + mPeripheralManager.respond(to: request, withResult: .attributeNotFound) return } - let hashCodeArgs = characteristicArgs.hashCodeArgs - let idArgs = request.hash.toInt64() + let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs + let value = request.value + let valueArgs = value == nil ? nil : FlutterStandardTypedData(bytes: value!) let offsetArgs = request.offset.toInt64() - _requests[idArgs] = request - _api.onCharacteristicReadRequest(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, idArgs: idArgs, offsetArgs: offsetArgs) {_ in } + let requestArgs = MyATTRequestArgs(hashCodeArgs: hashCodeArgs, centralArgs: centralArgs, characteristicHashCodeArgs: characteristicHashCodeArgs, valueArgs: valueArgs, offsetArgs: offsetArgs) + mRequests[hashCodeArgs] = request + mAPI.didReceiveRead(requestArgs: requestArgs) { _ 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 + var requestsArgs = [MyATTRequestArgs]() + for request in requests { + let hashCodeArgs = request.hash.toInt64() + let central = request.central + let centralArgs = central.toArgs() + mCentrals[centralArgs.uuidArgs] = central + let characteristic = request.characteristic + guard let characteristicArgs = mCharacteristicsArgs[characteristic.hash] else { + mPeripheralManager.respond(to: request, withResult: .attributeNotFound) + return + } + let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs + let value = request.value + let valueArgs = value == nil ? nil : FlutterStandardTypedData(bytes: value!) + let offsetArgs = request.offset.toInt64() + let requestArgs = MyATTRequestArgs(hashCodeArgs: hashCodeArgs, centralArgs: centralArgs, characteristicHashCodeArgs: characteristicHashCodeArgs, valueArgs: valueArgs, offsetArgs: offsetArgs) + requestsArgs.append(requestArgs) + } guard let request = requests.first else { return } - if requests.count > 1 { - // TODO: 支持多写入请求,暂时不清楚此处应如何处理 - let result = CBATTError.requestNotSupported - _peripheralManager.respond(to: request, withResult: result) + guard let requestArgs = requestsArgs.first else { 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 } + self.mRequests[requestArgs.hashCodeArgs] = request + mAPI.didReceiveWrite(requestsArgs: requestsArgs) { _ in } } func didSubscribeTo(peripheral: CBPeripheralManager, central: CBCentral, characteristic: CBCharacteristic) { let centralArgs = central.toArgs() - _centrals[centralArgs.uuidArgs] = central + mCentrals[centralArgs.uuidArgs] = central let hashCode = characteristic.hash - guard let characteristicArgs = _characteristicsArgs[hashCode] else { + guard let characteristicArgs = mCharacteristicsArgs[hashCode] else { return } let hashCodeArgs = characteristicArgs.hashCodeArgs let stateArgs = true - _api.onCharacteristicNotifyStateChanged(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, stateArgs: stateArgs) {_ in } + mAPI.onCharacteristicNotifyStateChanged(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, stateArgs: stateArgs) { _ in } } func didUnsubscribeFrom(peripheral: CBPeripheralManager, central: CBCentral, characteristic: CBCharacteristic) { let centralArgs = central.toArgs() - _centrals[centralArgs.uuidArgs] = central + mCentrals[centralArgs.uuidArgs] = central let hashCode = characteristic.hash - guard let characteristicArgs = _characteristicsArgs[hashCode] else { + guard let characteristicArgs = mCharacteristicsArgs[hashCode] else { return } let hashCodeArgs = characteristicArgs.hashCodeArgs let stateArgs = false - _api.onCharacteristicNotifyStateChanged(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, stateArgs: stateArgs) {_ in } + mAPI.onCharacteristicNotifyStateChanged(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, stateArgs: stateArgs) { _ in } } func isReadyToUpdateSubscribers(peripheral: CBPeripheralManager) { - let callbacks = _isReadyToUpdateSubscribersCallbacks - _isReadyToUpdateSubscribersCallbacks.removeAll() - for callback in callbacks { - callback() - } + mAPI.isReady() { _ in } } - private func _clearState() { - if(_peripheralManager.isAdvertising) { - _peripheralManager.stopAdvertising() + private func addServiceArgs(_ serviceArgs: MyMutableGATTServiceArgs) throws -> CBMutableService { + let service = serviceArgs.toService() + mServicesArgs[service.hash] = serviceArgs + mServices[serviceArgs.hashCodeArgs] = service + var includedServices = [CBService]() + let includedServicesArgs = serviceArgs.includedServicesArgs + for args in includedServicesArgs { + guard let includedServiceArgs = args else { + throw MyError.illegalArgument + } + let includedService = try addServiceArgs(includedServiceArgs) + self.mServicesArgs[includedService.hash] = includedServiceArgs + self.mServices[includedServiceArgs.hashCodeArgs] = includedService + includedServices.append(includedService) } - - _servicesArgs.removeAll() - _characteristicsArgs.removeAll() - _descriptorsArgs.removeAll() - - _centrals.removeAll() - _services.removeAll() - _characteristics.removeAll() - _descriptors.removeAll() - _requests.removeAll() - - _addServiceCompletion = nil - _startAdvertisingCompletion = nil - _isReadyToUpdateSubscribersCallbacks.removeAll() + service.includedServices = includedServices + var characteristics = [CBMutableCharacteristic]() + let characteristicsArgs = serviceArgs.characteristicsArgs + for args in characteristicsArgs { + guard let characteristicArgs = args else { + throw MyError.illegalArgument + } + let characteristic = characteristicArgs.toCharacteristic() + self.mCharacteristicsArgs[characteristic.hash] = characteristicArgs + self.mCharacteristics[characteristicArgs.hashCodeArgs] = characteristic + characteristics.append(characteristic) + var descriptors = [CBMutableDescriptor]() + let descriptorsArgs = characteristicArgs.descriptorsArgs + for args in descriptorsArgs { + guard let descriptorArgs = args else { + continue + } + let descriptor = descriptorArgs.toDescriptor() + self.mDescriptorsArgs[descriptor.hash] = descriptorArgs + self.mDescriptors[descriptorArgs.hashCodeArgs] = descriptor + descriptors.append(descriptor) + } + characteristic.descriptors = descriptors + } + service.characteristics = characteristics + return service + } + + private func removeServiceArgs(_ serviceArgs: MyMutableGATTServiceArgs) throws { + for args in serviceArgs.includedServicesArgs { + guard let includedServiceArgs = args else { + throw MyError.illegalArgument + } + try removeServiceArgs(includedServiceArgs) + } + for args in serviceArgs.characteristicsArgs { + guard let characteristicArgs = args else { + throw MyError.illegalArgument + } + for args in characteristicArgs.descriptorsArgs { + guard let descriptorArgs = args else { + throw MyError.illegalArgument + } + guard let descriptor = mDescriptors.removeValue(forKey: descriptorArgs.hashCodeArgs) else { + throw MyError.illegalArgument + } + mDescriptorsArgs.removeValue(forKey: descriptor.hash) + } + guard let characteristic = mCharacteristics.removeValue(forKey: characteristicArgs.hashCodeArgs) else { + throw MyError.illegalArgument + } + mCharacteristicsArgs.removeValue(forKey: characteristic.hash) + } + guard let service = mServices.removeValue(forKey: serviceArgs.hashCodeArgs) else { + throw MyError.illegalArgument + } + mServicesArgs.removeValue(forKey: service.hash) } } diff --git a/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManagerDelegate.swift b/bluetooth_low_energy_darwin/darwin/Classes/MyPeripheralManagerDelegate.swift index 87d66df..72293f9 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 { - private let _peripheralManager: MyPeripheralManager + private let mPeripheralManager: MyPeripheralManager init(peripheralManager: MyPeripheralManager) { - _peripheralManager = peripheralManager + self.mPeripheralManager = peripheralManager } func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) { - _peripheralManager.didUpdateState(peripheral: peripheral) + mPeripheralManager.didUpdateState(peripheral: peripheral) } func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) { - _peripheralManager.didAdd(peripheral: peripheral, service: service, error: error) + mPeripheralManager.didAdd(peripheral: peripheral, service: service, error: error) } func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) { - _peripheralManager.didStartAdvertising(peripheral: peripheral, error: error) + mPeripheralManager.didStartAdvertising(peripheral: peripheral, error: error) } func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) { - _peripheralManager.didReceiveRead(peripheral: peripheral, request: request) + mPeripheralManager.didReceiveRead(peripheral: peripheral, request: request) } func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) { - _peripheralManager.didReceiveWrite(peripheral: peripheral, requests: requests) + mPeripheralManager.didReceiveWrite(peripheral: peripheral, requests: requests) } func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) { - _peripheralManager.didSubscribeTo(peripheral: peripheral, central: central, characteristic: characteristic) + mPeripheralManager.didSubscribeTo(peripheral: peripheral, central: central, characteristic: characteristic) } func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) { - _peripheralManager.didUnsubscribeFrom(peripheral: peripheral, central: central, characteristic: characteristic) + mPeripheralManager.didUnsubscribeFrom(peripheral: peripheral, central: central, characteristic: characteristic) } func peripheralManagerIsReady(toUpdateSubscribers peripheral: CBPeripheralManager) { - _peripheralManager.isReadyToUpdateSubscribers(peripheral: peripheral) + mPeripheralManager.isReadyToUpdateSubscribers(peripheral: peripheral) } } diff --git a/bluetooth_low_energy_darwin/darwin/bluetooth_low_energy_darwin.podspec b/bluetooth_low_energy_darwin/darwin/bluetooth_low_energy_darwin.podspec index 04b8bdb..66af906 100644 --- a/bluetooth_low_energy_darwin/darwin/bluetooth_low_energy_darwin.podspec +++ b/bluetooth_low_energy_darwin/darwin/bluetooth_low_energy_darwin.podspec @@ -4,24 +4,24 @@ # Pod::Spec.new do |s| s.name = 'bluetooth_low_energy_darwin' - s.version = '2.0.2' - s.summary = 'iOS and macOS implementation of the bluetooth_low_energy plugin.' + s.version = '0.0.1' + s.summary = 'A new Flutter plugin project.' s.description = <<-DESC -iOS and macOS implementation of the bluetooth_low_energy plugin. +A new Flutter plugin project. DESC - s.homepage = 'https://github.com/yanshouwang/bluetooth_low_energy' + s.homepage = 'http://example.com' s.license = { :file => '../LICENSE' } - s.author = { 'yanshouwang' => 'yanshouwang@outlook.com' } - + s.author = { 'Your Company' => 'email@example.com' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.ios.dependency 'Flutter' - s.ios.deployment_target = '11.0' + s.ios.deployment_target = '12.0' s.osx.dependency 'FlutterMacOS' s.osx.deployment_target = '10.11' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } s.swift_version = '5.0' end diff --git a/bluetooth_low_energy_darwin/example/.gitignore b/bluetooth_low_energy_darwin/example/.gitignore index 24476c5..29a3a50 100644 --- a/bluetooth_low_energy_darwin/example/.gitignore +++ b/bluetooth_low_energy_darwin/example/.gitignore @@ -27,7 +27,6 @@ migrate_working_dir/ .dart_tool/ .flutter-plugins .flutter-plugins-dependencies -.packages .pub-cache/ .pub/ /build/ diff --git a/bluetooth_low_energy_darwin/example/README.md b/bluetooth_low_energy_darwin/example/README.md index 13063a6..a7bb85f 100644 --- a/bluetooth_low_energy_darwin/example/README.md +++ b/bluetooth_low_energy_darwin/example/README.md @@ -1,6 +1,6 @@ -# bluetooth_low_energy_example +# bluetooth_low_energy_darwin_example -Demonstrates how to use the bluetooth_low_energy plugin. +Demonstrates how to use the bluetooth_low_energy_darwin plugin. ## Getting Started diff --git a/bluetooth_low_energy_darwin/example/integration_test/plugin_integration_test.dart b/bluetooth_low_energy_darwin/example/integration_test/plugin_integration_test.dart index c03e5a8..d53d2ae 100644 --- a/bluetooth_low_energy_darwin/example/integration_test/plugin_integration_test.dart +++ b/bluetooth_low_energy_darwin/example/integration_test/plugin_integration_test.dart @@ -6,17 +6,8 @@ // For more information about Flutter integration tests, please see // https://docs.flutter.dev/cookbook/testing/integration/introduction -// import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - // testWidgets('getPlatformVersion test', (WidgetTester tester) async { - // final BluetoothLowEnergy plugin = BluetoothLowEnergy(); - // final String? version = await plugin.getPlatformVersion(); - // // The version string depends on the host platform running the test, so - // // just assert that some non-empty string is returned. - // expect(version?.isNotEmpty, true); - // }); } diff --git a/bluetooth_low_energy_darwin/example/ios/Flutter/AppFrameworkInfo.plist b/bluetooth_low_energy_darwin/example/ios/Flutter/AppFrameworkInfo.plist index 9625e10..7c56964 100644 --- a/bluetooth_low_energy_darwin/example/ios/Flutter/AppFrameworkInfo.plist +++ b/bluetooth_low_energy_darwin/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/bluetooth_low_energy_darwin/example/ios/Podfile b/bluetooth_low_energy_darwin/example/ios/Podfile index fdcc671..d97f17e 100644 --- a/bluetooth_low_energy_darwin/example/ios/Podfile +++ b/bluetooth_low_energy_darwin/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/bluetooth_low_energy_darwin/example/ios/Podfile.lock b/bluetooth_low_energy_darwin/example/ios/Podfile.lock index 90d7b93..66cdac3 100644 --- a/bluetooth_low_energy_darwin/example/ios/Podfile.lock +++ b/bluetooth_low_energy_darwin/example/ios/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - bluetooth_low_energy_darwin (2.0.2): + - bluetooth_low_energy_darwin (0.0.1): - Flutter - FlutterMacOS - Flutter (1.0.0) @@ -20,10 +20,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/integration_test/ios" SPEC CHECKSUMS: - bluetooth_low_energy_darwin: e37c1af3337ebc99a60807137dc5e47c6195eac7 - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - integration_test: 13825b8a9334a850581300559b8839134b124670 + bluetooth_low_energy_darwin: 764d8d1ae5abefbcdb839e812b4b25c0061fcf8b + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4 -PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 +PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/bluetooth_low_energy_darwin/example/ios/Runner.xcodeproj/project.pbxproj b/bluetooth_low_energy_darwin/example/ios/Runner.xcodeproj/project.pbxproj index a833137..4197358 100644 --- a/bluetooth_low_energy_darwin/example/ios/Runner.xcodeproj/project.pbxproj +++ b/bluetooth_low_energy_darwin/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,13 +9,13 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; - 34E52FEFFBA34148D7F4058F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2353C45A2E0DF37667D194A1 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 43A108903466BBFDEDE4985F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C27D70257F13C733066C27B6 /* Pods_Runner.framework */; }; + 5A4AF0726D433429B5CD6958 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0E724D72C393B2DFFF52340 /* Pods_RunnerTests.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - F6040B5AA5E5A63D395CB345 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 076A35C4833796B0B42E0237 /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,21 +42,19 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 03284B9B33BEA09EAC1AFD47 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 076A35C4833796B0B42E0237 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2353C45A2E0DF37667D194A1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 2EE38EE18344248FD19187BD /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 4BF6EEA10984CDD1A14F950F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 54C98B3103488CA87E98A6F4 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 710DD54C8BBDEA22C3F7F293 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 519342676F27E83FC65C1C80 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 5BAF5F8B046ABA562E5302F7 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 93DA77ACC45858A97F558838 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 7BB1125E379070B4AAAFC962 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 7CC3DA72E0F6DAA790F643B9 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -64,38 +62,31 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 9C6D6BDEFF4878166372D24A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + C27D70257F13C733066C27B6 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D3B21D3904F1EFAAEBD1A7FC /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + F0E724D72C393B2DFFF52340 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 6B5302E24483403072236BBB /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - F6040B5AA5E5A63D395CB345 /* Pods_RunnerTests.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 34E52FEFFBA34148D7F4058F /* Pods_Runner.framework in Frameworks */, + 43A108903466BBFDEDE4985F /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D87DCF5A67E56FE2ED284031 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5A4AF0726D433429B5CD6958 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0FAACD3CD9E188E482384358 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 2353C45A2E0DF37667D194A1 /* Pods_Runner.framework */, - 076A35C4833796B0B42E0237 /* Pods_RunnerTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -104,17 +95,13 @@ path = RunnerTests; sourceTree = ""; }; - 3C9A3D0714D61C07F02BFBB4 /* Pods */ = { + 711971ED4BF1C4587FC0E521 /* Frameworks */ = { isa = PBXGroup; children = ( - 93DA77ACC45858A97F558838 /* Pods-Runner.debug.xcconfig */, - 4BF6EEA10984CDD1A14F950F /* Pods-Runner.release.xcconfig */, - 03284B9B33BEA09EAC1AFD47 /* Pods-Runner.profile.xcconfig */, - 9C6D6BDEFF4878166372D24A /* Pods-RunnerTests.debug.xcconfig */, - 54C98B3103488CA87E98A6F4 /* Pods-RunnerTests.release.xcconfig */, - 710DD54C8BBDEA22C3F7F293 /* Pods-RunnerTests.profile.xcconfig */, + C27D70257F13C733066C27B6 /* Pods_Runner.framework */, + F0E724D72C393B2DFFF52340 /* Pods_RunnerTests.framework */, ); - path = Pods; + name = Frameworks; sourceTree = ""; }; 9740EEB11CF90186004384FC /* Flutter */ = { @@ -135,8 +122,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, - 3C9A3D0714D61C07F02BFBB4 /* Pods */, - 0FAACD3CD9E188E482384358 /* Frameworks */, + E27789BD215E61248833A6B2 /* Pods */, + 711971ED4BF1C4587FC0E521 /* Frameworks */, ); sourceTree = ""; }; @@ -164,6 +151,19 @@ path = Runner; sourceTree = ""; }; + E27789BD215E61248833A6B2 /* Pods */ = { + isa = PBXGroup; + children = ( + 7CC3DA72E0F6DAA790F643B9 /* Pods-Runner.debug.xcconfig */, + 7BB1125E379070B4AAAFC962 /* Pods-Runner.release.xcconfig */, + 2EE38EE18344248FD19187BD /* Pods-Runner.profile.xcconfig */, + 5BAF5F8B046ABA562E5302F7 /* Pods-RunnerTests.debug.xcconfig */, + D3B21D3904F1EFAAEBD1A7FC /* Pods-RunnerTests.release.xcconfig */, + 519342676F27E83FC65C1C80 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -171,10 +171,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - A3C3FEADF4EBF04832B143F6 /* [CP] Check Pods Manifest.lock */, + 7BD6B85155ECEFD741D916A4 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, - 6B5302E24483403072236BBB /* Frameworks */, + D87DCF5A67E56FE2ED284031 /* Frameworks */, ); buildRules = ( ); @@ -190,14 +190,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 67E1A4356061A10A5EC11286 /* [CP] Check Pods Manifest.lock */, + 07FF42C617DF912EF8EA67AC /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 066C9063BC370AC8304B71D9 /* [CP] Embed Pods Frameworks */, + C9B7C0BA95EE7285CDB16832 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -215,7 +215,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { @@ -269,40 +269,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 066C9063BC370AC8304B71D9 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 67E1A4356061A10A5EC11286 /* [CP] Check Pods Manifest.lock */ = { + 07FF42C617DF912EF8EA67AC /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -324,22 +291,23 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); - name = "Run Script"; + name = "Thin Binary"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - A3C3FEADF4EBF04832B143F6 /* [CP] Check Pods Manifest.lock */ = { + 7BD6B85155ECEFD741D916A4 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -361,6 +329,38 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + C9B7C0BA95EE7285CDB16832 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -415,6 +415,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -444,6 +445,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -452,7 +454,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -468,14 +470,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = T5YVNX2NAC; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -485,14 +487,14 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9C6D6BDEFF4878166372D24A /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 5BAF5F8B046ABA562E5302F7 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -503,14 +505,14 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 54C98B3103488CA87E98A6F4 /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = D3B21D3904F1EFAAEBD1A7FC /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -519,14 +521,14 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 710DD54C8BBDEA22C3F7F293 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = 519342676F27E83FC65C1C80 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -537,6 +539,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -566,6 +569,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -580,7 +584,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -592,6 +596,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -621,6 +626,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -629,7 +635,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -647,14 +653,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = T5YVNX2NAC; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -670,14 +676,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = T5YVNX2NAC; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/bluetooth_low_energy_darwin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/bluetooth_low_energy_darwin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 87131a0..8e3ca5d 100644 --- a/bluetooth_low_energy_darwin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/bluetooth_low_energy_darwin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Bluetooth Low Energy + Bluetooth Low Energy Darwin CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -15,7 +15,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - bluetooth_low_energy_example + bluetooth_low_energy_darwin_example CFBundlePackageType APPL CFBundleShortVersionString @@ -27,7 +27,7 @@ LSRequiresIPhoneOS NSBluetoothAlwaysUsageDescription - Hello Bluetooth LowEnergy! + Need to use the bluetooth to communicate with peripherals UIApplicationSupportsIndirectInputEvents UILaunchStoryboardName diff --git a/bluetooth_low_energy_darwin/example/ios/RunnerTests/RunnerTests.swift b/bluetooth_low_energy_darwin/example/ios/RunnerTests/RunnerTests.swift index 14dacc4..7db13c0 100644 --- a/bluetooth_low_energy_darwin/example/ios/RunnerTests/RunnerTests.swift +++ b/bluetooth_low_energy_darwin/example/ios/RunnerTests/RunnerTests.swift @@ -2,7 +2,7 @@ import Flutter import UIKit import XCTest -@testable import bluetooth_low_energy +@testable import bluetooth_low_energy_darwin // This demonstrates a simple unit test of the Swift portion of this plugin's implementation. // @@ -11,7 +11,7 @@ import XCTest class RunnerTests: XCTestCase { func testGetPlatformVersion() { - let plugin = BluetoothLowEnergyPlugin() + let plugin = BluetoothLowEnergyDarwinPlugin() let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) diff --git a/bluetooth_low_energy_darwin/example/lib/main.dart b/bluetooth_low_energy_darwin/example/lib/main.dart index 3cfddfa..a588ab4 100644 --- a/bluetooth_low_energy_darwin/example/lib/main.dart +++ b/bluetooth_low_energy_darwin/example/lib/main.dart @@ -1,12 +1,10 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:developer'; -import 'dart:typed_data'; -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:convert/convert.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; +import 'package:logging/logging.dart'; + +import 'router_config.dart'; void main() { runZonedGuarded(onStartUp, onCrashed); @@ -14,11 +12,7 @@ void main() { void onStartUp() async { Logger.root.onRecord.listen(onLogRecord); - // hierarchicalLoggingEnabled = true; - // CentralManager.instance.logLevel = Level.WARNING; - WidgetsFlutterBinding.ensureInitialized(); - await CentralManager.instance.setUp(); - await PeripheralManager.instance.setUp(); + hierarchicalLoggingEnabled = true; runApp(const MyApp()); } @@ -39,1124 +33,19 @@ void onLogRecord(LogRecord record) { ); } -class MyApp extends StatefulWidget { +class MyApp extends StatelessWidget { const MyApp({super.key}); - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData.light( - useMaterial3: true, - ).copyWith( + return MaterialApp.router( + routerConfig: routerConfig, + theme: ThemeData.light().copyWith( materialTapTargetSize: MaterialTapTargetSize.padded, ), - home: const HomeView(), - routes: { - 'peripheral': (context) { - final route = ModalRoute.of(context); - final eventArgs = route!.settings.arguments as DiscoveredEventArgs; - return PeripheralView( - eventArgs: eventArgs, - ); - }, - }, - ); - } -} - -class HomeView extends StatefulWidget { - 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(); - } -} - -class ScannerView extends StatefulWidget { - const ScannerView({super.key}); - - @override - State createState() => _ScannerViewState(); -} - -class _ScannerViewState extends State { - late final ValueNotifier state; - late final ValueNotifier discovering; - late final ValueNotifier> discoveredEventArgs; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription discoveredSubscription; - - @override - void initState() { - super.initState(); - state = ValueNotifier(BluetoothLowEnergyState.unknown); - discovering = ValueNotifier(false); - discoveredEventArgs = ValueNotifier([]); - stateChangedSubscription = CentralManager.instance.stateChanged.listen( - (eventArgs) { - state.value = eventArgs.state; - }, - ); - discoveredSubscription = CentralManager.instance.discovered.listen( - (eventArgs) { - final items = discoveredEventArgs.value; - final i = items.indexWhere( - (item) => item.peripheral == eventArgs.peripheral, - ); - if (i < 0) { - discoveredEventArgs.value = [...items, eventArgs]; - } else { - items[i] = eventArgs; - discoveredEventArgs.value = [...items]; - } - }, - ); - _initialize(); - } - - void _initialize() async { - state.value = await CentralManager.instance.getState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), - ); - } - - PreferredSizeWidget buildAppBar(BuildContext context) { - return AppBar( - title: const Text('Scanner'), - actions: [ - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: discovering, - builder: (context, discovering, child) { - return TextButton( - onPressed: state == BluetoothLowEnergyState.poweredOn - ? () async { - if (discovering) { - await stopDiscovery(); - } else { - await startDiscovery(); - } - } - : null, - child: Text( - discovering ? 'END' : 'BEGIN', - ), - ); - }, - ); - }, - ), - ], - ); - } - - Future startDiscovery() async { - discoveredEventArgs.value = []; - await CentralManager.instance.startDiscovery(); - discovering.value = true; - } - - Future stopDiscovery() async { - await CentralManager.instance.stopDiscovery(); - discovering.value = false; - } - - Widget buildBody(BuildContext context) { - return ValueListenableBuilder( - valueListenable: discoveredEventArgs, - builder: (context, discoveredEventArgs, child) { - // final items = discoveredEventArgs; - final items = discoveredEventArgs - .where((eventArgs) => eventArgs.advertisement.name != null) - .toList(); - return ListView.separated( - itemBuilder: (context, i) { - final theme = Theme.of(context); - final item = items[i]; - final uuid = item.peripheral.uuid; - final rssi = item.rssi; - final advertisement = item.advertisement; - final name = advertisement.name; - return ListTile( - onTap: () async { - final discovering = this.discovering.value; - if (discovering) { - await stopDiscovery(); - } - if (!mounted) { - throw UnimplementedError(); - } - await Navigator.of(context).pushNamed( - 'peripheral', - arguments: item, - ); - if (discovering) { - await startDiscovery(); - } - }, - onLongPress: () async { - await showModalBottomSheet( - context: context, - builder: (context) { - return BottomSheet( - onClosing: () {}, - clipBehavior: Clip.antiAlias, - builder: (context) { - final manufacturerSpecificData = - advertisement.manufacturerSpecificData; - return ListView.separated( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 40.0, - ), - itemBuilder: (context, i) { - const idWidth = 80.0; - if (i == 0) { - return const Row( - children: [ - SizedBox( - width: idWidth, - child: Text('ID'), - ), - Expanded( - child: Text('DATA'), - ), - ], - ); - } else { - final id = - '0x${manufacturerSpecificData!.id.toRadixString(16).padLeft(4, '0')}'; - final value = - hex.encode(manufacturerSpecificData.data); - return Row( - children: [ - SizedBox( - width: idWidth, - child: Text(id), - ), - Expanded( - child: Text(value), - ), - ], - ); - } - }, - separatorBuilder: (context, i) { - return const Divider(); - }, - itemCount: manufacturerSpecificData == null ? 1 : 2, - ); - }, - ); - }, - ); - }, - title: Text(name ?? 'N/A'), - subtitle: Text( - '$uuid', - style: theme.textTheme.bodySmall, - softWrap: false, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - RssiWidget(rssi), - Text('$rssi'), - ], - ), - ); - }, - separatorBuilder: (context, i) { - return const Divider( - height: 0.0, - ); - }, - itemCount: items.length, - ); - }, - ); - } - - @override - void dispose() { - super.dispose(); - stateChangedSubscription.cancel(); - discoveredSubscription.cancel(); - state.dispose(); - discovering.dispose(); - discoveredEventArgs.dispose(); - } -} - -class PeripheralView extends StatefulWidget { - final DiscoveredEventArgs eventArgs; - - const PeripheralView({ - super.key, - required this.eventArgs, - }); - - @override - State createState() => _PeripheralViewState(); -} - -class _PeripheralViewState extends 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> logs; - late final TextEditingController writeController; - late final StreamSubscription connectionStateChangedSubscription; - late final StreamSubscription characteristicNotifiedSubscription; - - @override - void initState() { - super.initState(); - eventArgs = widget.eventArgs; - connectionState = ValueNotifier(false); - services = ValueNotifier([]); - characteristics = ValueNotifier([]); - service = ValueNotifier(null); - characteristic = ValueNotifier(null); - writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); - logs = ValueNotifier([]); - writeController = TextEditingController(); - connectionStateChangedSubscription = - CentralManager.instance.connectionStateChanged.listen( - (eventArgs) { - if (eventArgs.peripheral != this.eventArgs.peripheral) { - return; - } - final connectionState = eventArgs.connectionState; - this.connectionState.value = connectionState; - if (!connectionState) { - services.value = []; - characteristics.value = []; - service.value = null; - characteristic.value = null; - logs.value = []; - } - }, - ); - characteristicNotifiedSubscription = - CentralManager.instance.characteristicNotified.listen( - (eventArgs) { - // final characteristic = this.characteristic.value; - // if (eventArgs.characteristic != characteristic) { - // return; - // } - const type = LogType.notify; - final log = Log(type, eventArgs.value); - logs.value = [ - ...logs.value, - log, - ]; - }, - ); - } - - @override - Widget build(BuildContext context) { - return PopScope( - onPopInvoked: (didPop) async { - if (connectionState.value) { - final peripheral = eventArgs.peripheral; - await CentralManager.instance.disconnect(peripheral); - } - }, - child: Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), + darkTheme: ThemeData.dark().copyWith( + materialTapTargetSize: MaterialTapTargetSize.padded, ), ); } - - PreferredSizeWidget buildAppBar(BuildContext context) { - final title = eventArgs.advertisement.name ?? ''; - return AppBar( - title: Text(title), - actions: [ - ValueListenableBuilder( - valueListenable: connectionState, - builder: (context, state, child) { - return TextButton( - onPressed: () async { - final peripheral = eventArgs.peripheral; - if (state) { - await CentralManager.instance.disconnect(peripheral); - } else { - await CentralManager.instance.connect(peripheral); - services.value = - await CentralManager.instance.discoverGATT(peripheral); - } - }, - child: Text(state ? 'DISCONNECT' : 'CONNECT'), - ); - }, - ), - ], - ); - } - - Widget buildBody(BuildContext context) { - final theme = Theme.of(context); - return Container( - margin: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ValueListenableBuilder( - valueListenable: services, - builder: (context, services, child) { - final items = services.map((service) { - return DropdownMenuItem( - value: service, - child: Text( - '${service.uuid}', - style: theme.textTheme.bodyMedium, - ), - ); - }).toList(); - return ValueListenableBuilder( - valueListenable: service, - builder: (context, service, child) { - return DropdownButton( - isExpanded: true, - items: items, - hint: const Text('CHOOSE A SERVICE'), - value: service, - onChanged: (service) async { - this.service.value = service; - characteristic.value = null; - if (service == null) { - return; - } - characteristics.value = service.characteristics; - }, - ); - }, - ); - }, - ), - ValueListenableBuilder( - valueListenable: characteristics, - builder: (context, characteristics, child) { - final items = characteristics.map((characteristic) { - return DropdownMenuItem( - value: characteristic, - child: Text( - '${characteristic.uuid}', - style: theme.textTheme.bodyMedium, - ), - ); - }).toList(); - return ValueListenableBuilder( - valueListenable: characteristic, - builder: (context, characteristic, child) { - return DropdownButton( - isExpanded: true, - items: items, - 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; - } - }, - ); - }, - ); - }, - ), - Expanded( - child: 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 = 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, - ); - }, - ), - ), - 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: [ - ElevatedButton( - onPressed: characteristic != null && canNotify - ? () async { - await CentralManager.instance - .setCharacteristicNotifyState( - characteristic, - state: true, - ); - } - : null, - child: const Text('NOTIFY'), - ), - const SizedBox(width: 8.0), - ElevatedButton( - onPressed: characteristic != null && canRead - ? () async { - final value = await CentralManager.instance - .readCharacteristic(characteristic); - const type = LogType.read; - final log = Log(type, value); - logs.value = [...logs.value, log]; - } - : null, - child: const Text('READ'), - ) - ], - ), - ), - 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), - ], - ); - }, - ), - ], - ), - ); - } - - @override - void dispose() { - super.dispose(); - connectionStateChangedSubscription.cancel(); - characteristicNotifiedSubscription.cancel(); - connectionState.dispose(); - services.dispose(); - characteristics.dispose(); - service.dispose(); - characteristic.dispose(); - writeType.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 characteristicReadSubscription; - late final StreamSubscription characteristicWrittenSubscription; - late final StreamSubscription characteristicNotifyStateChangedSubscription; - - @override - void initState() { - super.initState(); - state = ValueNotifier(BluetoothLowEnergyState.unknown); - advertising = ValueNotifier(false); - logs = ValueNotifier([]); - stateChangedSubscription = PeripheralManager.instance.stateChanged.listen( - (eventArgs) { - state.value = eventArgs.state; - }, - ); - characteristicReadSubscription = - PeripheralManager.instance.characteristicRead.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final value = eventArgs.value; - final log = Log( - LogType.read, - value, - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', - ); - logs.value = [ - ...logs.value, - log, - ]; - }, - ); - characteristicWrittenSubscription = - PeripheralManager.instance.characteristicWritten.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final value = eventArgs.value; - final log = Log( - LogType.write, - value, - 'central: ${central.uuid}; characteristic: ${characteristic.uuid}', - ); - logs.value = [ - ...logs.value, - log, - ]; - }, - ); - characteristicNotifyStateChangedSubscription = - PeripheralManager.instance.characteristicNotifyStateChanged.listen( - (eventArgs) async { - final central = eventArgs.central; - final characteristic = eventArgs.characteristic; - final state = eventArgs.state; - final log = Log( - LogType.notify, - 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 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 - 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.instance.clearServices(); - final elements = List.generate(1000, (i) => i % 256); - final value = Uint8List.fromList(elements); - final service = GattService( - uuid: UUID.short(100), - characteristics: [ - GattCharacteristic( - uuid: UUID.short(200), - properties: [ - GattCharacteristicProperty.read, - ], - value: value, - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(201), - properties: [ - GattCharacteristicProperty.write, - GattCharacteristicProperty.writeWithoutResponse, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(202), - properties: [ - GattCharacteristicProperty.notify, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(203), - properties: [ - GattCharacteristicProperty.indicate, - ], - descriptors: [], - ), - GattCharacteristic( - uuid: UUID.short(204), - properties: [ - GattCharacteristicProperty.read, - GattCharacteristicProperty.write, - GattCharacteristicProperty.writeWithoutResponse, - GattCharacteristicProperty.notify, - GattCharacteristicProperty.indicate, - ], - value: value, - descriptors: [], - ), - ], - ); - await PeripheralManager.instance.addService(service); - final advertisement = Advertisement( - name: 'le12138', - manufacturerSpecificData: ManufacturerSpecificData( - id: 0x2e19, - data: Uint8List.fromList([0x01, 0x02, 0x03]), - ), - ); - await PeripheralManager.instance.startAdvertising(advertisement); - advertising.value = true; - } - - Future stopAdvertising() async { - await PeripheralManager.instance.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(); - characteristicReadSubscription.cancel(); - characteristicWrittenSubscription.cancel(); - characteristicNotifyStateChangedSubscription.cancel(); - state.dispose(); - advertising.dispose(); - logs.dispose(); - } -} - -class Log { - final DateTime time; - final LogType type; - final Uint8List value; - final String? detail; - - Log( - this.type, - this.value, [ - this.detail, - ]) : time = DateTime.now(); - - @override - String toString() { - final type = this.type.toString().split('.').last; - final formatter = DateFormat.Hms(); - final time = formatter.format(this.time); - final message = hex.encode(value); - if (detail == null) { - return '[$type]$time: $message'; - } else { - return '[$type]$time: $message /* $detail */'; - } - } -} - -enum LogType { - read, - write, - notify, - error, -} - -class RssiWidget extends StatelessWidget { - final int rssi; - - const RssiWidget( - this.rssi, { - super.key, - }); - - @override - Widget build(BuildContext context) { - final IconData icon; - if (rssi > -70) { - icon = Icons.wifi_rounded; - } else if (rssi > -100) { - icon = Icons.wifi_2_bar_rounded; - } else { - icon = Icons.wifi_1_bar_rounded; - } - return Icon(icon); - } } diff --git a/bluetooth_low_energy_darwin/example/lib/models.dart b/bluetooth_low_energy_darwin/example/lib/models.dart new file mode 100644 index 0000000..627f4d2 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/models.dart @@ -0,0 +1 @@ +export 'models/log.dart'; diff --git a/bluetooth_low_energy_darwin/example/lib/models/log.dart b/bluetooth_low_energy_darwin/example/lib/models/log.dart new file mode 100644 index 0000000..2bcfbf7 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/models/log.dart @@ -0,0 +1,10 @@ +class Log { + final DateTime time; + final String type; + final String message; + + Log({ + required this.type, + required this.message, + }) : time = DateTime.now(); +} diff --git a/bluetooth_low_energy_darwin/example/lib/router_config.dart b/bluetooth_low_energy_darwin/example/lib/router_config.dart new file mode 100644 index 0000000..1d344c7 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/router_config.dart @@ -0,0 +1,85 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:collection/collection.dart'; +import 'package:go_router/go_router.dart'; + +import 'view_models.dart'; +import 'views.dart'; + +final routerConfig = GoRouter( + redirect: (context, state) { + if (state.matchedLocation == '/') { + return '/central'; + } + return null; + }, + routes: [ + StatefulShellRoute( + // builder: (context, state, navigationShell) { + // return HomeView(navigationShell: navigationShell); + // }, + builder: (context, state, navigationShell) => navigationShell, + navigatorContainerBuilder: (context, navigationShell, children) { + final navigators = children.mapIndexed( + (index, element) { + if (index == 0) { + return ViewModelBinding( + viewBuilder: (context) => element, + viewModelBuilder: (context) => CentralManagerViewModel(), + ); + } else { + return element; + } + }, + ).toList(); + return HomeView( + navigationShell: navigationShell, + navigators: navigators, + ); + }, + branches: [ + StatefulShellBranch( + routes: [ + GoRoute( + path: '/central', + builder: (context, state) { + return const CentralManagerView(); + }, + routes: [ + GoRoute( + path: ':uuid', + builder: (context, state) { + final uuidValue = state.pathParameters['uuid']!; + final uuid = UUID.fromString(uuidValue); + final viewModel = + ViewModel.of(context); + final eventArgs = viewModel.discoveries.firstWhere( + (discovery) => discovery.peripheral.uuid == uuid); + return ViewModelBinding( + viewBuilder: (context) => PeripheralView(), + viewModelBuilder: (context) => + PeripheralViewModel(eventArgs), + ); + }, + ), + ], + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/peripheral', + builder: (context, state) { + return ViewModelBinding( + viewBuilder: (context) => const PeripheralManagerView(), + viewModelBuilder: (context) => PeripheralManagerViewModel(), + ); + }, + ), + ], + ), + ], + ), + ], +); diff --git a/bluetooth_low_energy_darwin/example/lib/view_models.dart b/bluetooth_low_energy_darwin/example/lib/view_models.dart new file mode 100644 index 0000000..6c0c1fc --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/view_models.dart @@ -0,0 +1,6 @@ +export 'view_models/central_manager_view_model.dart'; +export 'view_models/peripheral_view_model.dart'; +export 'view_models/service_view_model.dart'; +export 'view_models/characteristic_view_model.dart'; +export 'view_models/descriptor_view_model.dart'; +export 'view_models/peripheral_manager_view_model.dart'; diff --git a/bluetooth_low_energy_darwin/example/lib/view_models/central_manager_view_model.dart b/bluetooth_low_energy_darwin/example/lib/view_models/central_manager_view_model.dart new file mode 100644 index 0000000..23abbdc --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/view_models/central_manager_view_model.dart @@ -0,0 +1,71 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:logging/logging.dart'; + +class CentralManagerViewModel extends ViewModel { + final CentralManager _manager; + final List _discoveries; + bool _discovering; + + late final StreamSubscription _stateChangedSubscription; + late final StreamSubscription _discoveredSubscription; + + CentralManagerViewModel() + : _manager = CentralManager()..logLevel = Level.INFO, + _discoveries = [], + _discovering = false { + _stateChangedSubscription = _manager.stateChanged.listen((eventArgs) { + notifyListeners(); + }); + _discoveredSubscription = _manager.discovered.listen((eventArgs) { + final peripheral = eventArgs.peripheral; + final index = _discoveries.indexWhere((i) => i.peripheral == peripheral); + if (index < 0) { + _discoveries.add(eventArgs); + } else { + _discoveries[index] = eventArgs; + } + notifyListeners(); + }); + } + + BluetoothLowEnergyState get state => _manager.state; + bool get discovering => _discovering; + List get discoveries => _discoveries; + + Future showAppSettings() async { + await _manager.showAppSettings(); + } + + Future startDiscovery({ + List? serviceUUIDs, + }) async { + if (_discovering) { + return; + } + _discoveries.clear(); + await _manager.startDiscovery( + serviceUUIDs: serviceUUIDs, + ); + _discovering = true; + notifyListeners(); + } + + Future stopDiscovery() async { + if (!_discovering) { + return; + } + await _manager.stopDiscovery(); + _discovering = false; + notifyListeners(); + } + + @override + void dispose() { + _stateChangedSubscription.cancel(); + _discoveredSubscription.cancel(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/view_models/characteristic_view_model.dart b/bluetooth_low_energy_darwin/example/lib/view_models/characteristic_view_model.dart new file mode 100644 index 0000000..b7a6706 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/view_models/characteristic_view_model.dart @@ -0,0 +1,145 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_darwin_example/models.dart'; +import 'package:clover/clover.dart'; + +import 'descriptor_view_model.dart'; + +class CharacteristicViewModel extends ViewModel { + final CentralManager _manager; + final Peripheral _peripheral; + final GATTCharacteristic _characteristic; + final List _descriptorViewModels; + final List _logs; + + GATTCharacteristicWriteType _writeType; + bool _notifyState; + + late final StreamSubscription _characteristicNotifiedSubscription; + + CharacteristicViewModel({ + required CentralManager manager, + required Peripheral peripheral, + required GATTCharacteristic characteristic, + }) : _manager = manager, + _peripheral = peripheral, + _characteristic = characteristic, + _descriptorViewModels = characteristic.descriptors + .map((descriptor) => DescriptorViewModel(descriptor)) + .toList(), + _logs = [], + _writeType = GATTCharacteristicWriteType.withResponse, + _notifyState = false { + if (!canWrite && canWriteWithoutResponse) { + _writeType = GATTCharacteristicWriteType.withoutResponse; + } + _characteristicNotifiedSubscription = + _manager.characteristicNotified.listen((eventArgs) { + if (eventArgs.characteristic != _characteristic) { + return; + } + final log = Log( + type: 'Notified', + message: '[${eventArgs.value.length}] ${eventArgs.value}', + ); + _logs.add(log); + notifyListeners(); + }); + } + + UUID get uuid => _characteristic.uuid; + bool get canRead => + _characteristic.properties.contains(GATTCharacteristicProperty.read); + bool get canWrite => + _characteristic.properties.contains(GATTCharacteristicProperty.write); + bool get canWriteWithoutResponse => _characteristic.properties + .contains(GATTCharacteristicProperty.writeWithoutResponse); + bool get canNotify => + _characteristic.properties.contains(GATTCharacteristicProperty.notify) || + _characteristic.properties.contains(GATTCharacteristicProperty.indicate); + List get descriptorViewModels => _descriptorViewModels; + List get logs => _logs; + GATTCharacteristicWriteType get writeType => _writeType; + bool get notifyState => _notifyState; + + Future read() async { + final value = await _manager.readCharacteristic( + _peripheral, + _characteristic, + ); + final log = Log( + type: 'Read', + message: '[${value.length}] $value', + ); + _logs.add(log); + notifyListeners(); + } + + void setWriteType(GATTCharacteristicWriteType type) { + if (type == GATTCharacteristicWriteType.withResponse && !canWrite) { + throw ArgumentError.value(type); + } + if (type == GATTCharacteristicWriteType.withoutResponse && + !canWriteWithoutResponse) { + throw ArgumentError.value(type); + } + _writeType = type; + notifyListeners(); + } + + Future write(Uint8List value) async { + // Fragments the value by maximumWriteLength. + final fragmentSize = await _manager.getMaximumWriteLength( + _peripheral, + type: writeType, + ); + var start = 0; + while (start < value.length) { + final end = start + fragmentSize; + final fragmentedValue = + end < value.length ? value.sublist(start, end) : value.sublist(start); + final type = writeType; + await _manager.writeCharacteristic( + _peripheral, + _characteristic, + value: fragmentedValue, + type: type, + ); + final log = Log( + type: type == GATTCharacteristicWriteType.withResponse + ? 'Write' + : 'Write without response', + message: '[${value.length}] $value', + ); + _logs.add(log); + notifyListeners(); + start = end; + } + } + + Future setNotifyState(bool state) async { + await _manager.setCharacteristicNotifyState( + _peripheral, + _characteristic, + state: state, + ); + _notifyState = state; + notifyListeners(); + } + + void clearLogs() { + _logs.clear(); + notifyListeners(); + } + + @override + void dispose() { + _characteristicNotifiedSubscription.cancel(); + for (var descriptorViewModel in descriptorViewModels) { + descriptorViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/view_models/descriptor_view_model.dart b/bluetooth_low_energy_darwin/example/lib/view_models/descriptor_view_model.dart new file mode 100644 index 0000000..a03bce4 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/view_models/descriptor_view_model.dart @@ -0,0 +1,10 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; + +class DescriptorViewModel extends ViewModel { + final GATTDescriptor _descriptor; + + DescriptorViewModel(this._descriptor); + + UUID get uuid => _descriptor.uuid; +} diff --git a/bluetooth_low_energy_darwin/example/lib/view_models/peripheral_manager_view_model.dart b/bluetooth_low_energy_darwin/example/lib/view_models/peripheral_manager_view_model.dart new file mode 100644 index 0000000..dd6dba6 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/view_models/peripheral_manager_view_model.dart @@ -0,0 +1,161 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_darwin_example/models.dart'; +import 'package:clover/clover.dart'; +import 'package:logging/logging.dart'; + +class PeripheralManagerViewModel extends ViewModel { + final PeripheralManager _manager; + final List _logs; + bool _advertising; + + late final StreamSubscription _stateChangedSubscription; + late final StreamSubscription _characteristicReadRequestedSubscription; + late final StreamSubscription _characteristicWriteRequestedSubscription; + late final StreamSubscription _characteristicNotifyStateChangedSubscription; + + PeripheralManagerViewModel() + : _manager = PeripheralManager()..logLevel = Level.INFO, + _logs = [], + _advertising = false { + _stateChangedSubscription = _manager.stateChanged.listen((eventArgs) { + notifyListeners(); + }); + _characteristicReadRequestedSubscription = + _manager.characteristicReadRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final log = Log( + type: 'Characteristic read requested', + message: '${central.uuid}, ${characteristic.uuid}, $offset', + ); + _logs.add(log); + notifyListeners(); + final elements = List.generate(100, (i) => i % 256); + final value = Uint8List.fromList(elements); + final trimmedValue = value.sublist(offset); + await _manager.respondReadRequestWithValue( + request, + value: trimmedValue, + ); + }); + _characteristicWriteRequestedSubscription = + _manager.characteristicWriteRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final value = request.value; + final log = Log( + type: 'Characteristic write requested', + message: + '[${value.length}] ${central.uuid}, ${characteristic.uuid}, $offset, $value', + ); + _logs.add(log); + notifyListeners(); + await _manager.respondWriteRequest(request); + }); + _characteristicNotifyStateChangedSubscription = + _manager.characteristicNotifyStateChanged.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final state = eventArgs.state; + final log = Log( + type: 'Characteristic notify state changed', + message: '${central.uuid}, ${characteristic.uuid}, $state', + ); + _logs.add(log); + notifyListeners(); + // Write someting to the central when notify started. + if (state) { + final maximumNotifyLength = + await _manager.getMaximumNotifyLength(central); + final elements = List.generate(maximumNotifyLength, (i) => i % 256); + final value = Uint8List.fromList(elements); + await _manager.notifyCharacteristic( + central, + characteristic, + value: value, + ); + } + }); + } + + BluetoothLowEnergyState get state => _manager.state; + bool get advertising => _advertising; + List get logs => _logs; + + Future showAppSettings() async { + await _manager.showAppSettings(); + } + + Future startAdvertising() async { + if (_advertising) { + return; + } + await _manager.removeAllServices(); + final elements = List.generate(100, (i) => i % 256); + final value = Uint8List.fromList(elements); + final service = GATTService( + uuid: UUID.short(100), + isPrimary: true, + includedServices: [], + characteristics: [ + GATTCharacteristic.immutable( + uuid: UUID.short(200), + value: value, + descriptors: [], + ), + GATTCharacteristic.mutable( + uuid: UUID.short(201), + properties: [ + GATTCharacteristicProperty.read, + GATTCharacteristicProperty.write, + GATTCharacteristicProperty.writeWithoutResponse, + GATTCharacteristicProperty.notify, + GATTCharacteristicProperty.indicate, + ], + permissions: [ + GATTCharacteristicPermission.read, + GATTCharacteristicPermission.write, + ], + descriptors: [], + ), + ], + ); + await _manager.addService(service); + final advertisement = Advertisement( + name: 'BLE-12138', + ); + await _manager.startAdvertising(advertisement); + _advertising = true; + notifyListeners(); + } + + Future stopAdvertising() async { + if (!_advertising) { + return; + } + await _manager.stopAdvertising(); + _advertising = false; + notifyListeners(); + } + + void clearLogs() { + _logs.clear(); + notifyListeners(); + } + + @override + void dispose() { + _stateChangedSubscription.cancel(); + _characteristicReadRequestedSubscription.cancel(); + _characteristicWriteRequestedSubscription.cancel(); + _characteristicNotifyStateChangedSubscription.cancel(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/view_models/peripheral_view_model.dart b/bluetooth_low_energy_darwin/example/lib/view_models/peripheral_view_model.dart new file mode 100644 index 0000000..7fab7e0 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/view_models/peripheral_view_model.dart @@ -0,0 +1,75 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:hybrid_logging/hybrid_logging.dart'; + +import 'service_view_model.dart'; + +class PeripheralViewModel extends ViewModel with TypeLogger { + final CentralManager _manager; + final Peripheral _peripheral; + final String? _name; + bool _connected; + List _serviceViewModels; + + late final StreamSubscription _connectionStateChangedSubscription; + + PeripheralViewModel(DiscoveredEventArgs eventArgs) + : _manager = CentralManager(), + _peripheral = eventArgs.peripheral, + _name = eventArgs.advertisement.name, + _connected = false, + _serviceViewModels = [] { + _connectionStateChangedSubscription = + _manager.connectionStateChanged.listen((eventArgs) { + if (eventArgs.peripheral != _peripheral) { + return; + } + if (eventArgs.state == ConnectionState.connected) { + _connected = true; + } else { + _connected = false; + _serviceViewModels = []; + } + notifyListeners(); + }); + } + + UUID get uuid => _peripheral.uuid; + String? get name => _name; + bool get connected => _connected; + List get serviceViewModels => _serviceViewModels; + + Future connect() async { + await _manager.connect(_peripheral); + } + + Future disconnect() async { + await _manager.disconnect(_peripheral); + } + + Future discoverGATT() async { + final services = await _manager.discoverGATT(_peripheral); + _serviceViewModels = services + .map((service) => ServiceViewModel( + manager: _manager, + peripheral: _peripheral, + service: service, + )) + .toList(); + notifyListeners(); + } + + @override + void dispose() { + if (connected) { + disconnect(); + } + _connectionStateChangedSubscription.cancel(); + for (var serviceViewModel in serviceViewModels) { + serviceViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/view_models/service_view_model.dart b/bluetooth_low_energy_darwin/example/lib/view_models/service_view_model.dart new file mode 100644 index 0000000..0dfd888 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/view_models/service_view_model.dart @@ -0,0 +1,46 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; + +import 'characteristic_view_model.dart'; + +class ServiceViewModel extends ViewModel { + final GATTService _service; + + final List _includedServiceViewModels; + final List _characteristicViewModels; + + ServiceViewModel({ + required CentralManager manager, + required Peripheral peripheral, + required GATTService service, + }) : _service = service, + _includedServiceViewModels = service.includedServices + .map((service) => ServiceViewModel( + manager: manager, + peripheral: peripheral, + service: service, + )) + .toList(), + _characteristicViewModels = service.characteristics + .map((characteristic) => CharacteristicViewModel( + manager: manager, + peripheral: peripheral, + characteristic: characteristic, + )) + .toList(); + + UUID get uuid => _service.uuid; + bool get isPrimary => _service.isPrimary; + List get includedServiceViewModels => + _includedServiceViewModels; + List get characteristicViewModels => + _characteristicViewModels; + + @override + void dispose() { + for (var characteristicViewModel in characteristicViewModels) { + characteristicViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/views.dart b/bluetooth_low_energy_darwin/example/lib/views.dart new file mode 100644 index 0000000..b0727cb --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/views.dart @@ -0,0 +1,4 @@ +export 'views/home_view.dart'; +export 'views/central_manager_view.dart'; +export 'views/peripheral_manager_view.dart'; +export 'views/peripheral_view.dart'; diff --git a/bluetooth_low_energy_darwin/example/lib/views/advertisement_view.dart b/bluetooth_low_energy_darwin/example/lib/views/advertisement_view.dart new file mode 100644 index 0000000..2082064 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/views/advertisement_view.dart @@ -0,0 +1,69 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:convert/convert.dart'; +import 'package:flutter/material.dart'; + +class AdvertisementView extends StatelessWidget { + final Advertisement advertisement; + + const AdvertisementView({ + super.key, + required this.advertisement, + }); + + @override + Widget build(BuildContext context) { + final manufacturerSpecificData = advertisement.manufacturerSpecificData; + return LayoutBuilder( + builder: (context, constraints) { + const idWidth = 100.0; + final valueWidth = constraints.maxWidth - idWidth - 16.0 * 2.0; + return DataTable( + columnSpacing: 0.0, + horizontalMargin: 16.0, + columns: [ + DataColumn( + label: Container( + width: idWidth, + alignment: Alignment.center, + child: const Text('Id'), + ), + ), + DataColumn( + label: Container( + width: valueWidth, + alignment: Alignment.center, + child: const Text('Value'), + ), + ), + ], + rows: manufacturerSpecificData.map((item) { + final id = '0x${item.id.toRadixString(16).padLeft(4, '0')}'; + final value = hex.encode(item.data); + return DataRow( + cells: [ + DataCell( + Container( + width: idWidth, + alignment: Alignment.center, + child: Text(id), + ), + ), + DataCell( + Container( + width: valueWidth, + alignment: Alignment.center, + child: Text( + value, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ); + }).toList(), + ); + }, + ); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/views/central_manager_view.dart b/bluetooth_low_energy_darwin/example/lib/views/central_manager_view.dart new file mode 100644 index 0000000..e489b66 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/views/central_manager_view.dart @@ -0,0 +1,126 @@ +import 'dart:io'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_darwin_example/view_models.dart'; +import 'package:bluetooth_low_energy_darwin_example/widgets.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import 'advertisement_view.dart'; + +class CentralManagerView extends StatelessWidget { + const CentralManagerView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final discovering = viewModel.discovering; + return Scaffold( + appBar: AppBar( + title: const Text('Central Manager'), + actions: [ + TextButton( + onPressed: state == BluetoothLowEnergyState.poweredOn + ? () async { + if (discovering) { + await viewModel.stopDiscovery(); + } else { + await viewModel.startDiscovery(); + } + } + : null, + child: Text(discovering ? 'END' : 'BEGIN'), + ), + ], + ), + body: buildBody(context), + ); + } + + Widget buildBody(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + if (state == BluetoothLowEnergyState.unauthorized && Platform.isIOS) { + return Center( + child: TextButton( + onPressed: () => viewModel.showAppSettings(), + child: const Text('Go to settings'), + ), + ); + } else if (state == BluetoothLowEnergyState.poweredOn) { + final discoveries = viewModel.discoveries; + return ListView.separated( + itemBuilder: (context, index) { + final theme = Theme.of(context); + final discovery = discoveries[index]; + final uuid = discovery.peripheral.uuid; + final name = discovery.advertisement.name; + final rssi = discovery.rssi; + return ListTile( + onTap: () { + onTapDissovery(context, discovery); + }, + onLongPress: () { + onLongPressDiscovery(context, discovery); + }, + title: Text(name ?? ''), + subtitle: Text( + '$uuid', + style: theme.textTheme.bodySmall, + softWrap: false, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RSSIIndicator(rssi), + Text('$rssi'), + ], + ), + ); + }, + separatorBuilder: (context, i) { + return const Divider( + height: 0.0, + ); + }, + itemCount: discoveries.length, + ); + } else { + return Center( + child: Text( + '$state', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + } + + void onTapDissovery( + BuildContext context, DiscoveredEventArgs discovery) async { + final viewModel = ViewModel.of(context); + if (viewModel.discovering) { + await viewModel.stopDiscovery(); + if (!context.mounted) { + return; + } + } + final uuid = discovery.peripheral.uuid; + context.go('/central/$uuid'); + } + + void onLongPressDiscovery( + BuildContext context, DiscoveredEventArgs discovery) async { + await showModalBottomSheet( + context: context, + builder: (context) { + return AdvertisementView( + advertisement: discovery.advertisement, + ); + }, + ); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/views/characteristic_tree_node_view.dart b/bluetooth_low_energy_darwin/example/lib/views/characteristic_tree_node_view.dart new file mode 100644 index 0000000..92491dc --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/views/characteristic_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_darwin_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class CharacteristicTreeNodeView extends StatelessWidget { + const CharacteristicTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/views/characteristic_view.dart b/bluetooth_low_energy_darwin/example/lib/views/characteristic_view.dart new file mode 100644 index 0000000..de62d0b --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/views/characteristic_view.dart @@ -0,0 +1,178 @@ +import 'dart:convert'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_darwin_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +import 'log_view.dart'; + +class CharacteristicView extends StatefulWidget { + const CharacteristicView({super.key}); + + @override + State createState() => _CharacteristicViewState(); +} + +class _CharacteristicViewState extends State { + late final TextEditingController _textController; + + @override + void initState() { + super.initState(); + _textController = TextEditingController(); + } + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final logs = viewModel.logs; + return Column( + children: [ + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + return OverflowBox( + alignment: Alignment.topCenter, + maxHeight: constraints.maxHeight + 1.0, + child: Row( + children: [ + Expanded( + child: InputDecorator( + decoration: InputDecoration( + contentPadding: const EdgeInsets.all(12.0), + border: OutlineInputBorder( + borderRadius: BorderRadius.vertical( + top: const Radius.circular(12.0), + bottom: viewModel.canWrite || + viewModel.canWriteWithoutResponse + ? Radius.zero + : const Radius.circular(12.0), + ), + ), + ), + child: ListView.builder( + itemBuilder: (context, i) { + final log = logs[i]; + return LogView( + log: log, + ); + }, + itemCount: logs.length, + ), + ), + ), + Column( + children: [ + Visibility( + visible: viewModel.canNotify, + child: viewModel.notifyState + ? IconButton.filled( + onPressed: () async { + await viewModel.setNotifyState(false); + }, + icon: + const Icon(Symbols.notifications_active), + ) + : IconButton.filledTonal( + onPressed: () async { + await viewModel.setNotifyState(true); + }, + icon: const Icon(Symbols.notifications_off), + ), + ), + Visibility( + visible: viewModel.canRead, + child: IconButton.filled( + onPressed: () async { + await viewModel.read(); + }, + icon: const Icon(Symbols.arrow_downward), + ), + ), + Visibility( + visible: viewModel.canWrite || + viewModel.canWriteWithoutResponse, + child: viewModel.writeType == + GATTCharacteristicWriteType.withResponse + ? IconButton.filled( + onPressed: viewModel.canWriteWithoutResponse + ? () { + viewModel.setWriteType( + GATTCharacteristicWriteType + .withoutResponse); + } + : null, + icon: const Icon(Symbols.swap_vert), + ) + : IconButton.filledTonal( + onPressed: viewModel.canWrite + ? () { + viewModel.setWriteType( + GATTCharacteristicWriteType + .withResponse); + } + : null, + icon: const Icon(Symbols.arrow_upward), + ), + ), + IconButton.filled( + onPressed: () => viewModel.clearLogs(), + icon: const Icon(Symbols.delete), + ), + ], + ), + ], + ), + ); + }, + ), + ), + Visibility( + visible: viewModel.canWrite || viewModel.canWriteWithoutResponse, + child: Row( + children: [ + Expanded( + child: TextField( + controller: _textController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.symmetric( + horizontal: 12.0, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12.0), + ), + ), + ), + ), + ), + ValueListenableBuilder( + valueListenable: _textController, + builder: (context, tev, child) { + final text = tev.text; + return IconButton.filled( + onPressed: text.isEmpty + ? null + : () async { + final value = utf8.encode(text); + await viewModel.write(value); + }, + icon: const Icon(Symbols.pets), + ); + }, + ), + ], + ), + ), + ], + ); + } + + @override + void dispose() { + _textController.dispose(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/views/descriptor_tree_node_view.dart b/bluetooth_low_energy_darwin/example/lib/views/descriptor_tree_node_view.dart new file mode 100644 index 0000000..5afd34f --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/views/descriptor_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_darwin_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class DescriptorTreeNodeView extends StatelessWidget { + const DescriptorTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.tertiary, + ), + ); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/views/home_view.dart b/bluetooth_low_energy_darwin/example/lib/views/home_view.dart new file mode 100644 index 0000000..9b7d146 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/views/home_view.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class HomeView extends StatefulWidget { + final StatefulNavigationShell navigationShell; + final List navigators; + + const HomeView({ + super.key, + required this.navigationShell, + required this.navigators, + }); + + @override + State createState() => _HomeViewState(); +} + +class _HomeViewState extends State { + late final PageController _controller; + + @override + void initState() { + super.initState(); + _controller = PageController( + initialPage: widget.navigationShell.currentIndex, + ); + } + + @override + Widget build(BuildContext context) { + final navigationShell = widget.navigationShell; + final navigators = widget.navigators; + return Scaffold( + // body: navigationShell, + body: PageView.builder( + controller: _controller, + onPageChanged: (index) { + // Ignore tap events. + if (index == navigationShell.currentIndex) { + return; + } + navigationShell.goBranch( + index, + initialLocation: false, + ); + }, + itemBuilder: (context, index) { + return navigators[index]; + }, + itemCount: navigators.length, + ), + bottomNavigationBar: BottomNavigationBar( + onTap: (index) { + navigationShell.goBranch( + index, + initialLocation: index == navigationShell.currentIndex, + ); + }, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.radar), + label: 'Central', + ), + BottomNavigationBarItem( + icon: Icon(Icons.sensors), + label: 'Peripheral', + ), + ], + currentIndex: widget.navigationShell.currentIndex, + ), + ); + } + + @override + void didUpdateWidget(covariant HomeView oldWidget) { + super.didUpdateWidget(oldWidget); + final navigationShell = widget.navigationShell; + final page = _controller.page ?? _controller.initialPage; + final index = page.round(); + // Ignore swipe events. + if (index == navigationShell.currentIndex) { + return; + } + _controller.animateToPage( + navigationShell.currentIndex, + duration: const Duration(milliseconds: 300), + curve: Curves.linear, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/views/log_view.dart b/bluetooth_low_energy_darwin/example/lib/views/log_view.dart new file mode 100644 index 0000000..3c97a1c --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/views/log_view.dart @@ -0,0 +1,43 @@ +import 'package:bluetooth_low_energy_darwin_example/models.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class LogView extends StatelessWidget { + final Log log; + + const LogView({ + super.key, + required this.log, + }); + + @override + Widget build(BuildContext context) { + final formatter = DateFormat.Hms(); + final time = formatter.format(log.time); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich( + TextSpan( + text: '[$time] ', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.error, + ), + children: [ + TextSpan( + text: log.type, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + )), + ], + ), + ), + Text( + log.message, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/views/peripheral_manager_view.dart b/bluetooth_low_energy_darwin/example/lib/views/peripheral_manager_view.dart new file mode 100644 index 0000000..10bbfe1 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/views/peripheral_manager_view.dart @@ -0,0 +1,78 @@ +import 'dart:io'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_darwin_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +import 'log_view.dart'; + +class PeripheralManagerView extends StatelessWidget { + const PeripheralManagerView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final advertising = viewModel.advertising; + return Scaffold( + appBar: AppBar( + title: const Text('Peripheral Manager'), + actions: [ + TextButton( + onPressed: state == BluetoothLowEnergyState.poweredOn + ? () async { + if (advertising) { + await viewModel.stopAdvertising(); + } else { + await viewModel.startAdvertising(); + } + } + : null, + child: Text(advertising ? 'END' : 'BEGIN'), + ), + ], + ), + body: buildBody(context), + floatingActionButton: state == BluetoothLowEnergyState.poweredOn + ? FloatingActionButton( + onPressed: () => viewModel.clearLogs(), + child: const Icon(Symbols.delete), + ) + : null, + ); + } + + Widget buildBody(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + if (state == BluetoothLowEnergyState.unauthorized && Platform.isIOS) { + return Center( + child: TextButton( + onPressed: () => viewModel.showAppSettings(), + child: const Text('Go to settings'), + ), + ); + } else if (state == BluetoothLowEnergyState.poweredOn) { + final logs = viewModel.logs; + return ListView.builder( + padding: const EdgeInsets.all(16.0), + itemBuilder: (context, i) { + final log = logs[i]; + return LogView( + log: log, + ); + }, + itemCount: logs.length, + ); + } else { + return Center( + child: Text( + '$state', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/views/peripheral_view.dart b/bluetooth_low_energy_darwin/example/lib/views/peripheral_view.dart new file mode 100644 index 0000000..fb910f7 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/views/peripheral_view.dart @@ -0,0 +1,107 @@ +import 'package:bluetooth_low_energy_darwin_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_simple_treeview/flutter_simple_treeview.dart'; + +import 'characteristic_tree_node_view.dart'; +import 'characteristic_view.dart'; +import 'descriptor_tree_node_view.dart'; +import 'service_tree_node_view.dart'; + +class PeripheralView extends StatelessWidget { + final TreeController _treeController; + + PeripheralView({super.key}) + : _treeController = TreeController( + allNodesExpanded: false, + ); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final connected = viewModel.connected; + final serviceViewModels = viewModel.serviceViewModels; + return Scaffold( + appBar: AppBar( + title: Text(viewModel.name ?? '${viewModel.uuid}'), + actions: [ + TextButton( + onPressed: () async { + if (connected) { + await viewModel.disconnect(); + } else { + await viewModel.connect(); + await viewModel.discoverGATT(); + } + }, + child: Text(connected ? 'DISCONNECT' : 'CONNECT'), + ), + ], + ), + body: SingleChildScrollView( + child: TreeView( + treeController: _treeController, + indent: 0.0, + nodes: _buildServiceTreeNodes(serviceViewModels), + ), + ), + ); + } + + List _buildServiceTreeNodes(List viewModels) { + return viewModels.map((viewModel) { + final includedServiceViewModels = viewModel.includedServiceViewModels; + final characteristicViewModels = viewModel.characteristicViewModels; + return TreeNode( + children: [ + ..._buildServiceTreeNodes(includedServiceViewModels), + ..._buildCharacteristicTreeNodes(characteristicViewModels), + ], + content: InheritedViewModel( + view: const ServiceTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } + + List _buildCharacteristicTreeNodes( + List viewModels) { + return viewModels.map((viewModel) { + final descriptorViewModels = viewModel.descriptorViewModels; + return TreeNode( + children: [ + TreeNode( + content: Expanded( + child: Container( + margin: const EdgeInsets.only(right: 40.0), + height: 360.0, + child: InheritedViewModel( + view: const CharacteristicView(), + viewModel: viewModel, + ), + ), + ), + ), + ..._buildDescriptorTreeNodes(descriptorViewModels), + ], + content: InheritedViewModel( + view: const CharacteristicTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } + + List _buildDescriptorTreeNodes( + List viewModels) { + return viewModels.map((viewModel) { + return TreeNode( + content: InheritedViewModel( + view: const DescriptorTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/views/service_tree_node_view.dart b/bluetooth_low_energy_darwin/example/lib/views/service_tree_node_view.dart new file mode 100644 index 0000000..2416d97 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/views/service_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_darwin_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class ServiceTreeNodeView extends StatelessWidget { + const ServiceTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ); + } +} diff --git a/bluetooth_low_energy_darwin/example/lib/widgets.dart b/bluetooth_low_energy_darwin/example/lib/widgets.dart new file mode 100644 index 0000000..baccf61 --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/widgets.dart @@ -0,0 +1 @@ +export 'widgets/rssi_indicator.dart'; diff --git a/bluetooth_low_energy_darwin/example/lib/widgets/rssi_indicator.dart b/bluetooth_low_energy_darwin/example/lib/widgets/rssi_indicator.dart new file mode 100644 index 0000000..ddd59df --- /dev/null +++ b/bluetooth_low_energy_darwin/example/lib/widgets/rssi_indicator.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class RSSIIndicator extends StatelessWidget { + final int rssi; + + const RSSIIndicator( + this.rssi, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final IconData icon; + if (rssi > -70) { + icon = Icons.wifi_rounded; + } else if (rssi > -100) { + icon = Icons.wifi_2_bar_rounded; + } else { + icon = Icons.wifi_1_bar_rounded; + } + return Icon(icon); + } +} diff --git a/bluetooth_low_energy_darwin/example/macos/Flutter/GeneratedPluginRegistrant.swift b/bluetooth_low_energy_darwin/example/macos/Flutter/GeneratedPluginRegistrant.swift index 0e96062..4579819 100644 --- a/bluetooth_low_energy_darwin/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/bluetooth_low_energy_darwin/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,5 +8,5 @@ import Foundation import bluetooth_low_energy_darwin func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - BluetoothLowEnergyDarwin.register(with: registry.registrar(forPlugin: "BluetoothLowEnergyDarwin")) + BluetoothLowEnergyDarwinPlugin.register(with: registry.registrar(forPlugin: "BluetoothLowEnergyDarwinPlugin")) } diff --git a/bluetooth_low_energy_darwin/example/macos/Podfile.lock b/bluetooth_low_energy_darwin/example/macos/Podfile.lock index c3133f1..7c98915 100644 --- a/bluetooth_low_energy_darwin/example/macos/Podfile.lock +++ b/bluetooth_low_energy_darwin/example/macos/Podfile.lock @@ -1,5 +1,5 @@ PODS: - - bluetooth_low_energy_darwin (2.0.2): + - bluetooth_low_energy_darwin (0.0.1): - Flutter - FlutterMacOS - FlutterMacOS (1.0.0) @@ -15,9 +15,9 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral SPEC CHECKSUMS: - bluetooth_low_energy_darwin: e37c1af3337ebc99a60807137dc5e47c6195eac7 + bluetooth_low_energy_darwin: 764d8d1ae5abefbcdb839e812b4b25c0061fcf8b FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/bluetooth_low_energy_darwin/example/macos/Runner.xcodeproj/project.pbxproj b/bluetooth_low_energy_darwin/example/macos/Runner.xcodeproj/project.pbxproj index fe555c3..bd830f2 100644 --- a/bluetooth_low_energy_darwin/example/macos/Runner.xcodeproj/project.pbxproj +++ b/bluetooth_low_energy_darwin/example/macos/Runner.xcodeproj/project.pbxproj @@ -21,14 +21,14 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 11E67697E2DFF348C5E0FBA6 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 12AE3247A53A0A6C66A10AB1 /* Pods_RunnerTests.framework */; }; 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 98831461FCCC0C712B565489 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7618EFBF5D56DEA9B5BC6858 /* Pods_Runner.framework */; }; + 56BC448E9AA045CEEA9F18AE /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3ACAB56CBFCEA91F07EF49FE /* Pods_RunnerTests.framework */; }; + E85B965AEA257F11EDEFD117 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 481C7185A6A29B7356E7BC4F /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -62,14 +62,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 032DEBF51E6D235331EAFB6D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 12AE3247A53A0A6C66A10AB1 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 2C73F6F52B9D612E7A3ED61B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* bluetooth_low_energy_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bluetooth_low_energy_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* bluetooth_low_energy_darwin_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bluetooth_low_energy_darwin_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -81,13 +78,16 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 42843373A331898F762AE9FA /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - 69A28A72349A6C45CE7FD12D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 7618EFBF5D56DEA9B5BC6858 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3ACAB56CBFCEA91F07EF49FE /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 481C7185A6A29B7356E7BC4F /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 48CF260037ECCE45E257099A /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 498D21679C9C64E6DBCAB340 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 7F49A581B2FF1737F1557371 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 90C74EBB599CD156C5649B0E /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + 91C52C32743F47D804BC8050 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - BBBF70DBDF6B0B4E82F4E8A2 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + B2647444F8BBDB6D18347354 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + DCB648C2118B8E673D1A04D0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -95,7 +95,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 11E67697E2DFF348C5E0FBA6 /* Pods_RunnerTests.framework in Frameworks */, + 56BC448E9AA045CEEA9F18AE /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -103,13 +103,27 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 98831461FCCC0C712B565489 /* Pods_Runner.framework in Frameworks */, + E85B965AEA257F11EDEFD117 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 12C065AA4D4AEFAE0CFF1E11 /* Pods */ = { + isa = PBXGroup; + children = ( + B2647444F8BBDB6D18347354 /* Pods-Runner.debug.xcconfig */, + 48CF260037ECCE45E257099A /* Pods-Runner.release.xcconfig */, + DCB648C2118B8E673D1A04D0 /* Pods-Runner.profile.xcconfig */, + 90C74EBB599CD156C5649B0E /* Pods-RunnerTests.debug.xcconfig */, + 498D21679C9C64E6DBCAB340 /* Pods-RunnerTests.release.xcconfig */, + 91C52C32743F47D804BC8050 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 331C80D6294CF71000263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -137,14 +151,14 @@ 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, - 5413C8972DC878C53291A3D1 /* Pods */, + 12C065AA4D4AEFAE0CFF1E11 /* Pods */, ); sourceTree = ""; }; 33CC10EE2044A3C60003C045 /* Products */ = { isa = PBXGroup; children = ( - 33CC10ED2044A3C60003C045 /* bluetooth_low_energy_example.app */, + 33CC10ED2044A3C60003C045 /* bluetooth_low_energy_darwin_example.app */, 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, ); name = Products; @@ -185,24 +199,11 @@ path = Runner; sourceTree = ""; }; - 5413C8972DC878C53291A3D1 /* Pods */ = { - isa = PBXGroup; - children = ( - 2C73F6F52B9D612E7A3ED61B /* Pods-Runner.debug.xcconfig */, - 69A28A72349A6C45CE7FD12D /* Pods-Runner.release.xcconfig */, - 032DEBF51E6D235331EAFB6D /* Pods-Runner.profile.xcconfig */, - 42843373A331898F762AE9FA /* Pods-RunnerTests.debug.xcconfig */, - 7F49A581B2FF1737F1557371 /* Pods-RunnerTests.release.xcconfig */, - BBBF70DBDF6B0B4E82F4E8A2 /* Pods-RunnerTests.profile.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( - 7618EFBF5D56DEA9B5BC6858 /* Pods_Runner.framework */, - 12AE3247A53A0A6C66A10AB1 /* Pods_RunnerTests.framework */, + 481C7185A6A29B7356E7BC4F /* Pods_Runner.framework */, + 3ACAB56CBFCEA91F07EF49FE /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -214,7 +215,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 1AB4F7EB04EA489C9D3447CC /* [CP] Check Pods Manifest.lock */, + 58DC822B5511057672D5EA36 /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -233,13 +234,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - B609B32CB0330D926A7BF173 /* [CP] Check Pods Manifest.lock */, + 02F0ADF4750BC0D2334D6BF0 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - C553F2A78B180316DDD47899 /* [CP] Embed Pods Frameworks */, + 42F1C37024EDD17206C3254E /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -248,7 +249,7 @@ ); name = Runner; productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* bluetooth_low_energy_example.app */; + productReference = 33CC10ED2044A3C60003C045 /* bluetooth_low_energy_darwin_example.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -257,8 +258,9 @@ 33CC10E52044A3C60003C045 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C80D4294CF70F00263BE5 = { @@ -321,7 +323,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 1AB4F7EB04EA489C9D3447CC /* [CP] Check Pods Manifest.lock */ = { + 02F0ADF4750BC0D2334D6BF0 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -336,7 +338,7 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -381,29 +383,7 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - B609B32CB0330D926A7BF173 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - C553F2A78B180316DDD47899 /* [CP] Embed Pods Frameworks */ = { + 42F1C37024EDD17206C3254E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -420,6 +400,28 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + 58DC822B5511057672D5EA36 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -471,46 +473,46 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 42843373A331898F762AE9FA /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 90C74EBB599CD156C5649B0E /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_example"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_darwin_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_darwin_example"; }; name = Debug; }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7F49A581B2FF1737F1557371 /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = 498D21679C9C64E6DBCAB340 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_example"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_darwin_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_darwin_example"; }; name = Release; }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = BBBF70DBDF6B0B4E82F4E8A2 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = 91C52C32743F47D804BC8050 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_example"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/bluetooth_low_energy_darwin_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/bluetooth_low_energy_darwin_example"; }; name = Profile; }; @@ -519,6 +521,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -542,9 +545,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -592,6 +597,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -615,9 +621,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -645,6 +653,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -668,9 +677,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; diff --git a/bluetooth_low_energy_darwin/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/bluetooth_low_energy_darwin/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 29ceb04..0e6ee5e 100644 --- a/bluetooth_low_energy_darwin/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/bluetooth_low_energy_darwin/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ @@ -31,7 +31,7 @@ @@ -65,7 +65,7 @@ @@ -82,7 +82,7 @@ diff --git a/bluetooth_low_energy_darwin/example/macos/Runner/Configs/AppInfo.xcconfig b/bluetooth_low_energy_darwin/example/macos/Runner/Configs/AppInfo.xcconfig index 1aaf824..23489ae 100644 --- a/bluetooth_low_energy_darwin/example/macos/Runner/Configs/AppInfo.xcconfig +++ b/bluetooth_low_energy_darwin/example/macos/Runner/Configs/AppInfo.xcconfig @@ -5,10 +5,10 @@ // 'flutter create' template. // The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = bluetooth_low_energy_example +PRODUCT_NAME = bluetooth_low_energy_darwin_example // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = dev.yanshouwang.bluetoothLowEnergyExample +PRODUCT_BUNDLE_IDENTIFIER = dev.hebei.bluetoothLowEnergyDarwinExample // The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2023 dev.yanshouwang. All rights reserved. +PRODUCT_COPYRIGHT = Copyright © 2024 dev.hebei. All rights reserved. diff --git a/bluetooth_low_energy_darwin/example/macos/Runner/Info.plist b/bluetooth_low_energy_darwin/example/macos/Runner/Info.plist index e9c2bd8..4789daa 100644 --- a/bluetooth_low_energy_darwin/example/macos/Runner/Info.plist +++ b/bluetooth_low_energy_darwin/example/macos/Runner/Info.plist @@ -22,8 +22,6 @@ $(FLUTTER_BUILD_NUMBER) LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) - NSBluetoothAlwaysUsageDescription - Hello Bluetooth LowEnergy! NSHumanReadableCopyright $(PRODUCT_COPYRIGHT) NSMainNibFile diff --git a/bluetooth_low_energy_darwin/example/macos/RunnerTests/RunnerTests.swift b/bluetooth_low_energy_darwin/example/macos/RunnerTests/RunnerTests.swift index 3db3281..67a6bbd 100644 --- a/bluetooth_low_energy_darwin/example/macos/RunnerTests/RunnerTests.swift +++ b/bluetooth_low_energy_darwin/example/macos/RunnerTests/RunnerTests.swift @@ -1,8 +1,8 @@ -import FlutterMacOS import Cocoa +import FlutterMacOS import XCTest -@testable import bluetooth_low_energy +@testable import bluetooth_low_energy_darwin // This demonstrates a simple unit test of the Swift portion of this plugin's implementation. // @@ -11,7 +11,7 @@ import XCTest class RunnerTests: XCTestCase { func testGetPlatformVersion() { - let plugin = BluetoothLowEnergyPlugin() + let plugin = BluetoothLowEnergyDarwinPlugin() let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) diff --git a/bluetooth_low_energy_darwin/example/pubspec.lock b/bluetooth_low_energy_darwin/example/pubspec.lock index 6b7f7b2..00c2e7c 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: "5.0.5" + version: "6.0.0" bluetooth_low_energy_platform_interface: dependency: "direct main" description: name: bluetooth_low_energy_platform_interface - sha256: "5af74eb8f97a896dfdbcff2da284d4fe5b4e2e49ebb2f46f826ac1810f66e4d7" + sha256: bc2e8d97c141653e5747bcb3cdc9fe956541b6ecc6e5f158b99a2f3abc2d946a url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "6.0.0" boolean_selector: dependency: transitive description: @@ -48,8 +48,16 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + clover: + dependency: "direct main" + description: + name: clover + sha256: eba28e69b32f174a51c093eef098cf5ae646470322727081d5d3d8f66c786487 + url: "https://pub.dev" + source: hosted + version: "3.0.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a @@ -68,10 +76,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" fake_async: dependency: transitive description: @@ -102,7 +110,15 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + flutter_simple_treeview: + dependency: "direct main" + description: + name: flutter_simple_treeview + sha256: ad4978d2668dd078d3a09966832da111bef9102dd636e572c50c80133b7ff4d9 url: "https://pub.dev" source: hosted version: "3.0.2" @@ -111,11 +127,32 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter source: sdk version: "0.0.0" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "6ad5662b014c06c20fa46ab78715c96b2222a7fe4f87bf77e0289592c2539e86" + url: "https://pub.dev" + source: hosted + version: "14.1.3" + hybrid_logging: + dependency: "direct main" + description: + name: hybrid_logging + sha256: "54248d52ce68c14702a42fbc4083bac5c6be30f6afad8a41be4bbadd197b8af5" + url: "https://pub.dev" + source: hosted + version: "1.0.0" integration_test: dependency: "direct dev" description: flutter @@ -133,42 +170,34 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" - log_service: - dependency: transitive - description: - name: log_service - sha256: "21124936899e227d1779268077921d46d57456e2592d1562e455be273594e2e4" - url: "https://pub.dev" - source: hosted - version: "1.0.0" + version: "4.0.0" logging: dependency: "direct main" description: @@ -193,14 +222,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.0" + material_symbols_icons: + dependency: "direct main" + description: + name: material_symbols_icons + sha256: b2d3cbc3c42b8a217715b0d97ff03aebb14b2b4592875736e5599c603fb2db7e + url: "https://pub.dev" + source: hosted + version: "4.2758.0" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" path: dependency: transitive description: @@ -290,10 +327,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -314,10 +351,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" webdriver: dependency: transitive description: @@ -327,5 +364,5 @@ packages: source: hosted version: "3.0.3" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.0.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/bluetooth_low_energy_darwin/example/pubspec.yaml b/bluetooth_low_energy_darwin/example/pubspec.yaml index d904d36..60d3ed8 100644 --- a/bluetooth_low_energy_darwin/example/pubspec.yaml +++ b/bluetooth_low_energy_darwin/example/pubspec.yaml @@ -1,5 +1,5 @@ -name: bluetooth_low_energy_example -description: Demonstrates how to use the bluetooth_low_energy plugin. +name: bluetooth_low_energy_darwin_example +description: "Demonstrates how to use the bluetooth_low_energy_darwin plugin." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev @@ -17,10 +17,10 @@ dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^5.0.0 + bluetooth_low_energy_platform_interface: ^6.0.0 bluetooth_low_energy_darwin: # When depending on this package from a real application you should use: - # bluetooth_low_energy: ^x.y.z + # bluetooth_low_energy_darwin: ^x.y.z # See https://dart.dev/tools/pub/dependencies#version-constraints # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. @@ -28,10 +28,16 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - convert: ^3.1.1 - intl: ^0.19.0 + cupertino_icons: ^1.0.6 + clover: ^3.0.0 + go_router: ^14.1.3 logging: ^1.2.0 + hybrid_logging: ^1.0.0 + intl: ^0.19.0 + collection: ^1.18.0 + convert: ^3.1.1 + flutter_simple_treeview: ^3.0.2 + material_symbols_icons: ^4.2744.0 dev_dependencies: integration_test: @@ -44,7 +50,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: ^3.0.0 + flutter_lints: ^4.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_darwin/example/test/widget_test.dart b/bluetooth_low_energy_darwin/example/test/widget_test.dart index b15e87d..2a2b819 100644 --- a/bluetooth_low_energy_darwin/example/test/widget_test.dart +++ b/bluetooth_low_energy_darwin/example/test/widget_test.dart @@ -5,23 +5,4 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:bluetooth_low_energy_example/main.dart'; - -void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); -} +void main() {} 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 1666f3d..1ede96c 100644 --- a/bluetooth_low_energy_darwin/lib/bluetooth_low_energy_darwin.dart +++ b/bluetooth_low_energy_darwin/lib/bluetooth_low_energy_darwin.dart @@ -3,9 +3,9 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla import 'src/my_central_manager.dart'; import 'src/my_peripheral_manager.dart'; -abstract class BluetoothLowEnergyDarwin { +abstract class BluetoothLowEnergyDarwinPlugin { static void registerWith() { - CentralManager.instance = MyCentralManager(); - PeripheralManager.instance = MyPeripheralManager(); + PlatformCentralManager.instance = MyCentralManager(); + PlatformPeripheralManager.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 4ef66a6..cc2c251 100644 --- a/bluetooth_low_energy_darwin/lib/src/my_api.dart +++ b/bluetooth_low_energy_darwin/lib/src/my_api.dart @@ -3,13 +3,28 @@ 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'; +import 'my_gatt.dart'; // ToObject +extension Uint8ListX on Uint8List { + List toManufacturerSpecificData() { + if (length > 2) { + return [ + ManufacturerSpecificData( + id: ByteData.view( + buffer, + offsetInBytes, + length, + ).getUint16(0, Endian.host), + data: sublist(2), + ), + ]; + } else { + return []; + } + } +} + extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { BluetoothLowEnergyState toState() { switch (this) { @@ -28,211 +43,230 @@ extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { } } -extension MyGattCharacteristicPropertyArgsX - on MyGattCharacteristicPropertyArgs { - GattCharacteristicProperty toProperty() { - return GattCharacteristicProperty.values[index]; +extension MyConnectionStateArgsX on MyConnectionStateArgs { + ConnectionState toState() { + return ConnectionState.values[index]; } } -extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs { - ManufacturerSpecificData toManufacturerSpecificData() { - final id = idArgs; - final data = dataArgs; - return ManufacturerSpecificData( - id: id, - data: data, - ); +extension MyGATTCharacteristicPropertyArgsX + on MyGATTCharacteristicPropertyArgs { + GATTCharacteristicProperty toProperty() { + return GATTCharacteristicProperty.values[index]; } } 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 = uuidArgs.toUUID(); - return MyCentral( - uuid: uuid, + name: nameArgs, + serviceUUIDs: serviceUUIDsArgs + .cast() + .map((args) => UUID.fromString(args)) + .toList(), + serviceData: serviceDataArgs.cast().map( + (uuidArgs, dataArgs) { + final uuid = UUID.fromString(uuidArgs); + return MapEntry(uuid, dataArgs); + }, + ), + manufacturerSpecificData: + manufacturerSpecificDataArgs?.toManufacturerSpecificData() ?? [], ); } } extension MyPeripheralArgsX on MyPeripheralArgs { - MyPeripheral toPeripheral() { - final uuid = uuidArgs.toUUID(); - return MyPeripheral( - uuid: uuid, + Peripheral toPeripheral() { + return Peripheral( + uuid: UUID.fromString(uuidArgs), ); } } -extension MyGattDescriptorArgsX on MyGattDescriptorArgs { - MyGattDescriptor2 toDescriptor2(MyPeripheral peripheral) { - final hashCode = hashCodeArgs; - final uuid = uuidArgs.toUUID(); - return MyGattDescriptor2( - peripheral: peripheral, - hashCode: hashCode, - uuid: uuid, +extension MyGATTDescriptorArgsX on MyGATTDescriptorArgs { + MyGATTDescriptor toDescriptor() { + return MyGATTDescriptor( + hashCodeArgs: hashCodeArgs, + uuid: UUID.fromString(uuidArgs), ); } } -extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs { - MyGattCharacteristic2 toCharacteristic2(MyPeripheral peripheral) { - final hashCode = hashCodeArgs; - final uuid = uuidArgs.toUUID(); - final properties = propertyNumbersArgs.cast().map( - (args) { - final propertyArgs = MyGattCharacteristicPropertyArgs.values[args]; +extension MyGATTCharacteristicArgsX on MyGATTCharacteristicArgs { + MyGATTCharacteristic toCharacteristic() { + return MyGATTCharacteristic( + hashCodeArgs: hashCodeArgs, + uuid: UUID.fromString(uuidArgs), + 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, - hashCode: hashCode, - uuid: uuid, - properties: properties, - descriptors: descriptors, + }).toList(), + descriptors: descriptorsArgs + .cast() + .map((args) => args.toDescriptor()) + .toList(), ); } } -extension MyGattServiceArgsX on MyGattServiceArgs { - MyGattService2 toService2(MyPeripheral peripheral) { - final hashCode = hashCodeArgs; - 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 MyGATTServiceArgsX on MyGATTServiceArgs { + MyGATTService toService() { + return MyGATTService( + hashCodeArgs: hashCodeArgs, + uuid: UUID.fromString(uuidArgs), + isPrimary: isPrimaryArgs, + includedServices: includedServicesArgs + .cast() + .map((args) => args.toService()) + .toList(), + characteristics: characteristicsArgs + .cast() + .map((args) => args.toCharacteristic()) + .toList(), ); } } -extension MyUuidArgsX on String { - UUID toUUID() { - return UUID.fromString(this); +extension MyCentralArgsX on MyCentralArgs { + Central toCentral() { + return Central( + uuid: UUID.fromString(uuidArgs), + ); } } // ToArgs -extension GattCharacteristicPropertyX on GattCharacteristicProperty { - MyGattCharacteristicPropertyArgs toArgs() { - return MyGattCharacteristicPropertyArgs.values[index]; +extension UUIDX on UUID { + String toArgs() { + return toString(); } } -extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType { - MyGattCharacteristicWriteTypeArgs toArgs() { - return MyGattCharacteristicWriteTypeArgs.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 GATTCharacteristicPropertyX on GATTCharacteristicProperty { + MyGATTCharacteristicPropertyArgs toArgs() { + return MyGATTCharacteristicPropertyArgs.values[index]; + } +} + +extension GATTCharacteristicPermissionX on GATTCharacteristicPermission { + MyGATTCharacteristicPermissionArgs toArgs() { + return MyGATTCharacteristicPermissionArgs.values[index]; + } +} + +extension GATTErrorX on GATTError { + MyATTErrorArgs toArgs() { + switch (this) { + case GATTError.invalidHandle: + return MyATTErrorArgs.invalidHandle; + case GATTError.readNotPermitted: + return MyATTErrorArgs.readNotPermitted; + case GATTError.writeNotPermitted: + return MyATTErrorArgs.writeNotPermitted; + case GATTError.invalidPDU: + return MyATTErrorArgs.invalidPDU; + case GATTError.insufficientAuthentication: + return MyATTErrorArgs.insufficientAuthentication; + case GATTError.requestNotSupported: + return MyATTErrorArgs.requestNotSupported; + case GATTError.invalidOffset: + return MyATTErrorArgs.invalidOffset; + case GATTError.insufficientAuthorization: + return MyATTErrorArgs.insufficientAuthorization; + case GATTError.prepareQueueFull: + return MyATTErrorArgs.prepareQueueFull; + case GATTError.attributeNotFound: + return MyATTErrorArgs.attributeNotFound; + case GATTError.attributeNotLong: + return MyATTErrorArgs.attributeNotLong; + case GATTError.insufficientEncryptionKeySize: + return MyATTErrorArgs.insufficientEncryptionKeySize; + case GATTError.invalidAttributeValueLength: + return MyATTErrorArgs.invalidAttributeValueLength; + case GATTError.unlikelyError: + return MyATTErrorArgs.unlikelyError; + case GATTError.insufficientEncryption: + return MyATTErrorArgs.insufficientEncryption; + case GATTError.unsupportedGroupType: + return MyATTErrorArgs.unsupportedGroupType; + case GATTError.insufficientResources: + return MyATTErrorArgs.insufficientResources; + } } } 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(); + // CoreBluetooth only support `CBAdvertisementDataLocalNameKey` and `CBAdvertisementDataServiceUUIDsKey` + // see https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising + if (serviceData.isNotEmpty || manufacturerSpecificData.isNotEmpty) { + throw UnsupportedError( + 'serviceData and manufacturerSpecificData is not supported on Darwin.'); + } return MyAdvertisementArgs( - nameArgs: nameArgs, - serviceUUIDsArgs: serviceUUIDsArgs, - serviceDataArgs: serviceDataArgs, - manufacturerSpecificDataArgs: manufacturerSpecificDataArgs, + nameArgs: name, + serviceUUIDsArgs: serviceUUIDs.map((uuid) => uuid.toArgs()).toList(), + serviceDataArgs: {}, + manufacturerSpecificDataArgs: null, ); } } -extension MyGattDescriptorX on MyGattDescriptor { - MyGattDescriptorArgs toArgs() { - final hashCodeArgs = hashCode; - final uuidArgs = uuid.toArgs(); - final valueArgs = value; - return MyGattDescriptorArgs( - hashCodeArgs: hashCodeArgs, - uuidArgs: uuidArgs, - valueArgs: valueArgs, +extension MutableGATTDescriptorX on MutableGATTDescriptor { + MyMutableGATTDescriptorArgs toArgs() { + return MyMutableGATTDescriptorArgs( + hashCodeArgs: hashCode, + uuidArgs: uuid.toArgs(), + valueArgs: this is ImmutableGATTDescriptor + ? (this as ImmutableGATTDescriptor).value + : null, ); } } -extension MyGattCharacteristicX on MyGattCharacteristic { - MyGattCharacteristicArgs toArgs(List descriptorsArgs) { - final hashCodeArgs = hashCode; - final uuidArgs = uuid.toArgs(); - final propertyNumbersArgs = properties.map((property) { - final propertyArgs = property.toArgs(); - return propertyArgs.index; - }).toList(); - return MyGattCharacteristicArgs( - hashCodeArgs: hashCodeArgs, - uuidArgs: uuidArgs, - propertyNumbersArgs: propertyNumbersArgs, - descriptorsArgs: descriptorsArgs, +extension MutableGATTCharacteristicX on MutableGATTCharacteristic { + MyMutableGATTCharacteristicArgs toArgs() { + return MyMutableGATTCharacteristicArgs( + hashCodeArgs: hashCode, + uuidArgs: uuid.toArgs(), + propertyNumbersArgs: properties.map((property) { + final propertyArgs = property.toArgs(); + return propertyArgs.index; + }).toList(), + permissionNumbersArgs: permissions.map((permission) { + final permissionArgs = permission.toArgs(); + return permissionArgs.index; + }).toList(), + valueArgs: this is ImmutableGATTCharacteristic + ? (this as ImmutableGATTCharacteristic).value + : null, + descriptorsArgs: descriptors + .cast() + .map((descriptor) => descriptor.toArgs()) + .toList(), ); } } -extension MyGattServiceX on MyGattService { - MyGattServiceArgs toArgs(List characteristicsArgs) { - final hashCodeArgs = hashCode; - final uuidArgs = uuid.toArgs(); - return MyGattServiceArgs( - hashCodeArgs: hashCodeArgs, - uuidArgs: uuidArgs, - characteristicsArgs: characteristicsArgs, +extension GATTServiceX on GATTService { + MyMutableGATTServiceArgs toArgs() { + return MyMutableGATTServiceArgs( + hashCodeArgs: hashCode, + uuidArgs: uuid.toArgs(), + isPrimaryArgs: isPrimary, + includedServicesArgs: + includedServices.map((service) => service.toArgs()).toList(), + characteristicsArgs: characteristics + .cast() + .map((characteristic) => characteristic.toArgs()) + .toList(), ); } } - -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 e775b0d..d647e22 100644 --- a/bluetooth_low_energy_darwin/lib/src/my_api.g.dart +++ b/bluetooth_low_energy_darwin/lib/src/my_api.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v15.0.2), do not edit directly. +// Autogenerated from Pigeon (v19.0.1), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -34,7 +34,12 @@ enum MyBluetoothLowEnergyStateArgs { poweredOn, } -enum MyGattCharacteristicPropertyArgs { +enum MyConnectionStateArgs { + disconnected, + connected, +} + +enum MyGATTCharacteristicPropertyArgs { read, write, writeWithoutResponse, @@ -42,12 +47,19 @@ enum MyGattCharacteristicPropertyArgs { indicate, } -enum MyGattCharacteristicWriteTypeArgs { +enum MyGATTCharacteristicPermissionArgs { + read, + readEncrypted, + write, + writeEncrypted, +} + +enum MyGATTCharacteristicWriteTypeArgs { withResponse, withoutResponse, } -enum MyGattErrorArgs { +enum MyATTErrorArgs { success, invalidHandle, readNotPermitted, @@ -68,32 +80,6 @@ enum MyGattErrorArgs { insufficientResources, } -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, @@ -108,14 +94,14 @@ class MyAdvertisementArgs { Map serviceDataArgs; - MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs; + Uint8List? manufacturerSpecificDataArgs; Object encode() { return [ nameArgs, serviceUUIDsArgs, serviceDataArgs, - manufacturerSpecificDataArgs?.encode(), + manufacturerSpecificDataArgs, ]; } @@ -125,9 +111,7 @@ class 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, + manufacturerSpecificDataArgs: result[3] as Uint8List?, ); } } @@ -174,8 +158,111 @@ class MyPeripheralArgs { } } -class MyGattDescriptorArgs { - MyGattDescriptorArgs({ +class MyGATTDescriptorArgs { + MyGATTDescriptorArgs({ + required this.hashCodeArgs, + required this.uuidArgs, + }); + + int hashCodeArgs; + + String uuidArgs; + + Object encode() { + return [ + hashCodeArgs, + uuidArgs, + ]; + } + + static MyGATTDescriptorArgs decode(Object result) { + result as List; + return MyGATTDescriptorArgs( + hashCodeArgs: result[0]! as int, + uuidArgs: result[1]! as String, + ); + } +} + +class MyGATTCharacteristicArgs { + MyGATTCharacteristicArgs({ + required this.hashCodeArgs, + required this.uuidArgs, + required this.propertyNumbersArgs, + required this.descriptorsArgs, + }); + + int hashCodeArgs; + + String uuidArgs; + + List propertyNumbersArgs; + + List descriptorsArgs; + + Object encode() { + return [ + hashCodeArgs, + uuidArgs, + propertyNumbersArgs, + descriptorsArgs, + ]; + } + + static MyGATTCharacteristicArgs decode(Object result) { + result as List; + return MyGATTCharacteristicArgs( + hashCodeArgs: 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.hashCodeArgs, + required this.uuidArgs, + required this.isPrimaryArgs, + required this.includedServicesArgs, + required this.characteristicsArgs, + }); + + int hashCodeArgs; + + String uuidArgs; + + bool isPrimaryArgs; + + List includedServicesArgs; + + List characteristicsArgs; + + Object encode() { + return [ + hashCodeArgs, + uuidArgs, + isPrimaryArgs, + includedServicesArgs, + characteristicsArgs, + ]; + } + + static MyGATTServiceArgs decode(Object result) { + result as List; + return MyGATTServiceArgs( + hashCodeArgs: result[0]! as int, + uuidArgs: result[1]! as String, + isPrimaryArgs: result[2]! as bool, + includedServicesArgs: (result[3] as List?)!.cast(), + characteristicsArgs: (result[4] as List?)!.cast(), + ); + } +} + +class MyMutableGATTDescriptorArgs { + MyMutableGATTDescriptorArgs({ required this.hashCodeArgs, required this.uuidArgs, this.valueArgs, @@ -195,9 +282,9 @@ class MyGattDescriptorArgs { ]; } - static MyGattDescriptorArgs decode(Object result) { + static MyMutableGATTDescriptorArgs decode(Object result) { result as List; - return MyGattDescriptorArgs( + return MyMutableGATTDescriptorArgs( hashCodeArgs: result[0]! as int, uuidArgs: result[1]! as String, valueArgs: result[2] as Uint8List?, @@ -205,11 +292,13 @@ class MyGattDescriptorArgs { } } -class MyGattCharacteristicArgs { - MyGattCharacteristicArgs({ +class MyMutableGATTCharacteristicArgs { + MyMutableGATTCharacteristicArgs({ required this.hashCodeArgs, required this.uuidArgs, required this.propertyNumbersArgs, + required this.permissionNumbersArgs, + this.valueArgs, required this.descriptorsArgs, }); @@ -219,32 +308,42 @@ class MyGattCharacteristicArgs { List propertyNumbersArgs; - List descriptorsArgs; + List permissionNumbersArgs; + + Uint8List? valueArgs; + + List descriptorsArgs; Object encode() { return [ hashCodeArgs, uuidArgs, propertyNumbersArgs, + permissionNumbersArgs, + valueArgs, descriptorsArgs, ]; } - static MyGattCharacteristicArgs decode(Object result) { + static MyMutableGATTCharacteristicArgs decode(Object result) { result as List; - return MyGattCharacteristicArgs( + return MyMutableGATTCharacteristicArgs( hashCodeArgs: result[0]! as int, uuidArgs: result[1]! as String, propertyNumbersArgs: (result[2] as List?)!.cast(), - descriptorsArgs: (result[3] as List?)!.cast(), + permissionNumbersArgs: (result[3] as List?)!.cast(), + valueArgs: result[4] as Uint8List?, + descriptorsArgs: (result[5] as List?)!.cast(), ); } } -class MyGattServiceArgs { - MyGattServiceArgs({ +class MyMutableGATTServiceArgs { + MyMutableGATTServiceArgs({ required this.hashCodeArgs, required this.uuidArgs, + required this.isPrimaryArgs, + required this.includedServicesArgs, required this.characteristicsArgs, }); @@ -252,39 +351,91 @@ class MyGattServiceArgs { String uuidArgs; - List characteristicsArgs; + bool isPrimaryArgs; + + List includedServicesArgs; + + List characteristicsArgs; Object encode() { return [ hashCodeArgs, uuidArgs, + isPrimaryArgs, + includedServicesArgs, characteristicsArgs, ]; } - static MyGattServiceArgs decode(Object result) { + static MyMutableGATTServiceArgs decode(Object result) { result as List; - return MyGattServiceArgs( + return MyMutableGATTServiceArgs( hashCodeArgs: result[0]! as int, uuidArgs: result[1]! as String, - characteristicsArgs: (result[2] as List?)!.cast(), + isPrimaryArgs: result[2]! as bool, + includedServicesArgs: (result[3] as List?)!.cast(), + characteristicsArgs: (result[4] as List?)!.cast(), ); } } -class _MyCentralManagerHostApiCodec extends StandardMessageCodec { - const _MyCentralManagerHostApiCodec(); +class MyATTRequestArgs { + MyATTRequestArgs({ + required this.hashCodeArgs, + required this.centralArgs, + required this.characteristicHashCodeArgs, + this.valueArgs, + required this.offsetArgs, + }); + + int hashCodeArgs; + + MyCentralArgs centralArgs; + + int characteristicHashCodeArgs; + + Uint8List? valueArgs; + + int offsetArgs; + + Object encode() { + return [ + hashCodeArgs, + centralArgs, + characteristicHashCodeArgs, + valueArgs, + offsetArgs, + ]; + } + + static MyATTRequestArgs decode(Object result) { + result as List; + return MyATTRequestArgs( + hashCodeArgs: result[0]! as int, + centralArgs: result[1]! as MyCentralArgs, + characteristicHashCodeArgs: result[2]! as int, + valueArgs: result[3] as Uint8List?, + offsetArgs: result[4]! as int, + ); + } +} + +class _MyCentralManagerHostAPICodec extends StandardMessageCodec { + const _MyCentralManagerHostAPICodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is MyGattCharacteristicArgs) { + if (value is MyGATTCharacteristicArgs) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is MyGattDescriptorArgs) { + } else if (value is MyGATTDescriptorArgs) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is MyGattServiceArgs) { + } else if (value is MyGATTServiceArgs) { buffer.putUint8(130); writeValue(buffer, value.encode()); + } else if (value is MyPeripheralArgs) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -294,29 +445,34 @@ class _MyCentralManagerHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return MyGattCharacteristicArgs.decode(readValue(buffer)!); + return MyGATTCharacteristicArgs.decode(readValue(buffer)!); case 129: - return MyGattDescriptorArgs.decode(readValue(buffer)!); + return MyGATTDescriptorArgs.decode(readValue(buffer)!); case 130: - return MyGattServiceArgs.decode(readValue(buffer)!); + return MyGATTServiceArgs.decode(readValue(buffer)!); + case 131: + return MyPeripheralArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } } } -class MyCentralManagerHostApi { - /// Constructor for [MyCentralManagerHostApi]. The [binaryMessenger] named argument is +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; + MyCentralManagerHostAPI({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : __pigeon_binaryMessenger = binaryMessenger, + __pigeon_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec pigeonChannelCodec = _MyCentralManagerHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyCentralManagerHostAPICodec(); - Future setUp() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setUp'; + final String __pigeon_messageChannelSuffix; + + Future initialize() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.initialize$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -337,8 +493,8 @@ class MyCentralManagerHostApi { } } - Future startDiscovery() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.startDiscovery'; + Future getState() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.getState$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -354,13 +510,62 @@ class MyCentralManagerHostApi { 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 MyBluetoothLowEnergyStateArgs.values[__pigeon_replyList[0]! as int]; + } + } + + Future showAppSettings() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.showAppSettings$__pigeon_messageChannelSuffix'; + 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(List serviceUUIDsArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.startDiscovery$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([serviceUUIDsArgs]) 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_darwin.MyCentralManagerHostApi.stopDiscovery'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.stopDiscovery$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -381,8 +586,35 @@ class MyCentralManagerHostApi { } } + Future> retrieveConnectedPeripherals() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.retrieveConnectedPeripherals$__pigeon_messageChannelSuffix'; + 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 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 connect(String uuidArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.connect'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.connect$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -404,7 +636,7 @@ class MyCentralManagerHostApi { } Future disconnect(String uuidArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.disconnect'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.disconnect$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -425,15 +657,15 @@ class MyCentralManagerHostApi { } } - Future getMaximumWriteValueLength(String uuidArgs, int typeNumberArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.getMaximumWriteValueLength'; + Future getMaximumWriteLength(String uuidArgs, MyGATTCharacteristicWriteTypeArgs typeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.getMaximumWriteLength$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send([uuidArgs, typeNumberArgs]) as List?; + await __pigeon_channel.send([uuidArgs, typeArgs.index]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -453,7 +685,7 @@ class MyCentralManagerHostApi { } Future readRSSI(String uuidArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readRSSI'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.readRSSI$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -479,8 +711,8 @@ class MyCentralManagerHostApi { } } - Future> discoverServices(String uuidArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverServices'; + Future> discoverServices(String uuidArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.discoverServices$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -502,12 +734,12 @@ class MyCentralManagerHostApi { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)!.cast(); + return (__pigeon_replyList[0] as List?)!.cast(); } } - Future> discoverCharacteristics(String uuidArgs, int hashCodeArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverCharacteristics'; + Future> discoverIncludedServices(String uuidArgs, int hashCodeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.discoverIncludedServices$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -529,12 +761,12 @@ class MyCentralManagerHostApi { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)!.cast(); + return (__pigeon_replyList[0] as List?)!.cast(); } } - Future> discoverDescriptors(String uuidArgs, int hashCodeArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverDescriptors'; + Future> discoverCharacteristics(String uuidArgs, int hashCodeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.discoverCharacteristics$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -556,12 +788,39 @@ class MyCentralManagerHostApi { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)!.cast(); + return (__pigeon_replyList[0] as List?)!.cast(); + } + } + + Future> discoverDescriptors(String uuidArgs, int hashCodeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.discoverDescriptors$__pigeon_messageChannelSuffix'; + 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 List?)!.cast(); } } Future readCharacteristic(String uuidArgs, int hashCodeArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readCharacteristic'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.readCharacteristic$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -587,15 +846,15 @@ class MyCentralManagerHostApi { } } - Future writeCharacteristic(String uuidArgs, int hashCodeArgs, Uint8List valueArgs, int typeNumberArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.writeCharacteristic'; + Future writeCharacteristic(String uuidArgs, int hashCodeArgs, Uint8List valueArgs, MyGATTCharacteristicWriteTypeArgs typeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.writeCharacteristic$__pigeon_messageChannelSuffix'; 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?; + await __pigeon_channel.send([uuidArgs, hashCodeArgs, valueArgs, typeArgs.index]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -610,7 +869,7 @@ class MyCentralManagerHostApi { } Future setCharacteristicNotifyState(String uuidArgs, int hashCodeArgs, bool stateArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setCharacteristicNotifyState'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.setCharacteristicNotifyState$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -632,7 +891,7 @@ class MyCentralManagerHostApi { } Future readDescriptor(String uuidArgs, int hashCodeArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readDescriptor'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.readDescriptor$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -659,7 +918,7 @@ class MyCentralManagerHostApi { } Future writeDescriptor(String uuidArgs, int hashCodeArgs, Uint8List valueArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.writeDescriptor'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostAPI.writeDescriptor$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -681,19 +940,22 @@ class MyCentralManagerHostApi { } } -class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { - const _MyCentralManagerFlutterApiCodec(); +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) { + } else if (value is MyGATTCharacteristicArgs) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is MyPeripheralArgs) { + } else if (value is MyGATTDescriptorArgs) { buffer.putUint8(130); writeValue(buffer, value.encode()); + } else if (value is MyPeripheralArgs) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -705,8 +967,10 @@ class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { case 128: return MyAdvertisementArgs.decode(readValue(buffer)!); case 129: - return MyManufacturerSpecificDataArgs.decode(readValue(buffer)!); + return MyGATTCharacteristicArgs.decode(readValue(buffer)!); case 130: + return MyGATTDescriptorArgs.decode(readValue(buffer)!); + case 131: return MyPeripheralArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -714,34 +978,35 @@ class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { } } -abstract class MyCentralManagerFlutterApi { - static const MessageCodec pigeonChannelCodec = _MyCentralManagerFlutterApiCodec(); +abstract class MyCentralManagerFlutterAPI { + static const MessageCodec pigeonChannelCodec = _MyCentralManagerFlutterAPICodec(); - void onStateChanged(int stateNumberArgs); + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); void onDiscovered(MyPeripheralArgs peripheralArgs, int rssiArgs, MyAdvertisementArgs advertisementArgs); - void onConnectionStateChanged(String uuidArgs, bool stateArgs); + void onConnectionStateChanged(MyPeripheralArgs peripheralArgs, MyConnectionStateArgs stateArgs); - void onCharacteristicNotified(String uuidArgs, int hashCodeArgs, Uint8List valueArgs); + void onCharacteristicNotified(MyPeripheralArgs peripheralArgs, MyGATTCharacteristicArgs characteristicArgs, Uint8List valueArgs); - static void setup(MyCentralManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + static void setUp(MyCentralManagerFlutterAPI? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onStateChanged', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onStateChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onStateChanged was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.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_darwin.MyCentralManagerFlutterApi.onStateChanged was null, expected non-null int.'); + final MyBluetoothLowEnergyStateArgs? arg_stateArgs = args[0] == null ? null : MyBluetoothLowEnergyStateArgs.values[args[0]! as int]; + assert(arg_stateArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onStateChanged was null, expected non-null MyBluetoothLowEnergyStateArgs.'); try { - api.onStateChanged(arg_stateNumberArgs!); + api.onStateChanged(arg_stateArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -753,24 +1018,24 @@ abstract class MyCentralManagerFlutterApi { } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onDiscovered', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onDiscovered$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onDiscovered was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.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_darwin.MyCentralManagerFlutterApi.onDiscovered was null, expected non-null MyPeripheralArgs.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.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_darwin.MyCentralManagerFlutterApi.onDiscovered was null, expected non-null int.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.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_darwin.MyCentralManagerFlutterApi.onDiscovered was null, expected non-null MyAdvertisementArgs.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onDiscovered was null, expected non-null MyAdvertisementArgs.'); try { api.onDiscovered(arg_peripheralArgs!, arg_rssiArgs!, arg_advertisementArgs!); return wrapResponse(empty: true); @@ -784,23 +1049,23 @@ abstract class MyCentralManagerFlutterApi { } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onConnectionStateChanged', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onConnectionStateChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onConnectionStateChanged was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onConnectionStateChanged was null.'); final List args = (message as List?)!; - 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?); + final MyPeripheralArgs? arg_peripheralArgs = (args[0] as MyPeripheralArgs?); + assert(arg_peripheralArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onConnectionStateChanged was null, expected non-null MyPeripheralArgs.'); + final MyConnectionStateArgs? arg_stateArgs = args[1] == null ? null : MyConnectionStateArgs.values[args[1]! as int]; assert(arg_stateArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onConnectionStateChanged was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onConnectionStateChanged was null, expected non-null MyConnectionStateArgs.'); try { - api.onConnectionStateChanged(arg_uuidArgs!, arg_stateArgs!); + api.onConnectionStateChanged(arg_peripheralArgs!, arg_stateArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -812,26 +1077,26 @@ abstract class MyCentralManagerFlutterApi { } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicNotified', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onCharacteristicNotified$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicNotified was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onCharacteristicNotified was null.'); final List args = (message as List?)!; - 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 MyPeripheralArgs? arg_peripheralArgs = (args[0] as MyPeripheralArgs?); + assert(arg_peripheralArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onCharacteristicNotified was null, expected non-null MyPeripheralArgs.'); + final MyGATTCharacteristicArgs? arg_characteristicArgs = (args[1] as MyGATTCharacteristicArgs?); + assert(arg_characteristicArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterAPI.onCharacteristicNotified was null, expected non-null MyGATTCharacteristicArgs.'); final Uint8List? arg_valueArgs = (args[2] as Uint8List?); assert(arg_valueArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicNotified 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.onCharacteristicNotified(arg_uuidArgs!, arg_hashCodeArgs!, arg_valueArgs!); + api.onCharacteristicNotified(arg_peripheralArgs!, arg_characteristicArgs!, arg_valueArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -844,25 +1109,22 @@ abstract class MyCentralManagerFlutterApi { } } -class _MyPeripheralManagerHostApiCodec extends StandardMessageCodec { - const _MyPeripheralManagerHostApiCodec(); +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) { + } else if (value is MyMutableGATTCharacteristicArgs) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is MyGattDescriptorArgs) { + } else if (value is MyMutableGATTDescriptorArgs) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is MyGattServiceArgs) { + } else if (value is MyMutableGATTServiceArgs) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is MyManufacturerSpecificDataArgs) { - buffer.putUint8(132); - writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -874,31 +1136,32 @@ class _MyPeripheralManagerHostApiCodec extends StandardMessageCodec { case 128: return MyAdvertisementArgs.decode(readValue(buffer)!); case 129: - return MyGattCharacteristicArgs.decode(readValue(buffer)!); + return MyMutableGATTCharacteristicArgs.decode(readValue(buffer)!); case 130: - return MyGattDescriptorArgs.decode(readValue(buffer)!); + return MyMutableGATTDescriptorArgs.decode(readValue(buffer)!); case 131: - return MyGattServiceArgs.decode(readValue(buffer)!); - case 132: - return MyManufacturerSpecificDataArgs.decode(readValue(buffer)!); + return MyMutableGATTServiceArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } } } -class MyPeripheralManagerHostApi { - /// Constructor for [MyPeripheralManagerHostApi]. The [binaryMessenger] named argument is +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; + MyPeripheralManagerHostAPI({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : __pigeon_binaryMessenger = binaryMessenger, + __pigeon_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerHostAPICodec(); - Future setUp() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.setUp'; + final String __pigeon_messageChannelSuffix; + + Future initialize() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.initialize$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -919,8 +1182,57 @@ class MyPeripheralManagerHostApi { } } - Future addService(MyGattServiceArgs serviceArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.addService'; + Future getState() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.getState$__pigeon_messageChannelSuffix'; + 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 if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return MyBluetoothLowEnergyStateArgs.values[__pigeon_replyList[0]! as int]; + } + } + + Future showAppSettings() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.showAppSettings$__pigeon_messageChannelSuffix'; + 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(MyMutableGATTServiceArgs serviceArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.addService$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -942,7 +1254,7 @@ class MyPeripheralManagerHostApi { } Future removeService(int hashCodeArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.removeService'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.removeService$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -963,8 +1275,8 @@ class MyPeripheralManagerHostApi { } } - Future clearServices() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.clearServices'; + Future removeAllServices() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.removeAllServices$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -986,7 +1298,7 @@ class MyPeripheralManagerHostApi { } Future startAdvertising(MyAdvertisementArgs advertisementArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.startAdvertising'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.startAdvertising$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -1008,7 +1320,7 @@ class MyPeripheralManagerHostApi { } Future stopAdvertising() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.stopAdvertising'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.stopAdvertising$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -1029,8 +1341,8 @@ class MyPeripheralManagerHostApi { } } - Future getMaximumUpdateValueLength(String uuidArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.getMaximumUpdateValueLength'; + Future getMaximumNotifyLength(String uuidArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.getMaximumNotifyLength$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -1056,15 +1368,15 @@ class MyPeripheralManagerHostApi { } } - Future respond(int idArgs, int errorNumberArgs, Uint8List? valueArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.respond'; + Future respond(int hashCodeArgs, Uint8List? valueArgs, MyATTErrorArgs errorArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.respond$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send([idArgs, errorNumberArgs, valueArgs]) as List?; + await __pigeon_channel.send([hashCodeArgs, valueArgs, errorArgs.index]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -1078,8 +1390,8 @@ class MyPeripheralManagerHostApi { } } - Future updateCharacteristic(int hashCodeArgs, Uint8List valueArgs, List? uuidsArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.updateCharacteristic'; + Future updateValue(int hashCodeArgs, Uint8List valueArgs, List? uuidsArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostAPI.updateValue$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -1095,19 +1407,30 @@ class MyPeripheralManagerHostApi { message: __pigeon_replyList[1] as String?, details: __pigeon_replyList[2], ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { - return; + return (__pigeon_replyList[0] as bool?)!; } } } -class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { - const _MyPeripheralManagerFlutterApiCodec(); +class _MyPeripheralManagerFlutterAPICodec extends StandardMessageCodec { + const _MyPeripheralManagerFlutterAPICodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is MyCentralArgs) { + if (value is MyATTRequestArgs) { buffer.putUint8(128); writeValue(buffer, value.encode()); + } else if (value is MyATTRequestArgs) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MyCentralArgs) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -1117,6 +1440,10 @@ class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: + return MyATTRequestArgs.decode(readValue(buffer)!); + case 129: + return MyATTRequestArgs.decode(readValue(buffer)!); + case 130: return MyCentralArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -1124,34 +1451,37 @@ class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { } } -abstract class MyPeripheralManagerFlutterApi { - static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerFlutterApiCodec(); +abstract class MyPeripheralManagerFlutterAPI { + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerFlutterAPICodec(); - void onStateChanged(int stateNumberArgs); + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); - void onCharacteristicReadRequest(MyCentralArgs centralArgs, int hashCodeArgs, int idArgs, int offsetArgs); + void didReceiveRead(MyATTRequestArgs requestArgs); - void onCharacteristicWriteRequest(MyCentralArgs centralArgs, int hashCodeArgs, int idArgs, int offsetArgs, Uint8List valueArgs); + void didReceiveWrite(List requestsArgs); + + void isReady(); void onCharacteristicNotifyStateChanged(MyCentralArgs centralArgs, int hashCodeArgs, bool stateArgs); - static void setup(MyPeripheralManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + static void setUp(MyPeripheralManagerFlutterAPI? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onStateChanged', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.onStateChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onStateChanged was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.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_darwin.MyPeripheralManagerFlutterApi.onStateChanged was null, expected non-null int.'); + final MyBluetoothLowEnergyStateArgs? arg_stateArgs = args[0] == null ? null : MyBluetoothLowEnergyStateArgs.values[args[0]! as int]; + assert(arg_stateArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.onStateChanged was null, expected non-null MyBluetoothLowEnergyStateArgs.'); try { - api.onStateChanged(arg_stateNumberArgs!); + api.onStateChanged(arg_stateArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1163,29 +1493,20 @@ abstract class MyPeripheralManagerFlutterApi { } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.didReceiveRead$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.didReceiveRead 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.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.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.onCharacteristicReadRequest was null, expected non-null int.'); + final MyATTRequestArgs? arg_requestArgs = (args[0] as MyATTRequestArgs?); + assert(arg_requestArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.didReceiveRead was null, expected non-null MyATTRequestArgs.'); try { - api.onCharacteristicReadRequest(arg_centralArgs!, arg_hashCodeArgs!, arg_idArgs!, arg_offsetArgs!); + api.didReceiveRead(arg_requestArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1197,32 +1518,20 @@ abstract class MyPeripheralManagerFlutterApi { } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.didReceiveWrite$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest was null.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.didReceiveWrite 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.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.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.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.onCharacteristicWriteRequest was null, expected non-null Uint8List.'); + final List? arg_requestsArgs = (args[0] as List?)?.cast(); + assert(arg_requestsArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.didReceiveWrite was null, expected non-null List.'); try { - api.onCharacteristicWriteRequest(arg_centralArgs!, arg_hashCodeArgs!, arg_idArgs!, arg_offsetArgs!, arg_valueArgs!); + api.didReceiveWrite(arg_requestsArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1234,24 +1543,43 @@ abstract class MyPeripheralManagerFlutterApi { } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterAPI.isReady$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + try { + api.isReady(); + 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_darwin.MyPeripheralManagerFlutterAPI.onCharacteristicNotifyStateChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged 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.onCharacteristicNotifyStateChanged was null, expected non-null MyCentralArgs.'); + '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.'); + '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.onCharacteristicNotifyStateChanged 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.onCharacteristicNotifyStateChanged(arg_centralArgs!, arg_hashCodeArgs!, arg_stateArgs!); return wrapResponse(empty: true); diff --git a/bluetooth_low_energy_darwin/lib/src/my_central_manager.dart b/bluetooth_low_energy_darwin/lib/src/my_central_manager.dart index dbd8f41..e16a7ae 100644 --- a/bluetooth_low_energy_darwin/lib/src/my_central_manager.dart +++ b/bluetooth_low_energy_darwin/lib/src/my_central_manager.dart @@ -1,66 +1,81 @@ import 'dart:async'; +import 'dart:io'; +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_gatt_characteristic2.dart'; -import 'my_gatt_descriptor2.dart'; +import 'my_api.g.dart'; +import 'my_gatt.dart'; -class MyCentralManager extends CentralManager - implements MyCentralManagerFlutterApi { - final MyCentralManagerHostApi _api; +final class MyCentralManager extends PlatformCentralManager + implements MyCentralManagerFlutterAPI { + final MyCentralManagerHostAPI _api; final StreamController _stateChangedController; final StreamController _discoveredController; - final StreamController + final StreamController _connectionStateChangedController; - final StreamController + final StreamController _characteristicNotifiedController; - final Map _peripherals; - final Map> _characteristics; BluetoothLowEnergyState _state; MyCentralManager() - : _api = MyCentralManagerHostApi(), + : _api = MyCentralManagerHostAPI(), _stateChangedController = StreamController.broadcast(), _discoveredController = StreamController.broadcast(), _connectionStateChangedController = StreamController.broadcast(), _characteristicNotifiedController = StreamController.broadcast(), - _peripherals = {}, - _characteristics = {}, _state = BluetoothLowEnergyState.unknown; + @override + BluetoothLowEnergyState get state => _state; @override Stream get stateChanged => _stateChangedController.stream; @override Stream get discovered => _discoveredController.stream; @override - Stream get connectionStateChanged => - _connectionStateChangedController.stream; + Stream + get connectionStateChanged => _connectionStateChangedController.stream; @override - Stream get characteristicNotified => + Stream get mtuChanged => + throw UnsupportedError('mtuChanged is not supported on Darwin.'); + @override + Stream get characteristicNotified => _characteristicNotifiedController.stream; @override - Future setUp() async { - logger.info('setUp'); - await _api.setUp(); - MyCentralManagerFlutterApi.setup(this); + void initialize() { + MyCentralManagerFlutterAPI.setUp(this); + _initialize(); } @override - Future getState() { - logger.info('getState'); - return Future.value(_state); + Future authorize() { + throw UnsupportedError('authorize is not supported on Darwin.'); } @override - Future startDiscovery() async { - logger.info('startDiscovery'); - await _api.startDiscovery(); + Future showAppSettings() async { + if (Platform.isIOS) { + logger.info('showAppSettings'); + await _api.showAppSettings(); + } else { + throw UnsupportedError( + 'showAppSettings is not supported on ${Platform.operatingSystem}.'); + } + } + + @override + Future startDiscovery({ + List? serviceUUIDs, + }) async { + final serviceUUIDsArgs = + serviceUUIDs?.map((uuid) => uuid.toArgs()).toList() ?? []; + logger.info('startDiscovery: $serviceUUIDsArgs'); + await _api.startDiscovery(serviceUUIDsArgs); } @override @@ -69,11 +84,19 @@ class MyCentralManager extends CentralManager await _api.stopDiscovery(); } + @override + Future> retrieveConnectedPeripherals() async { + logger.info('retrieveConnectedPeripherals'); + final peripheralsArgs = await _api.retrieveConnectedPeripherals(); + final peripherals = peripheralsArgs + .cast() + .map((args) => args.toPeripheral()) + .toList(); + return peripherals; + } + @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); @@ -81,19 +104,36 @@ class MyCentralManager extends CentralManager @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 requestMTU( + Peripheral peripheral, { + required int mtu, + }) { + throw UnsupportedError('requestMTU is not supported on Darwin.'); + } + + @override + Future getMaximumWriteLength( + Peripheral peripheral, { + required GATTCharacteristicWriteType type, + }) async { + final uuidArgs = peripheral.uuid.toArgs(); + final typeArgs = type.toArgs(); + logger.info('getMaximumWriteLength: $uuidArgs - $typeArgs'); + final maximumWriteLength = await _api.getMaximumWriteLength( + uuidArgs, + typeArgs, + ); + return maximumWriteLength; + } + @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); @@ -101,53 +141,22 @@ class MyCentralManager extends CentralManager } @override - Future> discoverGATT(Peripheral peripheral) async { - if (peripheral is! MyPeripheral) { - throw TypeError(); - } + Future> discoverGATT(Peripheral peripheral) async { // 发现 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 - }; + final servicesArgs = await _discoverServices(uuidArgs); + final services = servicesArgs.map((args) => args.toService()).toList(); return services; } @override Future readCharacteristic( - GattCharacteristic characteristic, + Peripheral peripheral, + GATTCharacteristic characteristic, ) async { - if (characteristic is! MyGattCharacteristic2) { + if (characteristic is! MyGATTCharacteristic) { throw TypeError(); } - final peripheral = characteristic.peripheral; final uuidArgs = peripheral.uuid.toArgs(); final hashCodeArgs = characteristic.hashCode; logger.info('readCharacteristic: $uuidArgs.$hashCodeArgs'); @@ -157,97 +166,73 @@ class MyCentralManager extends CentralManager @override Future writeCharacteristic( - GattCharacteristic characteristic, { + Peripheral peripheral, + GATTCharacteristic characteristic, { required Uint8List value, - required GattCharacteristicWriteType type, + required GATTCharacteristicWriteType type, }) async { - if (characteristic is! MyGattCharacteristic2) { + if (characteristic is! MyGATTCharacteristic) { throw TypeError(); } - final peripheral = characteristic.peripheral; final uuidArgs = peripheral.uuid.toArgs(); final hashCodeArgs = characteristic.hashCode; - final trimmedValueArgs = value.trimGATT(); + final valueArgs = value; 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; - } + logger.info( + 'writeCharacteristic: $uuidArgs.$hashCodeArgs - $valueArgs, $typeArgs'); + await _api.writeCharacteristic(uuidArgs, hashCodeArgs, valueArgs, typeArgs); } @override Future setCharacteristicNotifyState( - GattCharacteristic characteristic, { + Peripheral peripheral, + GATTCharacteristic characteristic, { required bool state, }) async { - if (characteristic is! MyGattCharacteristic2) { + if (characteristic is! MyGATTCharacteristic) { 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, - ); + await _api.setCharacteristicNotifyState(uuidArgs, hashCodeArgs, stateArgs); } @override - Future readDescriptor(GattDescriptor descriptor) async { - if (descriptor is! MyGattDescriptor2) { + Future readDescriptor( + Peripheral peripheral, + GATTDescriptor descriptor, + ) async { + if (descriptor is! MyGATTDescriptor) { 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, - ); + final value = await _api.readDescriptor(uuidArgs, hashCodeArgs); return value; } @override Future writeDescriptor( - GattDescriptor descriptor, { + Peripheral peripheral, + GATTDescriptor descriptor, { required Uint8List value, }) async { - if (descriptor is! MyGattDescriptor2) { + if (descriptor is! MyGATTDescriptor) { 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); + final valueArgs = value; + logger.info('writeDescriptor: $uuidArgs.$hashCodeArgs - $valueArgs'); + await _api.writeDescriptor(uuidArgs, hashCodeArgs, valueArgs); } @override - void onStateChanged(int stateNumberArgs) { - final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs) { logger.info('onStateChanged: $stateArgs'); final state = stateArgs.toState(); if (_state == state) { @@ -266,15 +251,11 @@ class MyCentralManager extends CentralManager ) { final uuidArgs = peripheralArgs.uuidArgs; logger.info('onDiscovered: $uuidArgs - $rssiArgs, $advertisementArgs'); - final peripheral = _peripherals.putIfAbsent( - peripheralArgs.uuidArgs, - () => peripheralArgs.toPeripheral(), - ); - final rssi = rssiArgs; + final peripheral = peripheralArgs.toPeripheral(); final advertisement = advertisementArgs.toAdvertisement(); final eventArgs = DiscoveredEventArgs( peripheral, - rssi, + rssiArgs, advertisement, ); _discoveredController.add(eventArgs); @@ -282,50 +263,135 @@ class MyCentralManager extends CentralManager @override void onConnectionStateChanged( - String uuidArgs, - bool stateArgs, + MyPeripheralArgs peripheralArgs, + MyConnectionStateArgs stateArgs, ) { + final uuidArgs = peripheralArgs.uuidArgs; logger.info('onConnectionStateChanged: $uuidArgs - $stateArgs'); - final peripheral = _peripherals[uuidArgs]; - if (peripheral == null) { - return; - } - final state = stateArgs; - final eventArgs = ConnectionStateChangedEventArgs(peripheral, state); + final peripheral = peripheralArgs.toPeripheral(); + final state = stateArgs.toState(); + final eventArgs = PeripheralConnectionStateChangedEventArgs( + peripheral, + state, + ); _connectionStateChangedController.add(eventArgs); - if (!state) { - _characteristics.remove(uuidArgs); - } } @override void onCharacteristicNotified( - String uuidArgs, - int hashCodeArgs, + MyPeripheralArgs peripheralArgs, + MyGATTCharacteristicArgs characteristicArgs, Uint8List valueArgs, ) { + final uuidArgs = peripheralArgs.uuidArgs; + final hashCodeArgs = characteristicArgs.hashCodeArgs; logger .info('onCharacteristicNotified: $uuidArgs.$hashCodeArgs - $valueArgs'); - final characteristic = _retrieveCharacteristic(uuidArgs, hashCodeArgs); - if (characteristic == null) { - return; - } - final value = valueArgs; - final eventArgs = GattCharacteristicNotifiedEventArgs( + final peripheral = peripheralArgs.toPeripheral(); + final characteristic = characteristicArgs.toCharacteristic(); + final eventArgs = GATTCharacteristicNotifiedEventArgs( + peripheral, characteristic, - value, + valueArgs, ); _characteristicNotifiedController.add(eventArgs); } - MyGattCharacteristic2? _retrieveCharacteristic( + Future _initialize() async { + // Here we use `Future()` to make it possible to change the `logLevel` before `initialize()`. + await Future(() async { + try { + logger.info('initialize'); + await _api.initialize(); + _getState(); + } catch (e) { + logger.severe('initialize failed.', e); + } + }); + } + + Future _getState() async { + try { + logger.info('getState'); + final stateArgs = await _api.getState(); + onStateChanged(stateArgs); + } catch (e) { + logger.severe('getState failed.', e); + } + } + + Future> _discoverServices(String uuidArgs) async { + logger.info('discoverServices: $uuidArgs'); + final servicesArgs = await _api + .discoverServices(uuidArgs) + .then((args) => args.cast()); + for (var serviceArgs in servicesArgs) { + final hashCodeArgs = serviceArgs.hashCodeArgs; + final includedServicesArgs = await _discoverIncludedServices( + uuidArgs, + hashCodeArgs, + ); + serviceArgs.includedServicesArgs = includedServicesArgs; + final characteristicsArgs = await _discoverCharacteristics( + uuidArgs, + hashCodeArgs, + ); + serviceArgs.characteristicsArgs = characteristicsArgs; + } + return servicesArgs; + } + + Future> _discoverIncludedServices( String uuidArgs, int hashCodeArgs, - ) { - final characteristics = _characteristics[uuidArgs]; - if (characteristics == null) { - return null; + ) async { + logger.info('discoverIncludedServices: $uuidArgs.$hashCodeArgs'); + final servicesArgs = await _api + .discoverIncludedServices(uuidArgs, hashCodeArgs) + .then((args) => args.cast()); + for (var serviceArgs in servicesArgs) { + final hashCodeArgs = serviceArgs.hashCodeArgs; + final includedServicesArgs = await _discoverIncludedServices( + uuidArgs, + hashCodeArgs, + ); + serviceArgs.includedServicesArgs = includedServicesArgs; + final characteristicsArgs = await _discoverCharacteristics( + uuidArgs, + hashCodeArgs, + ); + serviceArgs.characteristicsArgs = characteristicsArgs; } - return characteristics[hashCodeArgs]; + return servicesArgs; + } + + Future> _discoverCharacteristics( + String uuidArgs, + int hashCodeArgs, + ) async { + logger.info('discoverCharacteristics: $uuidArgs.$hashCodeArgs'); + final characteristicsArgs = await _api + .discoverCharacteristics(uuidArgs, hashCodeArgs) + .then((args) => args.cast()); + for (var characteristicArgs in characteristicsArgs) { + final hashCodeArgs = characteristicArgs.hashCodeArgs; + final descriptorsArgs = await _discoverDescriptors( + uuidArgs, + hashCodeArgs, + ); + characteristicArgs.descriptorsArgs = descriptorsArgs; + } + return characteristicsArgs; + } + + Future> _discoverDescriptors( + String uuidArgs, + int hashCodeArgs, + ) async { + logger.info('discoverDescriptors: $uuidArgs.$hashCodeArgs'); + final descriptorsArgs = await _api + .discoverDescriptors(uuidArgs, hashCodeArgs) + .then((args) => args.cast()); + return descriptorsArgs; } } diff --git a/bluetooth_low_energy_darwin/lib/src/my_gatt.dart b/bluetooth_low_energy_darwin/lib/src/my_gatt.dart new file mode 100644 index 0000000..8f7bee7 --- /dev/null +++ b/bluetooth_low_energy_darwin/lib/src/my_gatt.dart @@ -0,0 +1,93 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +final class MyGATTDescriptor extends GATTDescriptor { + final int hashCodeArgs; + + MyGATTDescriptor({ + required this.hashCodeArgs, + required super.uuid, + }); + + @override + int get hashCode => hashCodeArgs; + + @override + bool operator ==(Object other) { + return other is MyGATTDescriptor && other.hashCodeArgs == hashCodeArgs; + } +} + +final class MyGATTCharacteristic extends GATTCharacteristic { + final int hashCodeArgs; + + MyGATTCharacteristic({ + required this.hashCodeArgs, + required super.uuid, + required super.properties, + required List descriptors, + }) : super( + descriptors: descriptors, + ); + + @override + List get descriptors => + super.descriptors.cast(); + + @override + int get hashCode => hashCodeArgs; + + @override + bool operator ==(Object other) { + return other is MyGATTCharacteristic && other.hashCodeArgs == hashCodeArgs; + } +} + +final class MyGATTService extends GATTService { + final int hashCodeArgs; + + MyGATTService({ + required this.hashCodeArgs, + required super.uuid, + required super.isPrimary, + required List includedServices, + required List characteristics, + }) : super( + includedServices: includedServices, + characteristics: characteristics, + ); + + @override + List get includedServices => + super.includedServices.cast(); + + @override + List get characteristics => + super.characteristics.cast(); + + @override + int get hashCode => hashCodeArgs; + + @override + bool operator ==(Object other) { + return other is MyGATTService && other.hashCodeArgs == hashCodeArgs; + } +} + +final class MyGATTReadRequest extends GATTReadRequest { + final int hashCodeArgs; + + MyGATTReadRequest({ + required this.hashCodeArgs, + required super.offset, + }); +} + +final class MyGATTWriteRequest extends GATTWriteRequest { + final int hashCodeArgs; + + MyGATTWriteRequest({ + required this.hashCodeArgs, + required super.offset, + required super.value, + }); +} diff --git a/bluetooth_low_energy_darwin/lib/src/my_gatt_characteristic2.dart b/bluetooth_low_energy_darwin/lib/src/my_gatt_characteristic2.dart deleted file mode 100644 index 5c0cde6..0000000 --- a/bluetooth_low_energy_darwin/lib/src/my_gatt_characteristic2.dart +++ /dev/null @@ -1,28 +0,0 @@ -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; - @override - final int hashCode; - - MyGattCharacteristic2({ - required this.peripheral, - required this.hashCode, - required super.uuid, - required super.properties, - required List descriptors, - }) : super(descriptors: descriptors); - - @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 deleted file mode 100644 index 658a94f..0000000 --- a/bluetooth_low_energy_darwin/lib/src/my_gatt_descriptor2.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; - -class MyGattDescriptor2 extends MyGattDescriptor { - final MyPeripheral peripheral; - @override - final int hashCode; - - MyGattDescriptor2({ - 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 deleted file mode 100644 index e57024c..0000000 --- a/bluetooth_low_energy_darwin/lib/src/my_gatt_service2.dart +++ /dev/null @@ -1,27 +0,0 @@ -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; - @override - final int hashCode; - - MyGattService2({ - required this.peripheral, - required this.hashCode, - required super.uuid, - required List characteristics, - }) : super(characteristics: characteristics); - - @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 index 4b00d92..dd00b70 100644 --- a/bluetooth_low_energy_darwin/lib/src/my_peripheral_manager.dart +++ b/bluetooth_low_energy_darwin/lib/src/my_peripheral_manager.dart @@ -1,100 +1,117 @@ import 'dart:async'; +import 'dart:io'; +import 'dart:typed_data'; +import 'package:bluetooth_low_energy_darwin/src/my_gatt.dart'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:flutter/foundation.dart'; import 'my_api.dart'; +import 'my_api.g.dart'; -class MyPeripheralManager extends PeripheralManager - implements MyPeripheralManagerFlutterApi { - final MyPeripheralManagerHostApi _api; +final class MyPeripheralManager extends PlatformPeripheralManager + implements MyPeripheralManagerFlutterAPI { + final MyPeripheralManagerHostAPI _api; final StreamController _stateChangedController; - final StreamController - _characteristicReadController; - final StreamController - _characteristicWrittenController; - final StreamController + final StreamController + _characteristicReadRequestedController; + final StreamController + _characteristicWriteRequestedController; + final StreamController _characteristicNotifyStateChangedController; + final StreamController _isReadyController; - final Map> _characteristics; - final Map> _listeners; + final Map _characteristics; BluetoothLowEnergyState _state; MyPeripheralManager() - : _api = MyPeripheralManagerHostApi(), + : _api = MyPeripheralManagerHostAPI(), _stateChangedController = StreamController.broadcast(), - _characteristicReadController = StreamController.broadcast(), - _characteristicWrittenController = StreamController.broadcast(), + _characteristicReadRequestedController = StreamController.broadcast(), + _characteristicWriteRequestedController = StreamController.broadcast(), _characteristicNotifyStateChangedController = StreamController.broadcast(), + _isReadyController = StreamController.broadcast(), _characteristics = {}, - _listeners = {}, _state = BluetoothLowEnergyState.unknown; + @override + BluetoothLowEnergyState get state => _state; @override Stream get stateChanged => _stateChangedController.stream; @override - Stream get characteristicRead => - _characteristicReadController.stream; + Stream get connectionStateChanged => + throw UnsupportedError( + 'connectionStateChanged is not supported on Darwin.'); @override - Stream get characteristicWritten => - _characteristicWrittenController.stream; + Stream get mtuChanged => + throw UnsupportedError('mtuChanged is not supported on Darwin.'); @override - Stream + Stream + get characteristicReadRequested => + _characteristicReadRequestedController.stream; + @override + Stream + get characteristicWriteRequested => + _characteristicWriteRequestedController.stream; + @override + Stream get characteristicNotifyStateChanged => _characteristicNotifyStateChangedController.stream; + @override + Stream get descriptorReadRequested => + throw UnsupportedError( + 'descriptorReadRequested is not supported on Darwin.'); + @override + Stream get descriptorWriteRequested => + throw UnsupportedError( + 'descriptorWriteRequested is not supported on Darwin.'); + Stream get _isReady => _isReadyController.stream; @override - Future setUp() async { - logger.info('setUp'); - await _api.setUp(); - MyPeripheralManagerFlutterApi.setup(this); + void initialize() { + MyPeripheralManagerFlutterAPI.setUp(this); + _initialize(); } @override - Future getState() { - logger.info('getState'); - return Future.value(_state); + Future authorize() { + throw UnsupportedError('authorize is not supported on Darwin.'); } @override - Future addService(GattService service) async { - if (service is! MyGattService) { - throw TypeError(); + Future showAppSettings() async { + if (Platform.isIOS) { + logger.info('showAppSettings'); + await _api.showAppSettings(); + } else { + throw UnsupportedError( + 'showAppSettings is not supported on ${Platform.operatingSystem}.'); } - final characteristics = {}; - final characteristicsArgs = []; - for (var characteristic in service.characteristics) { - final descriptorsArgs = []; - for (var descriptor in characteristic.descriptors) { - final descriptorArgs = descriptor.toArgs(); - descriptorsArgs.add(descriptorArgs); - } - final characteristicArgs = characteristic.toArgs(descriptorsArgs); - characteristicsArgs.add(characteristicArgs); - characteristics[characteristicArgs.hashCodeArgs] = characteristic; - } - final serviceArgs = service.toArgs(characteristicsArgs); + } + + @override + Future addService(GATTService service) async { + final serviceArgs = service.toArgs(); logger.info('addService: $serviceArgs'); await _api.addService(serviceArgs); - _characteristics[serviceArgs.hashCodeArgs] = characteristics; + _addService(service); } @override - Future removeService(GattService service) async { + Future removeService(GATTService service) async { final hashCodeArgs = service.hashCode; logger.info('removeService: $hashCodeArgs'); await _api.removeService(hashCodeArgs); - _characteristics.remove(hashCodeArgs); + _removeService(service); } @override - Future clearServices() async { - logger.info('clearServices'); - await _api.clearServices(); + Future removeAllServices() async { + logger.info('removeAllServices'); + await _api.removeAllServices(); _characteristics.clear(); } @@ -112,62 +129,112 @@ class MyPeripheralManager extends PeripheralManager } @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); + Future getMaximumNotifyLength(Central central) async { + final uuidArgs = central.uuid.toArgs(); + logger.info('getMaximumNotifyLength: $uuidArgs'); + final maximumNotifyLength = await _api.getMaximumNotifyLength(uuidArgs); + return maximumNotifyLength; } @override - Future writeCharacteristic( - GattCharacteristic characteristic, { + Future respondReadRequestWithValue( + GATTReadRequest request, { required Uint8List value, - Central? central, }) async { - if (characteristic is! MyGattCharacteristic) { + if (request is! MyGATTReadRequest) { throw TypeError(); } - characteristic.value = value; - if (central == null) { - return; - } - if (central is! MyCentral) { + final hashCodeArgs = request.hashCodeArgs; + final valueArgs = value; + const errorArgs = MyATTErrorArgs.success; + logger.info('respond: $hashCodeArgs - $valueArgs, $errorArgs'); + await _api.respond( + hashCodeArgs, + valueArgs, + errorArgs, + ); + } + + @override + Future respondReadRequestWithError( + GATTReadRequest request, { + required GATTError error, + }) async { + if (request is! MyGATTReadRequest) { throw TypeError(); } - final uuidArgs = central.uuid.toArgs(); + final hashCodeArgs = request.hashCodeArgs; + const valueArgs = null; + final errorArgs = error.toArgs(); + logger.info('respond: $hashCodeArgs - $valueArgs, $errorArgs'); + await _api.respond( + hashCodeArgs, + valueArgs, + errorArgs, + ); + } + + @override + Future respondWriteRequest(GATTWriteRequest request) async { + if (request is! MyGATTWriteRequest) { + throw TypeError(); + } + final hashCodeArgs = request.hashCodeArgs; + const valueArgs = null; + const errorArgs = MyATTErrorArgs.success; + logger.info('respond: $hashCodeArgs - $valueArgs, $errorArgs'); + await _api.respond( + hashCodeArgs, + valueArgs, + errorArgs, + ); + } + + @override + Future respondWriteRequestWithError( + GATTWriteRequest request, { + required GATTError error, + }) async { + if (request is! MyGATTWriteRequest) { + throw TypeError(); + } + final hashCodeArgs = request.hashCodeArgs; + const valueArgs = null; + final errorArgs = error.toArgs(); + logger.info('respond: $hashCodeArgs - $valueArgs, $errorArgs'); + await _api.respond( + hashCodeArgs, + valueArgs, + errorArgs, + ); + } + + @override + Future notifyCharacteristic( + Central central, + GATTCharacteristic characteristic, { + required Uint8List value, + }) async { final hashCodeArgs = characteristic.hashCode; - final listener = _retrieveListener(uuidArgs, hashCodeArgs); - if (listener == null) { - logger.warning('The central is not listening.'); - return; - } + final valueArgs = value; + final uuidArgs = central.uuid.toArgs(); 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( + while (true) { + logger.info('updateValue: $hashCodeArgs - $valueArgs, $uuidsArgs'); + final updated = await _api.updateValue( hashCodeArgs, - fragmentedValueArgs, + valueArgs, uuidsArgs, ); - start = end; + if (updated) { + break; + } + await _isReady.first; } } @override - void onStateChanged(int stateNumberArgs) { - final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs) { logger.info('onStateChanged: $stateArgs'); final state = stateArgs.toState(); if (_state == state) { @@ -179,54 +246,82 @@ class MyPeripheralManager extends PeripheralManager } @override - void onCharacteristicReadRequest( - MyCentralArgs centralArgs, - int hashCodeArgs, - int idArgs, - int offsetArgs, - ) async { - final uuidArgs = centralArgs.uuidArgs; + void didReceiveRead(MyATTRequestArgs requestArgs) async { + final centralArgs = requestArgs.centralArgs; + final hashCodeArgs = requestArgs.hashCodeArgs; + final characteristicHashCodeArgs = requestArgs.characteristicHashCodeArgs; + final offsetArgs = requestArgs.offsetArgs; + final valueArgs = requestArgs.valueArgs; logger.info( - 'onCharacteristicReadRequest: $uuidArgs.$hashCodeArgs - $idArgs, $offsetArgs'); + 'didReceiveRead: ${centralArgs.uuidArgs} - $hashCodeArgs, $characteristicHashCodeArgs, $offsetArgs, $valueArgs'); final central = centralArgs.toCentral(); - final characteristic = _retrieveCharacteristic(hashCodeArgs); + final characteristic = _characteristics[characteristicHashCodeArgs]; if (characteristic == null) { - return; + await _respond(hashCodeArgs, null, MyATTErrorArgs.attributeNotFound); + } else { + final eventArgs = GATTCharacteristicReadRequestedEventArgs( + central, + characteristic, + MyGATTReadRequest( + hashCodeArgs: hashCodeArgs, + offset: offsetArgs, + ), + ); + _characteristicReadRequestedController.add(eventArgs); } - 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; + void didReceiveWrite(List requestsArgs) async { + // 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 + final requestArgs = requestsArgs.cast().first; + final centralArgs = requestArgs.centralArgs; + final hashCodeArgs = requestArgs.hashCodeArgs; + final characteristicHashCodeArgs = requestArgs.characteristicHashCodeArgs; + final offsetArgs = requestArgs.offsetArgs; + final unsupported = requestsArgs.cast().any((args) => + args.centralArgs.uuidArgs != centralArgs.uuidArgs || + args.characteristicHashCodeArgs != characteristicHashCodeArgs); + if (unsupported) { + await _respond(hashCodeArgs, null, MyATTErrorArgs.unsupportedGroupType); + } else { + final central = centralArgs.toCentral(); + final characteristic = _characteristics[characteristicHashCodeArgs]; + if (characteristic == null) { + await _respond(hashCodeArgs, null, MyATTErrorArgs.attributeNotFound); + } else { + final elements = requestsArgs.cast().fold( + [], + (previousValue, args) { + final valueArgs = args.valueArgs; + if (valueArgs != null) { + previousValue.insertAll(args.offsetArgs, valueArgs); + } + return previousValue; + }, + ); + final eventArgs = GATTCharacteristicWriteRequestedEventArgs( + central, + characteristic, + MyGATTWriteRequest( + hashCodeArgs: hashCodeArgs, + offset: offsetArgs, + value: Uint8List.fromList(elements), + ), + ); + _characteristicWriteRequestedController.add(eventArgs); + } } - const errorArgs = MyGattErrorArgs.success; - final value = valueArgs; - _onCharacteristicWritten(central, characteristic, value); - await _tryRespond( - idArgs, - errorArgs, - null, - ); + } + + @override + void isReady() { + final eventArgs = EventArgs(); + _isReadyController.add(eventArgs); } @override @@ -237,88 +332,80 @@ class MyPeripheralManager extends PeripheralManager ) { final uuidArgs = centralArgs.uuidArgs; logger.info( - 'onCharacteristicNotifyStateChanged: $uuidArgs.$hashCodeArgs - $stateArgs'); + 'onCharacteristicNotifyStateChanged: $uuidArgs - $hashCodeArgs, $stateArgs'); final central = centralArgs.toCentral(); - final characteristic = _retrieveCharacteristic(hashCodeArgs); + final characteristic = _characteristics[hashCodeArgs]; if (characteristic == null) { + logger.warning('The characteristic[$hashCodeArgs] is null.'); return; } - final state = stateArgs; - final listeners = _listeners.putIfAbsent(uuidArgs, () => {}); - if (state) { - listeners[hashCodeArgs] = true; - } else { - listeners.remove(hashCodeArgs); - } - final eventArgs = GattCharacteristicNotifyStateChangedEventArgs( + final eventArgs = GATTCharacteristicNotifyStateChangedEventArgs( central, characteristic, - state, + stateArgs, ); _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; + void _addService(GATTService service) { + for (var includedService in service.includedServices) { + _addService(includedService); + } + for (var characteristic in service.characteristics) { + if (characteristic is! MutableGATTCharacteristic) { + throw TypeError(); + } + _characteristics[characteristic.hashCode] = characteristic; } - return listeners[hashCodeArgs]; } - Future _tryRespond( - int idArgs, - MyGattErrorArgs errorArgs, - Uint8List? valueArgs, - ) async { - final errorNumberArgs = errorArgs.index; + void _removeService(GATTService service) { + for (var includedService in service.includedServices) { + _removeService(includedService); + } + for (var characteristic in service.characteristics) { + final hashCodeArgs = characteristic.hashCode; + _characteristics.remove(hashCodeArgs); + } + } + + Future _initialize() async { + // Here we use `Future()` to make it possible to change the `logLevel` before `initialize()`. + await Future(() async { + try { + logger.info('initialize'); + await _api.initialize(); + _getState(); + } catch (e) { + logger.severe('initialize failed.', e); + } + }); + } + + Future _getState() async { try { - _api.respond( - idArgs, - errorNumberArgs, - valueArgs, - ); - } catch (e, stack) { - logger.shout('Respond failed.', e, stack); + logger.info('getState'); + final stateArgs = await _api.getState(); + onStateChanged(stateArgs); + } catch (e) { + logger.severe('getState failed.', e); } } - 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, + Future _respond( + int hashCodeArgs, + Uint8List? valueArgs, + MyATTErrorArgs errorArgs, ) async { - characteristic.value = value; - final trimmedValue = characteristic.value; - final eventArgs = GattCharacteristicWrittenEventArgs( - central, - characteristic, - trimmedValue, - ); - _characteristicWrittenController.add(eventArgs); + try { + logger.info('respond: $hashCodeArgs - $valueArgs, $errorArgs'); + await _api.respond( + hashCodeArgs, + valueArgs, + errorArgs, + ); + } catch (e) { + logger.severe('respond failed.', e); + } } } diff --git a/bluetooth_low_energy_darwin/my_api.dart b/bluetooth_low_energy_darwin/my_api.dart index 35d562a..2dadc02 100644 --- a/bluetooth_low_energy_darwin/my_api.dart +++ b/bluetooth_low_energy_darwin/my_api.dart @@ -1,10 +1,13 @@ +// Run with `dart run pigeon --input my_api.dart`. import 'package:pigeon/pigeon.dart'; +// TODO: Use `@ProxyApi` to manage instancs when this feature released: +// https://github.com/flutter/flutter/issues/147486 @ConfigurePigeon( PigeonOptions( dartOut: 'lib/src/my_api.g.dart', dartOptions: DartOptions(), - swiftOut: 'darwin/Classes/MyApi.g.swift', + swiftOut: 'darwin/Classes/MyAPI.g.swift', swiftOptions: SwiftOptions(), ), ) @@ -17,7 +20,12 @@ enum MyBluetoothLowEnergyStateArgs { poweredOn, } -enum MyGattCharacteristicPropertyArgs { +enum MyConnectionStateArgs { + disconnected, + connected, +} + +enum MyGATTCharacteristicPropertyArgs { read, write, writeWithoutResponse, @@ -25,12 +33,19 @@ enum MyGattCharacteristicPropertyArgs { indicate, } -enum MyGattCharacteristicWriteTypeArgs { +enum MyGATTCharacteristicPermissionArgs { + read, + readEncrypted, + write, + writeEncrypted, +} + +enum MyGATTCharacteristicWriteTypeArgs { withResponse, withoutResponse, } -enum MyGattErrorArgs { +enum MyATTErrorArgs { success, invalidHandle, readNotPermitted, @@ -62,7 +77,7 @@ class MyAdvertisementArgs { final String? nameArgs; final List serviceUUIDsArgs; final Map serviceDataArgs; - final MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs; + final Uint8List? manufacturerSpecificDataArgs; MyAdvertisementArgs( this.nameArgs, @@ -84,25 +99,23 @@ class MyPeripheralArgs { MyPeripheralArgs(this.uuidArgs); } -class MyGattDescriptorArgs { +class MyGATTDescriptorArgs { final int hashCodeArgs; final String uuidArgs; - final Uint8List? valueArgs; - MyGattDescriptorArgs( + MyGATTDescriptorArgs( this.hashCodeArgs, this.uuidArgs, - this.valueArgs, ); } -class MyGattCharacteristicArgs { +class MyGATTCharacteristicArgs { final int hashCodeArgs; final String uuidArgs; final List propertyNumbersArgs; - final List descriptorsArgs; + final List descriptorsArgs; - MyGattCharacteristicArgs( + MyGATTCharacteristicArgs( this.hashCodeArgs, this.uuidArgs, this.propertyNumbersArgs, @@ -110,39 +123,117 @@ class MyGattCharacteristicArgs { ); } -class MyGattServiceArgs { +class MyGATTServiceArgs { final int hashCodeArgs; final String uuidArgs; - final List characteristicsArgs; + final bool isPrimaryArgs; + final List includedServicesArgs; + final List characteristicsArgs; - MyGattServiceArgs( + MyGATTServiceArgs( this.hashCodeArgs, this.uuidArgs, + this.isPrimaryArgs, + this.includedServicesArgs, this.characteristicsArgs, ); } +class MyMutableGATTDescriptorArgs { + final int hashCodeArgs; + final String uuidArgs; + final Uint8List? valueArgs; + + MyMutableGATTDescriptorArgs( + this.hashCodeArgs, + this.uuidArgs, + this.valueArgs, + ); +} + +class MyMutableGATTCharacteristicArgs { + final int hashCodeArgs; + final String uuidArgs; + final List propertyNumbersArgs; + final List permissionNumbersArgs; + final Uint8List? valueArgs; + final List descriptorsArgs; + + MyMutableGATTCharacteristicArgs( + this.hashCodeArgs, + this.uuidArgs, + this.propertyNumbersArgs, + this.permissionNumbersArgs, + this.valueArgs, + this.descriptorsArgs, + ); +} + +class MyMutableGATTServiceArgs { + final int hashCodeArgs; + final String uuidArgs; + final bool isPrimaryArgs; + final List includedServicesArgs; + final List characteristicsArgs; + + MyMutableGATTServiceArgs( + this.hashCodeArgs, + this.uuidArgs, + this.isPrimaryArgs, + this.includedServicesArgs, + this.characteristicsArgs, + ); +} + +class MyATTRequestArgs { + final int hashCodeArgs; + final MyCentralArgs centralArgs; + final int characteristicHashCodeArgs; + final Uint8List? valueArgs; + final int offsetArgs; + + MyATTRequestArgs( + this.hashCodeArgs, + this.centralArgs, + this.characteristicHashCodeArgs, + this.valueArgs, + this.offsetArgs, + ); +} + @HostApi() -abstract class MyCentralManagerHostApi { - void setUp(); - void startDiscovery(); +abstract class MyCentralManagerHostAPI { + void initialize(); + MyBluetoothLowEnergyStateArgs getState(); + @async + void showAppSettings(); + void startDiscovery(List serviceUUIDsArgs); void stopDiscovery(); + List retrieveConnectedPeripherals(); @async void connect(String uuidArgs); @async void disconnect(String uuidArgs); - int getMaximumWriteValueLength(String uuidArgs, int typeNumberArgs); + int getMaximumWriteLength( + String uuidArgs, + MyGATTCharacteristicWriteTypeArgs typeArgs, + ); @async int readRSSI(String uuidArgs); @async - List discoverServices(String uuidArgs); + List discoverServices(String uuidArgs); @async - List discoverCharacteristics( + List discoverIncludedServices( String uuidArgs, int hashCodeArgs, ); @async - List discoverDescriptors( + List discoverCharacteristics( + String uuidArgs, + int hashCodeArgs, + ); + @async + List discoverDescriptors( String uuidArgs, int hashCodeArgs, ); @@ -153,7 +244,7 @@ abstract class MyCentralManagerHostApi { String uuidArgs, int hashCodeArgs, Uint8List valueArgs, - int typeNumberArgs, + MyGATTCharacteristicWriteTypeArgs typeArgs, ); @async void setCharacteristicNotifyState( @@ -168,35 +259,44 @@ abstract class MyCentralManagerHostApi { } @FlutterApi() -abstract class MyCentralManagerFlutterApi { - void onStateChanged(int stateNumberArgs); +abstract class MyCentralManagerFlutterAPI { + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); void onDiscovered( MyPeripheralArgs peripheralArgs, int rssiArgs, MyAdvertisementArgs advertisementArgs, ); - void onConnectionStateChanged(String uuidArgs, bool stateArgs); + void onConnectionStateChanged( + MyPeripheralArgs peripheralArgs, + MyConnectionStateArgs stateArgs, + ); void onCharacteristicNotified( - String uuidArgs, - int hashCodeArgs, + MyPeripheralArgs peripheralArgs, + MyGATTCharacteristicArgs characteristicArgs, Uint8List valueArgs, ); } @HostApi() -abstract class MyPeripheralManagerHostApi { - void setUp(); +abstract class MyPeripheralManagerHostAPI { + void initialize(); + MyBluetoothLowEnergyStateArgs getState(); @async - void addService(MyGattServiceArgs serviceArgs); + void showAppSettings(); + @async + void addService(MyMutableGATTServiceArgs serviceArgs); void removeService(int hashCodeArgs); - void clearServices(); + void removeAllServices(); @async void startAdvertising(MyAdvertisementArgs advertisementArgs); void stopAdvertising(); - int getMaximumUpdateValueLength(String uuidArgs); - void respond(int idArgs, int errorNumberArgs, Uint8List? valueArgs); - @async - void updateCharacteristic( + int getMaximumNotifyLength(String uuidArgs); + void respond( + int hashCodeArgs, + Uint8List? valueArgs, + MyATTErrorArgs errorArgs, + ); + bool updateValue( int hashCodeArgs, Uint8List valueArgs, List? uuidsArgs, @@ -204,21 +304,11 @@ abstract class MyPeripheralManagerHostApi { } @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, - ); +abstract class MyPeripheralManagerFlutterAPI { + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); + void didReceiveRead(MyATTRequestArgs requestArgs); + void didReceiveWrite(List requestsArgs); + void isReady(); void onCharacteristicNotifyStateChanged( MyCentralArgs centralArgs, int hashCodeArgs, diff --git a/bluetooth_low_energy_darwin/pubspec.yaml b/bluetooth_low_energy_darwin/pubspec.yaml index 9b4856d..20dbc6e 100644 --- a/bluetooth_low_energy_darwin/pubspec.yaml +++ b/bluetooth_low_energy_darwin/pubspec.yaml @@ -1,31 +1,42 @@ name: bluetooth_low_energy_darwin -description: iOS and macOS implementation of the bluetooth_low_energy plugin. -version: 5.0.5 +description: "iOS and macOS implementation of the bluetooth_low_energy plugin." +version: 6.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy +repository: https://github.com/yanshouwang/bluetooth_low_energy +issue_tracker: https://github.com/yanshouwang/bluetooth_low_energy/issues +topics: + - bluetooth + - bluetooth-low-energy + - ble +funding: + - https://paypal.me/yanshouwang5112 + - https://afdian.net/a/yanshouwang environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.0.0" + sdk: '>=3.0.0 <4.0.0' + # Flutter versions prior to 3.7 did not support the + # sharedDarwinSource option. + flutter: '>=3.7.0' dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^5.0.2 + bluetooth_low_energy_platform_interface: ^6.0.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.0 - pigeon: ^15.0.2 + flutter_lints: ^4.0.0 + pigeon: ^19.0.0 flutter: plugin: platforms: ios: - pluginClass: BluetoothLowEnergyDarwin + pluginClass: BluetoothLowEnergyDarwinPlugin sharedDarwinSource: true - dartPluginClass: BluetoothLowEnergyDarwin + dartPluginClass: BluetoothLowEnergyDarwinPlugin macos: - pluginClass: BluetoothLowEnergyDarwin + pluginClass: BluetoothLowEnergyDarwinPlugin sharedDarwinSource: true - dartPluginClass: BluetoothLowEnergyDarwin + dartPluginClass: BluetoothLowEnergyDarwinPlugin diff --git a/bluetooth_low_energy_darwin/test/bluetooth_low_energy_darwin_test.dart b/bluetooth_low_energy_darwin/test/bluetooth_low_energy_darwin_test.dart new file mode 100644 index 0000000..ab73b3a --- /dev/null +++ b/bluetooth_low_energy_darwin/test/bluetooth_low_energy_darwin_test.dart @@ -0,0 +1 @@ +void main() {} diff --git a/bluetooth_low_energy_linux/.gitignore b/bluetooth_low_energy_linux/.gitignore index 96486fd..ac5aa98 100644 --- a/bluetooth_low_energy_linux/.gitignore +++ b/bluetooth_low_energy_linux/.gitignore @@ -26,5 +26,4 @@ migrate_working_dir/ /pubspec.lock **/doc/api/ .dart_tool/ -.packages build/ diff --git a/bluetooth_low_energy_linux/.metadata b/bluetooth_low_energy_linux/.metadata index 3c75153..a76423b 100644 --- a/bluetooth_low_energy_linux/.metadata +++ b/bluetooth_low_energy_linux/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "efbf63d9c66b9f6ec30e9ad4611189aa80003d31" + revision: "5dcb86f68f239346676ceb1ed1ea385bd215fba1" channel: "stable" project_type: plugin @@ -13,11 +13,11 @@ project_type: plugin migration: platforms: - platform: root - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 - platform: linux - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 # User provided section diff --git a/bluetooth_low_energy_linux/CHANGELOG.md b/bluetooth_low_energy_linux/CHANGELOG.md index 217dc16..948eb37 100644 --- a/bluetooth_low_energy_linux/CHANGELOG.md +++ b/bluetooth_low_energy_linux/CHANGELOG.md @@ -1,3 +1,27 @@ +## 6.0.0 + +* Implement new APIs. +* Rewrite example with MVVM. +* Fix known issues. + +## 6.0.0-dev.3 + +* Rewrite example with MVVM. +* Fix known issues. + +## 6.0.0-dev.2 + +* Fix example errors. + +## 6.0.0-dev.1 + +* Move organization. +* Fix errors. + +## 6.0.0-dev.0 + +* Implement new APIs. + ## 5.0.2 * Change flutter minimum version to 3.0.0. diff --git a/bluetooth_low_energy_linux/LICENSE b/bluetooth_low_energy_linux/LICENSE index 752d28b..3977562 100644 --- a/bluetooth_low_energy_linux/LICENSE +++ b/bluetooth_low_energy_linux/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 yanshouwang +Copyright (c) 2024 hebei.dev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/bluetooth_low_energy_linux/example/.gitignore b/bluetooth_low_energy_linux/example/.gitignore index 24476c5..29a3a50 100644 --- a/bluetooth_low_energy_linux/example/.gitignore +++ b/bluetooth_low_energy_linux/example/.gitignore @@ -27,7 +27,6 @@ migrate_working_dir/ .dart_tool/ .flutter-plugins .flutter-plugins-dependencies -.packages .pub-cache/ .pub/ /build/ diff --git a/bluetooth_low_energy_linux/example/README.md b/bluetooth_low_energy_linux/example/README.md index 13063a6..b121225 100644 --- a/bluetooth_low_energy_linux/example/README.md +++ b/bluetooth_low_energy_linux/example/README.md @@ -1,6 +1,6 @@ -# bluetooth_low_energy_example +# bluetooth_low_energy_linux_example -Demonstrates how to use the bluetooth_low_energy plugin. +Demonstrates how to use the bluetooth_low_energy_linux plugin. ## Getting Started diff --git a/bluetooth_low_energy_linux/example/integration_test/plugin_integration_test.dart b/bluetooth_low_energy_linux/example/integration_test/plugin_integration_test.dart index c03e5a8..d53d2ae 100644 --- a/bluetooth_low_energy_linux/example/integration_test/plugin_integration_test.dart +++ b/bluetooth_low_energy_linux/example/integration_test/plugin_integration_test.dart @@ -6,17 +6,8 @@ // For more information about Flutter integration tests, please see // https://docs.flutter.dev/cookbook/testing/integration/introduction -// import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - // testWidgets('getPlatformVersion test', (WidgetTester tester) async { - // final BluetoothLowEnergy plugin = BluetoothLowEnergy(); - // final String? version = await plugin.getPlatformVersion(); - // // The version string depends on the host platform running the test, so - // // just assert that some non-empty string is returned. - // expect(version?.isNotEmpty, true); - // }); } diff --git a/bluetooth_low_energy_linux/example/lib/main.dart b/bluetooth_low_energy_linux/example/lib/main.dart index f5c3eb0..a588ab4 100644 --- a/bluetooth_low_energy_linux/example/lib/main.dart +++ b/bluetooth_low_energy_linux/example/lib/main.dart @@ -1,12 +1,10 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:developer'; -import 'dart:typed_data'; -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:convert/convert.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; +import 'package:logging/logging.dart'; + +import 'router_config.dart'; void main() { runZonedGuarded(onStartUp, onCrashed); @@ -14,9 +12,7 @@ void main() { void onStartUp() async { Logger.root.onRecord.listen(onLogRecord); - WidgetsFlutterBinding.ensureInitialized(); - await CentralManager.instance.setUp(); - // await peripheralManager.setUp(); + hierarchicalLoggingEnabled = true; runApp(const MyApp()); } @@ -37,795 +33,19 @@ void onLogRecord(LogRecord record) { ); } -class MyApp extends StatefulWidget { +class MyApp extends StatelessWidget { const MyApp({super.key}); - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData.light( - useMaterial3: true, - ).copyWith( + return MaterialApp.router( + routerConfig: routerConfig, + theme: ThemeData.light().copyWith( materialTapTargetSize: MaterialTapTargetSize.padded, ), - home: const HomeView(), - routes: { - 'peripheral': (context) { - final route = ModalRoute.of(context); - final eventArgs = route!.settings.arguments as DiscoveredEventArgs; - return PeripheralView( - eventArgs: eventArgs, - ); - }, - }, - ); - } -} - -class HomeView extends StatelessWidget { - const HomeView({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - body: buildBody(context), - ); - } - - Widget buildBody(BuildContext context) { - return const ScannerView(); - } -} - -class ScannerView extends StatefulWidget { - const ScannerView({super.key}); - - @override - State createState() => _ScannerViewState(); -} - -class _ScannerViewState extends State { - late final ValueNotifier state; - late final ValueNotifier discovering; - late final ValueNotifier> discoveredEventArgs; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription discoveredSubscription; - - @override - void initState() { - super.initState(); - state = ValueNotifier(BluetoothLowEnergyState.unknown); - discovering = ValueNotifier(false); - discoveredEventArgs = ValueNotifier([]); - stateChangedSubscription = CentralManager.instance.stateChanged.listen( - (eventArgs) { - state.value = eventArgs.state; - }, - ); - 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, - ); - if (i < 0) { - discoveredEventArgs.value = [...items, eventArgs]; - } else { - items[i] = eventArgs; - discoveredEventArgs.value = [...items]; - } - }, - ); - setUp(); - } - - void setUp() async { - state.value = await CentralManager.instance.getState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), - ); - } - - PreferredSizeWidget buildAppBar(BuildContext context) { - return AppBar( - title: const Text('Scanner'), - actions: [ - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: discovering, - builder: (context, discovering, child) { - return TextButton( - onPressed: state == BluetoothLowEnergyState.poweredOn - ? () async { - if (discovering) { - await stopDiscovery(); - } else { - await startDiscovery(); - } - } - : null, - child: Text( - discovering ? 'END' : 'BEGIN', - ), - ); - }, - ); - }, - ), - ], - ); - } - - Future startDiscovery() async { - discoveredEventArgs.value = []; - await CentralManager.instance.startDiscovery(); - discovering.value = true; - } - - Future stopDiscovery() async { - await CentralManager.instance.stopDiscovery(); - discovering.value = false; - } - - Widget buildBody(BuildContext context) { - return ValueListenableBuilder( - valueListenable: discoveredEventArgs, - builder: (context, discoveredEventArgs, child) { - // final items = discoveredEventArgs; - final items = discoveredEventArgs - .where((eventArgs) => eventArgs.advertisement.name != null) - .toList(); - return ListView.separated( - itemBuilder: (context, i) { - final theme = Theme.of(context); - final item = items[i]; - final uuid = item.peripheral.uuid; - final rssi = item.rssi; - final advertisement = item.advertisement; - final name = advertisement.name; - return ListTile( - onTap: () async { - final discovering = this.discovering.value; - if (discovering) { - await stopDiscovery(); - } - if (!mounted) { - throw UnimplementedError(); - } - await Navigator.of(context).pushNamed( - 'peripheral', - arguments: item, - ); - if (discovering) { - await startDiscovery(); - } - }, - onLongPress: () async { - await showModalBottomSheet( - context: context, - builder: (context) { - return BottomSheet( - onClosing: () {}, - clipBehavior: Clip.antiAlias, - builder: (context) { - final manufacturerSpecificData = - advertisement.manufacturerSpecificData; - return ListView.separated( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 40.0, - ), - itemBuilder: (context, i) { - const idWidth = 80.0; - if (i == 0) { - return const Row( - children: [ - SizedBox( - width: idWidth, - child: Text('ID'), - ), - Expanded( - child: Text('DATA'), - ), - ], - ); - } else { - final id = - '0x${manufacturerSpecificData!.id.toRadixString(16).padLeft(4, '0')}'; - final value = - hex.encode(manufacturerSpecificData.data); - return Row( - children: [ - SizedBox( - width: idWidth, - child: Text(id), - ), - Expanded( - child: Text(value), - ), - ], - ); - } - }, - separatorBuilder: (context, i) { - return const Divider(); - }, - itemCount: manufacturerSpecificData == null ? 1 : 2, - ); - }, - ); - }, - ); - }, - title: Text(name ?? 'N/A'), - subtitle: Text( - '$uuid', - style: theme.textTheme.bodySmall, - softWrap: false, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - RssiWidget(rssi), - Text('$rssi'), - ], - ), - ); - }, - separatorBuilder: (context, i) { - return const Divider( - height: 0.0, - ); - }, - itemCount: items.length, - ); - }, - ); - } - - @override - void dispose() { - super.dispose(); - stateChangedSubscription.cancel(); - discoveredSubscription.cancel(); - state.dispose(); - discovering.dispose(); - discoveredEventArgs.dispose(); - } -} - -class PeripheralView extends StatefulWidget { - final DiscoveredEventArgs eventArgs; - - const PeripheralView({ - super.key, - required this.eventArgs, - }); - - @override - State createState() => _PeripheralViewState(); -} - -class _PeripheralViewState extends 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> logs; - late final TextEditingController writeController; - late final StreamSubscription connectionStateChangedSubscription; - late final StreamSubscription characteristicNotifiedSubscription; - - @override - void initState() { - super.initState(); - eventArgs = widget.eventArgs; - connectionState = ValueNotifier(false); - services = ValueNotifier([]); - characteristics = ValueNotifier([]); - service = ValueNotifier(null); - characteristic = ValueNotifier(null); - writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); - logs = ValueNotifier([]); - writeController = TextEditingController(); - connectionStateChangedSubscription = - CentralManager.instance.connectionStateChanged.listen( - (eventArgs) { - if (eventArgs.peripheral != this.eventArgs.peripheral) { - return; - } - final connectionState = eventArgs.connectionState; - this.connectionState.value = connectionState; - if (!connectionState) { - services.value = []; - characteristics.value = []; - service.value = null; - characteristic.value = null; - logs.value = []; - } - }, - ); - characteristicNotifiedSubscription = - CentralManager.instance.characteristicNotified.listen( - (eventArgs) { - // final characteristic = this.characteristic.value; - // if (eventArgs.characteristic != characteristic) { - // return; - // } - const type = LogType.notify; - final log = Log(type, eventArgs.value); - logs.value = [ - ...logs.value, - log, - ]; - }, - ); - } - - @override - Widget build(BuildContext context) { - return PopScope( - onPopInvoked: (didPop) async { - if (connectionState.value) { - final peripheral = eventArgs.peripheral; - await CentralManager.instance.disconnect(peripheral); - } - }, - child: Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), + darkTheme: ThemeData.dark().copyWith( + materialTapTargetSize: MaterialTapTargetSize.padded, ), ); } - - PreferredSizeWidget buildAppBar(BuildContext context) { - final title = eventArgs.advertisement.name ?? ''; - return AppBar( - title: Text(title), - actions: [ - ValueListenableBuilder( - valueListenable: connectionState, - builder: (context, state, child) { - return TextButton( - onPressed: () async { - final peripheral = eventArgs.peripheral; - if (state) { - await CentralManager.instance.disconnect(peripheral); - } else { - await CentralManager.instance.connect(peripheral); - services.value = - await CentralManager.instance.discoverGATT(peripheral); - } - }, - child: Text(state ? 'DISCONNECT' : 'CONNECT'), - ); - }, - ), - ], - ); - } - - Widget buildBody(BuildContext context) { - final theme = Theme.of(context); - return Container( - margin: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ValueListenableBuilder( - valueListenable: services, - builder: (context, services, child) { - final items = services.map((service) { - return DropdownMenuItem( - value: service, - child: Text( - '${service.uuid}', - style: theme.textTheme.bodyMedium, - ), - ); - }).toList(); - return ValueListenableBuilder( - valueListenable: service, - builder: (context, service, child) { - return DropdownButton( - isExpanded: true, - items: items, - hint: const Text('CHOOSE A SERVICE'), - value: service, - onChanged: (service) async { - this.service.value = service; - characteristic.value = null; - if (service == null) { - return; - } - characteristics.value = service.characteristics; - }, - ); - }, - ); - }, - ), - ValueListenableBuilder( - valueListenable: characteristics, - builder: (context, characteristics, child) { - final items = characteristics.map((characteristic) { - return DropdownMenuItem( - value: characteristic, - child: Text( - '${characteristic.uuid}', - style: theme.textTheme.bodyMedium, - ), - ); - }).toList(); - return ValueListenableBuilder( - valueListenable: characteristic, - builder: (context, characteristic, child) { - return DropdownButton( - isExpanded: true, - items: items, - 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; - } - }, - ); - }, - ); - }, - ), - Expanded( - child: 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 = 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, - ); - }, - ), - ), - 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: [ - ElevatedButton( - onPressed: characteristic != null && canNotify - ? () async { - await CentralManager.instance - .setCharacteristicNotifyState( - characteristic, - state: true, - ); - } - : null, - child: const Text('NOTIFY'), - ), - const SizedBox(width: 8.0), - ElevatedButton( - onPressed: characteristic != null && canRead - ? () async { - final value = await CentralManager.instance - .readCharacteristic(characteristic); - const type = LogType.read; - final log = Log(type, value); - logs.value = [...logs.value, log]; - } - : null, - child: const Text('READ'), - ) - ], - ), - ), - 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), - ], - ); - }, - ), - ], - ), - ); - } - - @override - void dispose() { - super.dispose(); - connectionStateChangedSubscription.cancel(); - characteristicNotifiedSubscription.cancel(); - connectionState.dispose(); - services.dispose(); - characteristics.dispose(); - service.dispose(); - characteristic.dispose(); - writeType.dispose(); - logs.dispose(); - writeController.dispose(); - } -} - -class Log { - final DateTime time; - final LogType type; - final Uint8List value; - final String? detail; - - Log( - this.type, - this.value, [ - this.detail, - ]) : time = DateTime.now(); - - @override - String toString() { - final type = this.type.toString().split('.').last; - final formatter = DateFormat.Hms(); - final time = formatter.format(this.time); - final message = hex.encode(value); - if (detail == null) { - return '[$type]$time: $message'; - } else { - return '[$type]$time: $message /* $detail */'; - } - } -} - -enum LogType { - read, - write, - notify, - error, -} - -class RssiWidget extends StatelessWidget { - final int rssi; - - const RssiWidget( - this.rssi, { - super.key, - }); - - @override - Widget build(BuildContext context) { - final IconData icon; - if (rssi > -70) { - icon = Icons.wifi_rounded; - } else if (rssi > -100) { - icon = Icons.wifi_2_bar_rounded; - } else { - icon = Icons.wifi_1_bar_rounded; - } - return Icon(icon); - } } diff --git a/bluetooth_low_energy_linux/example/lib/models.dart b/bluetooth_low_energy_linux/example/lib/models.dart new file mode 100644 index 0000000..627f4d2 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/models.dart @@ -0,0 +1 @@ +export 'models/log.dart'; diff --git a/bluetooth_low_energy_linux/example/lib/models/log.dart b/bluetooth_low_energy_linux/example/lib/models/log.dart new file mode 100644 index 0000000..2bcfbf7 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/models/log.dart @@ -0,0 +1,10 @@ +class Log { + final DateTime time; + final String type; + final String message; + + Log({ + required this.type, + required this.message, + }) : time = DateTime.now(); +} diff --git a/bluetooth_low_energy_linux/example/lib/router_config.dart b/bluetooth_low_energy_linux/example/lib/router_config.dart new file mode 100644 index 0000000..1d344c7 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/router_config.dart @@ -0,0 +1,85 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:collection/collection.dart'; +import 'package:go_router/go_router.dart'; + +import 'view_models.dart'; +import 'views.dart'; + +final routerConfig = GoRouter( + redirect: (context, state) { + if (state.matchedLocation == '/') { + return '/central'; + } + return null; + }, + routes: [ + StatefulShellRoute( + // builder: (context, state, navigationShell) { + // return HomeView(navigationShell: navigationShell); + // }, + builder: (context, state, navigationShell) => navigationShell, + navigatorContainerBuilder: (context, navigationShell, children) { + final navigators = children.mapIndexed( + (index, element) { + if (index == 0) { + return ViewModelBinding( + viewBuilder: (context) => element, + viewModelBuilder: (context) => CentralManagerViewModel(), + ); + } else { + return element; + } + }, + ).toList(); + return HomeView( + navigationShell: navigationShell, + navigators: navigators, + ); + }, + branches: [ + StatefulShellBranch( + routes: [ + GoRoute( + path: '/central', + builder: (context, state) { + return const CentralManagerView(); + }, + routes: [ + GoRoute( + path: ':uuid', + builder: (context, state) { + final uuidValue = state.pathParameters['uuid']!; + final uuid = UUID.fromString(uuidValue); + final viewModel = + ViewModel.of(context); + final eventArgs = viewModel.discoveries.firstWhere( + (discovery) => discovery.peripheral.uuid == uuid); + return ViewModelBinding( + viewBuilder: (context) => PeripheralView(), + viewModelBuilder: (context) => + PeripheralViewModel(eventArgs), + ); + }, + ), + ], + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/peripheral', + builder: (context, state) { + return ViewModelBinding( + viewBuilder: (context) => const PeripheralManagerView(), + viewModelBuilder: (context) => PeripheralManagerViewModel(), + ); + }, + ), + ], + ), + ], + ), + ], +); diff --git a/bluetooth_low_energy_linux/example/lib/view_models.dart b/bluetooth_low_energy_linux/example/lib/view_models.dart new file mode 100644 index 0000000..6c0c1fc --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/view_models.dart @@ -0,0 +1,6 @@ +export 'view_models/central_manager_view_model.dart'; +export 'view_models/peripheral_view_model.dart'; +export 'view_models/service_view_model.dart'; +export 'view_models/characteristic_view_model.dart'; +export 'view_models/descriptor_view_model.dart'; +export 'view_models/peripheral_manager_view_model.dart'; diff --git a/bluetooth_low_energy_linux/example/lib/view_models/central_manager_view_model.dart b/bluetooth_low_energy_linux/example/lib/view_models/central_manager_view_model.dart new file mode 100644 index 0000000..4d64938 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/view_models/central_manager_view_model.dart @@ -0,0 +1,67 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:logging/logging.dart'; + +class CentralManagerViewModel extends ViewModel { + final CentralManager _manager; + final List _discoveries; + bool _discovering; + + late final StreamSubscription _stateChangedSubscription; + late final StreamSubscription _discoveredSubscription; + + CentralManagerViewModel() + : _manager = CentralManager()..logLevel = Level.INFO, + _discoveries = [], + _discovering = false { + _stateChangedSubscription = _manager.stateChanged.listen((eventArgs) { + notifyListeners(); + }); + _discoveredSubscription = _manager.discovered.listen((eventArgs) { + final peripheral = eventArgs.peripheral; + final index = _discoveries.indexWhere((i) => i.peripheral == peripheral); + if (index < 0) { + _discoveries.add(eventArgs); + } else { + _discoveries[index] = eventArgs; + } + notifyListeners(); + }); + } + + BluetoothLowEnergyState get state => _manager.state; + bool get discovering => _discovering; + List get discoveries => _discoveries; + + Future startDiscovery({ + List? serviceUUIDs, + }) async { + if (_discovering) { + return; + } + _discoveries.clear(); + await _manager.startDiscovery( + serviceUUIDs: serviceUUIDs, + ); + _discovering = true; + notifyListeners(); + } + + Future stopDiscovery() async { + if (!_discovering) { + return; + } + await _manager.stopDiscovery(); + _discovering = false; + notifyListeners(); + } + + @override + void dispose() { + _stateChangedSubscription.cancel(); + _discoveredSubscription.cancel(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/view_models/characteristic_view_model.dart b/bluetooth_low_energy_linux/example/lib/view_models/characteristic_view_model.dart new file mode 100644 index 0000000..e1a9fc8 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/view_models/characteristic_view_model.dart @@ -0,0 +1,145 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_linux_example/models.dart'; +import 'package:clover/clover.dart'; + +import 'descriptor_view_model.dart'; + +class CharacteristicViewModel extends ViewModel { + final CentralManager _manager; + final Peripheral _peripheral; + final GATTCharacteristic _characteristic; + final List _descriptorViewModels; + final List _logs; + + GATTCharacteristicWriteType _writeType; + bool _notifyState; + + late final StreamSubscription _characteristicNotifiedSubscription; + + CharacteristicViewModel({ + required CentralManager manager, + required Peripheral peripheral, + required GATTCharacteristic characteristic, + }) : _manager = manager, + _peripheral = peripheral, + _characteristic = characteristic, + _descriptorViewModels = characteristic.descriptors + .map((descriptor) => DescriptorViewModel(descriptor)) + .toList(), + _logs = [], + _writeType = GATTCharacteristicWriteType.withResponse, + _notifyState = false { + if (!canWrite && canWriteWithoutResponse) { + _writeType = GATTCharacteristicWriteType.withoutResponse; + } + _characteristicNotifiedSubscription = + _manager.characteristicNotified.listen((eventArgs) { + if (eventArgs.characteristic != _characteristic) { + return; + } + final log = Log( + type: 'Notified', + message: '[${eventArgs.value.length}] ${eventArgs.value}', + ); + _logs.add(log); + notifyListeners(); + }); + } + + UUID get uuid => _characteristic.uuid; + bool get canRead => + _characteristic.properties.contains(GATTCharacteristicProperty.read); + bool get canWrite => + _characteristic.properties.contains(GATTCharacteristicProperty.write); + bool get canWriteWithoutResponse => _characteristic.properties + .contains(GATTCharacteristicProperty.writeWithoutResponse); + bool get canNotify => + _characteristic.properties.contains(GATTCharacteristicProperty.notify) || + _characteristic.properties.contains(GATTCharacteristicProperty.indicate); + List get descriptorViewModels => _descriptorViewModels; + List get logs => _logs; + GATTCharacteristicWriteType get writeType => _writeType; + bool get notifyState => _notifyState; + + Future read() async { + final value = await _manager.readCharacteristic( + _peripheral, + _characteristic, + ); + final log = Log( + type: 'Read', + message: '[${value.length}] $value', + ); + _logs.add(log); + notifyListeners(); + } + + void setWriteType(GATTCharacteristicWriteType type) { + if (type == GATTCharacteristicWriteType.withResponse && !canWrite) { + throw ArgumentError.value(type); + } + if (type == GATTCharacteristicWriteType.withoutResponse && + !canWriteWithoutResponse) { + throw ArgumentError.value(type); + } + _writeType = type; + notifyListeners(); + } + + Future write(Uint8List value) async { + // Fragments the value by maximumWriteLength. + final fragmentSize = await _manager.getMaximumWriteLength( + _peripheral, + type: writeType, + ); + var start = 0; + while (start < value.length) { + final end = start + fragmentSize; + final fragmentedValue = + end < value.length ? value.sublist(start, end) : value.sublist(start); + final type = writeType; + await _manager.writeCharacteristic( + _peripheral, + _characteristic, + value: fragmentedValue, + type: type, + ); + final log = Log( + type: type == GATTCharacteristicWriteType.withResponse + ? 'Write' + : 'Write without response', + message: '[${value.length}] $value', + ); + _logs.add(log); + notifyListeners(); + start = end; + } + } + + Future setNotifyState(bool state) async { + await _manager.setCharacteristicNotifyState( + _peripheral, + _characteristic, + state: state, + ); + _notifyState = state; + notifyListeners(); + } + + void clearLogs() { + _logs.clear(); + notifyListeners(); + } + + @override + void dispose() { + _characteristicNotifiedSubscription.cancel(); + for (var descriptorViewModel in descriptorViewModels) { + descriptorViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/view_models/descriptor_view_model.dart b/bluetooth_low_energy_linux/example/lib/view_models/descriptor_view_model.dart new file mode 100644 index 0000000..a03bce4 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/view_models/descriptor_view_model.dart @@ -0,0 +1,10 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; + +class DescriptorViewModel extends ViewModel { + final GATTDescriptor _descriptor; + + DescriptorViewModel(this._descriptor); + + UUID get uuid => _descriptor.uuid; +} diff --git a/bluetooth_low_energy_linux/example/lib/view_models/peripheral_manager_view_model.dart b/bluetooth_low_energy_linux/example/lib/view_models/peripheral_manager_view_model.dart new file mode 100644 index 0000000..571105c --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/view_models/peripheral_manager_view_model.dart @@ -0,0 +1,162 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_linux_example/models.dart'; +import 'package:clover/clover.dart'; +import 'package:logging/logging.dart'; + +class PeripheralManagerViewModel extends ViewModel { + final PeripheralManager _manager; + final List _logs; + bool _advertising; + + late final StreamSubscription _stateChangedSubscription; + late final StreamSubscription _characteristicReadRequestedSubscription; + late final StreamSubscription _characteristicWriteRequestedSubscription; + late final StreamSubscription _characteristicNotifyStateChangedSubscription; + + PeripheralManagerViewModel() + : _manager = PeripheralManager()..logLevel = Level.INFO, + _logs = [], + _advertising = false { + _stateChangedSubscription = _manager.stateChanged.listen((eventArgs) { + notifyListeners(); + }); + _characteristicReadRequestedSubscription = + _manager.characteristicReadRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final log = Log( + type: 'Characteristic read requested', + message: '${central.uuid}, ${characteristic.uuid}, $offset', + ); + _logs.add(log); + notifyListeners(); + final elements = List.generate(100, (i) => i % 256); + final value = Uint8List.fromList(elements); + final trimmedValue = value.sublist(offset); + await _manager.respondReadRequestWithValue( + request, + value: trimmedValue, + ); + }); + _characteristicWriteRequestedSubscription = + _manager.characteristicWriteRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final value = request.value; + final log = Log( + type: 'Characteristic write requested', + message: + '[${value.length}] ${central.uuid}, ${characteristic.uuid}, $offset, $value', + ); + _logs.add(log); + notifyListeners(); + await _manager.respondWriteRequest(request); + }); + _characteristicNotifyStateChangedSubscription = + _manager.characteristicNotifyStateChanged.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final state = eventArgs.state; + final log = Log( + type: 'Characteristic notify state changed', + message: '${central.uuid}, ${characteristic.uuid}, $state', + ); + _logs.add(log); + notifyListeners(); + // Write someting to the central when notify started. + if (state) { + final maximumNotifyLength = + await _manager.getMaximumNotifyLength(central); + final elements = List.generate(maximumNotifyLength, (i) => i % 256); + final value = Uint8List.fromList(elements); + await _manager.notifyCharacteristic( + central, + characteristic, + value: value, + ); + } + }); + } + + BluetoothLowEnergyState get state => _manager.state; + bool get advertising => _advertising; + List get logs => _logs; + + Future startAdvertising() async { + if (_advertising) { + return; + } + await _manager.removeAllServices(); + final elements = List.generate(100, (i) => i % 256); + final value = Uint8List.fromList(elements); + final service = GATTService( + uuid: UUID.short(100), + isPrimary: true, + includedServices: [], + characteristics: [ + GATTCharacteristic.immutable( + uuid: UUID.short(200), + value: value, + descriptors: [], + ), + GATTCharacteristic.mutable( + uuid: UUID.short(201), + properties: [ + GATTCharacteristicProperty.read, + GATTCharacteristicProperty.write, + GATTCharacteristicProperty.writeWithoutResponse, + GATTCharacteristicProperty.notify, + GATTCharacteristicProperty.indicate, + ], + permissions: [ + GATTCharacteristicPermission.read, + GATTCharacteristicPermission.write, + ], + descriptors: [], + ), + ], + ); + await _manager.addService(service); + final advertisement = Advertisement( + manufacturerSpecificData: [ + ManufacturerSpecificData( + id: 0x2e19, + data: Uint8List.fromList([0x01, 0x02, 0x03]), + ) + ], + ); + await _manager.startAdvertising(advertisement); + _advertising = true; + notifyListeners(); + } + + Future stopAdvertising() async { + if (!_advertising) { + return; + } + await _manager.stopAdvertising(); + _advertising = false; + notifyListeners(); + } + + void clearLogs() { + _logs.clear(); + notifyListeners(); + } + + @override + void dispose() { + _stateChangedSubscription.cancel(); + _characteristicReadRequestedSubscription.cancel(); + _characteristicWriteRequestedSubscription.cancel(); + _characteristicNotifyStateChangedSubscription.cancel(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/view_models/peripheral_view_model.dart b/bluetooth_low_energy_linux/example/lib/view_models/peripheral_view_model.dart new file mode 100644 index 0000000..7fab7e0 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/view_models/peripheral_view_model.dart @@ -0,0 +1,75 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:hybrid_logging/hybrid_logging.dart'; + +import 'service_view_model.dart'; + +class PeripheralViewModel extends ViewModel with TypeLogger { + final CentralManager _manager; + final Peripheral _peripheral; + final String? _name; + bool _connected; + List _serviceViewModels; + + late final StreamSubscription _connectionStateChangedSubscription; + + PeripheralViewModel(DiscoveredEventArgs eventArgs) + : _manager = CentralManager(), + _peripheral = eventArgs.peripheral, + _name = eventArgs.advertisement.name, + _connected = false, + _serviceViewModels = [] { + _connectionStateChangedSubscription = + _manager.connectionStateChanged.listen((eventArgs) { + if (eventArgs.peripheral != _peripheral) { + return; + } + if (eventArgs.state == ConnectionState.connected) { + _connected = true; + } else { + _connected = false; + _serviceViewModels = []; + } + notifyListeners(); + }); + } + + UUID get uuid => _peripheral.uuid; + String? get name => _name; + bool get connected => _connected; + List get serviceViewModels => _serviceViewModels; + + Future connect() async { + await _manager.connect(_peripheral); + } + + Future disconnect() async { + await _manager.disconnect(_peripheral); + } + + Future discoverGATT() async { + final services = await _manager.discoverGATT(_peripheral); + _serviceViewModels = services + .map((service) => ServiceViewModel( + manager: _manager, + peripheral: _peripheral, + service: service, + )) + .toList(); + notifyListeners(); + } + + @override + void dispose() { + if (connected) { + disconnect(); + } + _connectionStateChangedSubscription.cancel(); + for (var serviceViewModel in serviceViewModels) { + serviceViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/view_models/service_view_model.dart b/bluetooth_low_energy_linux/example/lib/view_models/service_view_model.dart new file mode 100644 index 0000000..adc7d28 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/view_models/service_view_model.dart @@ -0,0 +1,40 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; + +import 'characteristic_view_model.dart'; + +class ServiceViewModel extends ViewModel { + final GATTService _service; + + final List _includedServiceViewModels; + final List _characteristicViewModels; + + ServiceViewModel({ + required CentralManager manager, + required Peripheral peripheral, + required GATTService service, + }) : _service = service, + _includedServiceViewModels = [], + _characteristicViewModels = service.characteristics + .map((characteristic) => CharacteristicViewModel( + manager: manager, + peripheral: peripheral, + characteristic: characteristic, + )) + .toList(); + + UUID get uuid => _service.uuid; + bool get isPrimary => _service.isPrimary; + List get includedServiceViewModels => + _includedServiceViewModels; + List get characteristicViewModels => + _characteristicViewModels; + + @override + void dispose() { + for (var characteristicViewModel in characteristicViewModels) { + characteristicViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/views.dart b/bluetooth_low_energy_linux/example/lib/views.dart new file mode 100644 index 0000000..b0727cb --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/views.dart @@ -0,0 +1,4 @@ +export 'views/home_view.dart'; +export 'views/central_manager_view.dart'; +export 'views/peripheral_manager_view.dart'; +export 'views/peripheral_view.dart'; diff --git a/bluetooth_low_energy_linux/example/lib/views/advertisement_view.dart b/bluetooth_low_energy_linux/example/lib/views/advertisement_view.dart new file mode 100644 index 0000000..2082064 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/views/advertisement_view.dart @@ -0,0 +1,69 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:convert/convert.dart'; +import 'package:flutter/material.dart'; + +class AdvertisementView extends StatelessWidget { + final Advertisement advertisement; + + const AdvertisementView({ + super.key, + required this.advertisement, + }); + + @override + Widget build(BuildContext context) { + final manufacturerSpecificData = advertisement.manufacturerSpecificData; + return LayoutBuilder( + builder: (context, constraints) { + const idWidth = 100.0; + final valueWidth = constraints.maxWidth - idWidth - 16.0 * 2.0; + return DataTable( + columnSpacing: 0.0, + horizontalMargin: 16.0, + columns: [ + DataColumn( + label: Container( + width: idWidth, + alignment: Alignment.center, + child: const Text('Id'), + ), + ), + DataColumn( + label: Container( + width: valueWidth, + alignment: Alignment.center, + child: const Text('Value'), + ), + ), + ], + rows: manufacturerSpecificData.map((item) { + final id = '0x${item.id.toRadixString(16).padLeft(4, '0')}'; + final value = hex.encode(item.data); + return DataRow( + cells: [ + DataCell( + Container( + width: idWidth, + alignment: Alignment.center, + child: Text(id), + ), + ), + DataCell( + Container( + width: valueWidth, + alignment: Alignment.center, + child: Text( + value, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ); + }).toList(), + ); + }, + ); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/views/central_manager_view.dart b/bluetooth_low_energy_linux/example/lib/views/central_manager_view.dart new file mode 100644 index 0000000..3adcbbf --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/views/central_manager_view.dart @@ -0,0 +1,117 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_linux_example/view_models.dart'; +import 'package:bluetooth_low_energy_linux_example/widgets.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import 'advertisement_view.dart'; + +class CentralManagerView extends StatelessWidget { + const CentralManagerView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final discovering = viewModel.discovering; + return Scaffold( + appBar: AppBar( + title: const Text('Central Manager'), + actions: [ + TextButton( + onPressed: state == BluetoothLowEnergyState.poweredOn + ? () async { + if (discovering) { + await viewModel.stopDiscovery(); + } else { + await viewModel.startDiscovery(); + } + } + : null, + child: Text(discovering ? 'END' : 'BEGIN'), + ), + ], + ), + body: buildBody(context), + ); + } + + Widget buildBody(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + if (state == BluetoothLowEnergyState.poweredOn) { + final discoveries = viewModel.discoveries; + return ListView.separated( + itemBuilder: (context, index) { + final theme = Theme.of(context); + final discovery = discoveries[index]; + final uuid = discovery.peripheral.uuid; + final name = discovery.advertisement.name; + final rssi = discovery.rssi; + return ListTile( + onTap: () { + onTapDissovery(context, discovery); + }, + onLongPress: () { + onLongPressDiscovery(context, discovery); + }, + title: Text(name ?? ''), + subtitle: Text( + '$uuid', + style: theme.textTheme.bodySmall, + softWrap: false, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RSSIIndicator(rssi), + Text('$rssi'), + ], + ), + ); + }, + separatorBuilder: (context, i) { + return const Divider( + height: 0.0, + ); + }, + itemCount: discoveries.length, + ); + } else { + return Center( + child: Text( + '$state', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + } + + void onTapDissovery( + BuildContext context, DiscoveredEventArgs discovery) async { + final viewModel = ViewModel.of(context); + if (viewModel.discovering) { + await viewModel.stopDiscovery(); + if (!context.mounted) { + return; + } + } + final uuid = discovery.peripheral.uuid; + context.go('/central/$uuid'); + } + + void onLongPressDiscovery( + BuildContext context, DiscoveredEventArgs discovery) async { + await showModalBottomSheet( + context: context, + builder: (context) { + return AdvertisementView( + advertisement: discovery.advertisement, + ); + }, + ); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/views/characteristic_tree_node_view.dart b/bluetooth_low_energy_linux/example/lib/views/characteristic_tree_node_view.dart new file mode 100644 index 0000000..e8c6644 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/views/characteristic_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_linux_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class CharacteristicTreeNodeView extends StatelessWidget { + const CharacteristicTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/views/characteristic_view.dart b/bluetooth_low_energy_linux/example/lib/views/characteristic_view.dart new file mode 100644 index 0000000..4962e02 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/views/characteristic_view.dart @@ -0,0 +1,178 @@ +import 'dart:convert'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_linux_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +import 'log_view.dart'; + +class CharacteristicView extends StatefulWidget { + const CharacteristicView({super.key}); + + @override + State createState() => _CharacteristicViewState(); +} + +class _CharacteristicViewState extends State { + late final TextEditingController _textController; + + @override + void initState() { + super.initState(); + _textController = TextEditingController(); + } + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final logs = viewModel.logs; + return Column( + children: [ + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + return OverflowBox( + alignment: Alignment.topCenter, + maxHeight: constraints.maxHeight + 1.0, + child: Row( + children: [ + Expanded( + child: InputDecorator( + decoration: InputDecoration( + contentPadding: const EdgeInsets.all(12.0), + border: OutlineInputBorder( + borderRadius: BorderRadius.vertical( + top: const Radius.circular(12.0), + bottom: viewModel.canWrite || + viewModel.canWriteWithoutResponse + ? Radius.zero + : const Radius.circular(12.0), + ), + ), + ), + child: ListView.builder( + itemBuilder: (context, i) { + final log = logs[i]; + return LogView( + log: log, + ); + }, + itemCount: logs.length, + ), + ), + ), + Column( + children: [ + Visibility( + visible: viewModel.canNotify, + child: viewModel.notifyState + ? IconButton.filled( + onPressed: () async { + await viewModel.setNotifyState(false); + }, + icon: + const Icon(Symbols.notifications_active), + ) + : IconButton.filledTonal( + onPressed: () async { + await viewModel.setNotifyState(true); + }, + icon: const Icon(Symbols.notifications_off), + ), + ), + Visibility( + visible: viewModel.canRead, + child: IconButton.filled( + onPressed: () async { + await viewModel.read(); + }, + icon: const Icon(Symbols.arrow_downward), + ), + ), + Visibility( + visible: viewModel.canWrite || + viewModel.canWriteWithoutResponse, + child: viewModel.writeType == + GATTCharacteristicWriteType.withResponse + ? IconButton.filled( + onPressed: viewModel.canWriteWithoutResponse + ? () { + viewModel.setWriteType( + GATTCharacteristicWriteType + .withoutResponse); + } + : null, + icon: const Icon(Symbols.swap_vert), + ) + : IconButton.filledTonal( + onPressed: viewModel.canWrite + ? () { + viewModel.setWriteType( + GATTCharacteristicWriteType + .withResponse); + } + : null, + icon: const Icon(Symbols.arrow_upward), + ), + ), + IconButton.filled( + onPressed: () => viewModel.clearLogs(), + icon: const Icon(Symbols.delete), + ), + ], + ), + ], + ), + ); + }, + ), + ), + Visibility( + visible: viewModel.canWrite || viewModel.canWriteWithoutResponse, + child: Row( + children: [ + Expanded( + child: TextField( + controller: _textController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.symmetric( + horizontal: 12.0, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12.0), + ), + ), + ), + ), + ), + ValueListenableBuilder( + valueListenable: _textController, + builder: (context, tev, child) { + final text = tev.text; + return IconButton.filled( + onPressed: text.isEmpty + ? null + : () async { + final value = utf8.encode(text); + await viewModel.write(value); + }, + icon: const Icon(Symbols.pets), + ); + }, + ), + ], + ), + ), + ], + ); + } + + @override + void dispose() { + _textController.dispose(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/views/descriptor_tree_node_view.dart b/bluetooth_low_energy_linux/example/lib/views/descriptor_tree_node_view.dart new file mode 100644 index 0000000..a46260d --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/views/descriptor_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_linux_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class DescriptorTreeNodeView extends StatelessWidget { + const DescriptorTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.tertiary, + ), + ); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/views/home_view.dart b/bluetooth_low_energy_linux/example/lib/views/home_view.dart new file mode 100644 index 0000000..9b7d146 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/views/home_view.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class HomeView extends StatefulWidget { + final StatefulNavigationShell navigationShell; + final List navigators; + + const HomeView({ + super.key, + required this.navigationShell, + required this.navigators, + }); + + @override + State createState() => _HomeViewState(); +} + +class _HomeViewState extends State { + late final PageController _controller; + + @override + void initState() { + super.initState(); + _controller = PageController( + initialPage: widget.navigationShell.currentIndex, + ); + } + + @override + Widget build(BuildContext context) { + final navigationShell = widget.navigationShell; + final navigators = widget.navigators; + return Scaffold( + // body: navigationShell, + body: PageView.builder( + controller: _controller, + onPageChanged: (index) { + // Ignore tap events. + if (index == navigationShell.currentIndex) { + return; + } + navigationShell.goBranch( + index, + initialLocation: false, + ); + }, + itemBuilder: (context, index) { + return navigators[index]; + }, + itemCount: navigators.length, + ), + bottomNavigationBar: BottomNavigationBar( + onTap: (index) { + navigationShell.goBranch( + index, + initialLocation: index == navigationShell.currentIndex, + ); + }, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.radar), + label: 'Central', + ), + BottomNavigationBarItem( + icon: Icon(Icons.sensors), + label: 'Peripheral', + ), + ], + currentIndex: widget.navigationShell.currentIndex, + ), + ); + } + + @override + void didUpdateWidget(covariant HomeView oldWidget) { + super.didUpdateWidget(oldWidget); + final navigationShell = widget.navigationShell; + final page = _controller.page ?? _controller.initialPage; + final index = page.round(); + // Ignore swipe events. + if (index == navigationShell.currentIndex) { + return; + } + _controller.animateToPage( + navigationShell.currentIndex, + duration: const Duration(milliseconds: 300), + curve: Curves.linear, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/views/log_view.dart b/bluetooth_low_energy_linux/example/lib/views/log_view.dart new file mode 100644 index 0000000..ec2eca3 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/views/log_view.dart @@ -0,0 +1,43 @@ +import 'package:bluetooth_low_energy_linux_example/models.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class LogView extends StatelessWidget { + final Log log; + + const LogView({ + super.key, + required this.log, + }); + + @override + Widget build(BuildContext context) { + final formatter = DateFormat.Hms(); + final time = formatter.format(log.time); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich( + TextSpan( + text: '[$time] ', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.error, + ), + children: [ + TextSpan( + text: log.type, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + )), + ], + ), + ), + Text( + log.message, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/views/peripheral_manager_view.dart b/bluetooth_low_energy_linux/example/lib/views/peripheral_manager_view.dart new file mode 100644 index 0000000..c55fabb --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/views/peripheral_manager_view.dart @@ -0,0 +1,69 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_linux_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +import 'log_view.dart'; + +class PeripheralManagerView extends StatelessWidget { + const PeripheralManagerView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final advertising = viewModel.advertising; + return Scaffold( + appBar: AppBar( + title: const Text('Peripheral Manager'), + actions: [ + TextButton( + onPressed: state == BluetoothLowEnergyState.poweredOn + ? () async { + if (advertising) { + await viewModel.stopAdvertising(); + } else { + await viewModel.startAdvertising(); + } + } + : null, + child: Text(advertising ? 'END' : 'BEGIN'), + ), + ], + ), + body: buildBody(context), + floatingActionButton: state == BluetoothLowEnergyState.poweredOn + ? FloatingActionButton( + onPressed: () => viewModel.clearLogs(), + child: const Icon(Symbols.delete), + ) + : null, + ); + } + + Widget buildBody(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + if (state == BluetoothLowEnergyState.poweredOn) { + final logs = viewModel.logs; + return ListView.builder( + padding: const EdgeInsets.all(16.0), + itemBuilder: (context, i) { + final log = logs[i]; + return LogView( + log: log, + ); + }, + itemCount: logs.length, + ); + } else { + return Center( + child: Text( + '$state', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + } +} diff --git a/bluetooth_low_energy_linux/example/lib/views/peripheral_view.dart b/bluetooth_low_energy_linux/example/lib/views/peripheral_view.dart new file mode 100644 index 0000000..1b58f1a --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/views/peripheral_view.dart @@ -0,0 +1,107 @@ +import 'package:bluetooth_low_energy_linux_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_simple_treeview/flutter_simple_treeview.dart'; + +import 'characteristic_tree_node_view.dart'; +import 'characteristic_view.dart'; +import 'descriptor_tree_node_view.dart'; +import 'service_tree_node_view.dart'; + +class PeripheralView extends StatelessWidget { + final TreeController _treeController; + + PeripheralView({super.key}) + : _treeController = TreeController( + allNodesExpanded: false, + ); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final connected = viewModel.connected; + final serviceViewModels = viewModel.serviceViewModels; + return Scaffold( + appBar: AppBar( + title: Text(viewModel.name ?? '${viewModel.uuid}'), + actions: [ + TextButton( + onPressed: () async { + if (connected) { + await viewModel.disconnect(); + } else { + await viewModel.connect(); + await viewModel.discoverGATT(); + } + }, + child: Text(connected ? 'DISCONNECT' : 'CONNECT'), + ), + ], + ), + body: SingleChildScrollView( + child: TreeView( + treeController: _treeController, + indent: 0.0, + nodes: _buildServiceTreeNodes(serviceViewModels), + ), + ), + ); + } + + List _buildServiceTreeNodes(List viewModels) { + return viewModels.map((viewModel) { + final includedServiceViewModels = viewModel.includedServiceViewModels; + final characteristicViewModels = viewModel.characteristicViewModels; + return TreeNode( + children: [ + ..._buildServiceTreeNodes(includedServiceViewModels), + ..._buildCharacteristicTreeNodes(characteristicViewModels), + ], + content: InheritedViewModel( + view: const ServiceTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } + + List _buildCharacteristicTreeNodes( + List viewModels) { + return viewModels.map((viewModel) { + final descriptorViewModels = viewModel.descriptorViewModels; + return TreeNode( + children: [ + TreeNode( + content: Expanded( + child: Container( + margin: const EdgeInsets.only(right: 40.0), + height: 360.0, + child: InheritedViewModel( + view: const CharacteristicView(), + viewModel: viewModel, + ), + ), + ), + ), + ..._buildDescriptorTreeNodes(descriptorViewModels), + ], + content: InheritedViewModel( + view: const CharacteristicTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } + + List _buildDescriptorTreeNodes( + List viewModels) { + return viewModels.map((viewModel) { + return TreeNode( + content: InheritedViewModel( + view: const DescriptorTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/views/service_tree_node_view.dart b/bluetooth_low_energy_linux/example/lib/views/service_tree_node_view.dart new file mode 100644 index 0000000..7936025 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/views/service_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_linux_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class ServiceTreeNodeView extends StatelessWidget { + const ServiceTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ); + } +} diff --git a/bluetooth_low_energy_linux/example/lib/widgets.dart b/bluetooth_low_energy_linux/example/lib/widgets.dart new file mode 100644 index 0000000..baccf61 --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/widgets.dart @@ -0,0 +1 @@ +export 'widgets/rssi_indicator.dart'; diff --git a/bluetooth_low_energy_linux/example/lib/widgets/rssi_indicator.dart b/bluetooth_low_energy_linux/example/lib/widgets/rssi_indicator.dart new file mode 100644 index 0000000..ddd59df --- /dev/null +++ b/bluetooth_low_energy_linux/example/lib/widgets/rssi_indicator.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class RSSIIndicator extends StatelessWidget { + final int rssi; + + const RSSIIndicator( + this.rssi, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final IconData icon; + if (rssi > -70) { + icon = Icons.wifi_rounded; + } else if (rssi > -100) { + icon = Icons.wifi_2_bar_rounded; + } else { + icon = Icons.wifi_1_bar_rounded; + } + return Icon(icon); + } +} diff --git a/bluetooth_low_energy_linux/example/linux/CMakeLists.txt b/bluetooth_low_energy_linux/example/linux/CMakeLists.txt index 4dd7f8e..d548497 100644 --- a/bluetooth_low_energy_linux/example/linux/CMakeLists.txt +++ b/bluetooth_low_energy_linux/example/linux/CMakeLists.txt @@ -4,10 +4,10 @@ project(runner LANGUAGES CXX) # The name of the executable created for the application. Change this to change # the on-disk name of your application. -set(BINARY_NAME "bluetooth_low_energy_example") +set(BINARY_NAME "bluetooth_low_energy_linux_example") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "dev.yanshouwang.bluetooth_low_energy") +set(APPLICATION_ID "dev.hebei.bluetooth_low_energy_linux") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. @@ -87,7 +87,7 @@ set_target_properties(${BINARY_NAME} ) # Enable the test target. -set(include_bluetooth_low_energy_tests TRUE) +set(include_bluetooth_low_energy_linux_tests TRUE) # Generated plugin build rules, which manage building the plugins and adding # them to the application. @@ -125,6 +125,12 @@ foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) COMPONENT Runtime) endforeach(bundled_library) +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") diff --git a/bluetooth_low_energy_linux/example/linux/my_application.cc b/bluetooth_low_energy_linux/example/linux/my_application.cc index 920ef9f..7ca71f4 100644 --- a/bluetooth_low_energy_linux/example/linux/my_application.cc +++ b/bluetooth_low_energy_linux/example/linux/my_application.cc @@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) { if (use_header_bar) { GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "bluetooth_low_energy_example"); + gtk_header_bar_set_title(header_bar, "bluetooth_low_energy_linux_example"); gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); } else { - gtk_window_set_title(window, "bluetooth_low_energy_example"); + gtk_window_set_title(window, "bluetooth_low_energy_linux_example"); } gtk_window_set_default_size(window, 1280, 720); @@ -81,6 +81,24 @@ static gboolean my_application_local_command_line(GApplication* application, gch return TRUE; } +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + // Implements GObject::dispose. static void my_application_dispose(GObject* object) { MyApplication* self = MY_APPLICATION(object); @@ -91,6 +109,8 @@ static void my_application_dispose(GObject* object) { static void my_application_class_init(MyApplicationClass* klass) { G_APPLICATION_CLASS(klass)->activate = my_application_activate; G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; G_OBJECT_CLASS(klass)->dispose = my_application_dispose; } diff --git a/bluetooth_low_energy_linux/example/pubspec.lock b/bluetooth_low_energy_linux/example/pubspec.lock index 3914ccb..3101311 100644 --- a/bluetooth_low_energy_linux/example/pubspec.lock +++ b/bluetooth_low_energy_linux/example/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" async: dependency: transitive description: @@ -23,15 +23,15 @@ packages: path: ".." relative: true source: path - version: "5.0.2" + version: "6.0.0" bluetooth_low_energy_platform_interface: dependency: "direct main" description: name: bluetooth_low_energy_platform_interface - sha256: "5af74eb8f97a896dfdbcff2da284d4fe5b4e2e49ebb2f46f826ac1810f66e4d7" + sha256: bc2e8d97c141653e5747bcb3cdc9fe956541b6ecc6e5f158b99a2f3abc2d946a url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "6.0.0" bluez: dependency: transitive description: @@ -64,8 +64,16 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + clover: + dependency: "direct main" + description: + name: clover + sha256: eba28e69b32f174a51c093eef098cf5ae646470322727081d5d3d8f66c786487 + url: "https://pub.dev" + source: hosted + version: "3.0.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a @@ -84,10 +92,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" dbus: dependency: transitive description: @@ -134,7 +142,15 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + flutter_simple_treeview: + dependency: "direct main" + description: + name: flutter_simple_treeview + sha256: ad4978d2668dd078d3a09966832da111bef9102dd636e572c50c80133b7ff4d9 url: "https://pub.dev" source: hosted version: "3.0.2" @@ -143,11 +159,32 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter source: sdk version: "0.0.0" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "6ad5662b014c06c20fa46ab78715c96b2222a7fe4f87bf77e0289592c2539e86" + url: "https://pub.dev" + source: hosted + version: "14.1.3" + hybrid_logging: + dependency: "direct main" + description: + name: hybrid_logging + sha256: "54248d52ce68c14702a42fbc4083bac5c6be30f6afad8a41be4bbadd197b8af5" + url: "https://pub.dev" + source: hosted + version: "1.0.0" integration_test: dependency: "direct dev" description: flutter @@ -165,44 +202,36 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" - log_service: - dependency: transitive - description: - name: log_service - sha256: "21124936899e227d1779268077921d46d57456e2592d1562e455be273594e2e4" - url: "https://pub.dev" - source: hosted - version: "1.0.0" + version: "4.0.0" logging: - dependency: transitive + dependency: "direct main" description: name: logging sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" @@ -225,14 +254,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.0" + material_symbols_icons: + dependency: "direct main" + description: + name: material_symbols_icons + sha256: b2d3cbc3c42b8a217715b0d97ff03aebb14b2b4592875736e5599c603fb2db7e + url: "https://pub.dev" + source: hosted + version: "4.2758.0" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" path: dependency: transitive description: @@ -330,10 +367,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -354,10 +391,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" webdriver: dependency: transitive description: @@ -375,5 +412,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.3.0-279.1.beta <4.0.0" - flutter: ">=3.0.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/bluetooth_low_energy_linux/example/pubspec.yaml b/bluetooth_low_energy_linux/example/pubspec.yaml index 0fa0834..b0b1227 100644 --- a/bluetooth_low_energy_linux/example/pubspec.yaml +++ b/bluetooth_low_energy_linux/example/pubspec.yaml @@ -1,5 +1,5 @@ -name: bluetooth_low_energy_example -description: Demonstrates how to use the bluetooth_low_energy plugin. +name: bluetooth_low_energy_linux_example +description: "Demonstrates how to use the bluetooth_low_energy_linux plugin." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev @@ -17,10 +17,10 @@ dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^5.0.0 + bluetooth_low_energy_platform_interface: ^6.0.0 bluetooth_low_energy_linux: # When depending on this package from a real application you should use: - # bluetooth_low_energy: ^x.y.z + # bluetooth_low_energy_linux: ^x.y.z # See https://dart.dev/tools/pub/dependencies#version-constraints # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. @@ -28,9 +28,16 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - convert: ^3.1.1 + cupertino_icons: ^1.0.6 + clover: ^3.0.0 + go_router: ^14.1.3 + logging: ^1.2.0 + hybrid_logging: ^1.0.0 intl: ^0.19.0 + collection: ^1.18.0 + convert: ^3.1.1 + flutter_simple_treeview: ^3.0.2 + material_symbols_icons: ^4.2744.0 dev_dependencies: integration_test: @@ -43,7 +50,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: ^3.0.0 + flutter_lints: ^4.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_linux/example/test/widget_test.dart b/bluetooth_low_energy_linux/example/test/widget_test.dart index b15e87d..2a2b819 100644 --- a/bluetooth_low_energy_linux/example/test/widget_test.dart +++ b/bluetooth_low_energy_linux/example/test/widget_test.dart @@ -5,23 +5,4 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:bluetooth_low_energy_example/main.dart'; - -void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); -} +void main() {} 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 4f11f9d..b348c12 100644 --- a/bluetooth_low_energy_linux/lib/bluetooth_low_energy_linux.dart +++ b/bluetooth_low_energy_linux/lib/bluetooth_low_energy_linux.dart @@ -2,8 +2,8 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla import 'src/my_central_manager.dart'; -abstract class BluetoothLowEnergyLinux { +abstract class BluetoothLowEnergyLinuxPlugin { static void registerWith() { - CentralManager.instance = MyCentralManager(); + PlatformCentralManager.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 7e5d714..bc68543 100644 --- a/bluetooth_low_energy_linux/lib/src/my_bluez.dart +++ b/bluetooth_low_energy_linux/lib/src/my_bluez.dart @@ -3,38 +3,38 @@ import 'dart:typed_data'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'package:bluez/bluez.dart'; -import 'my_gatt_characteristic2.dart'; -import 'my_gatt_descriptor2.dart'; -import 'my_gatt_service2.dart'; +import 'my_gatt.dart'; + +extension BlueZUUIDX on BlueZUUID { + UUID toMyUUID() => UUID(value); +} extension BlueZGattCharacteristicFlagX on BlueZGattCharacteristicFlag { - GattCharacteristicProperty? toMyProperty() { + GATTCharacteristicProperty? toMyProperty() { switch (this) { case BlueZGattCharacteristicFlag.read: - return GattCharacteristicProperty.read; + return GATTCharacteristicProperty.read; case BlueZGattCharacteristicFlag.write: - return GattCharacteristicProperty.write; + return GATTCharacteristicProperty.write; case BlueZGattCharacteristicFlag.writeWithoutResponse: - return GattCharacteristicProperty.writeWithoutResponse; + return GATTCharacteristicProperty.writeWithoutResponse; case BlueZGattCharacteristicFlag.notify: - return GattCharacteristicProperty.notify; + return GATTCharacteristicProperty.notify; case BlueZGattCharacteristicFlag.indicate: - return GattCharacteristicProperty.indicate; + return GATTCharacteristicProperty.indicate; default: return null; } } } -extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType { +extension GattCharacteristicWriteTypeX on GATTCharacteristicWriteType { BlueZGattCharacteristicWriteType toBlueZWriteType() { switch (this) { - case GattCharacteristicWriteType.withResponse: + case GATTCharacteristicWriteType.withResponse: return BlueZGattCharacteristicWriteType.request; - case GattCharacteristicWriteType.withoutResponse: + case GATTCharacteristicWriteType.withoutResponse: return BlueZGattCharacteristicWriteType.command; - default: - throw UnimplementedError(); } } } @@ -50,35 +50,26 @@ extension BlueZAdapterX on BlueZAdapter { extension BlueZDeviceX on BlueZDevice { UUID get myUUID => UUID.fromAddress(address); - List get myServices => - gattServices.map((service) => MyGattService2(service)).toList(); + List get myServices => + gattServices.map((service) => MyGATTService(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, + name: name.isEmpty ? null : name, + serviceUUIDs: uuids.map((uuid) => uuid.toMyUUID()).toList(), + serviceData: serviceData.map((uuid, data) { + final myUUID = uuid.toMyUUID(); + final myData = Uint8List.fromList(data); + return MapEntry(myUUID, myData); + }), + manufacturerSpecificData: manufacturerData.entries.map((entry) { + final myId = entry.key.id; + final myData = Uint8List.fromList(entry.value); + return ManufacturerSpecificData( + id: myId, + data: myData, + ); + }).toList(), ); } } @@ -90,23 +81,19 @@ extension BlueZGattDescriptorX on BlueZGattDescriptor { extension MyBlueZGattCharacteristic on BlueZGattCharacteristic { UUID get myUUID => uuid.toMyUUID(); - List get myProperties => flags + List get myProperties => flags .map((e) => e.toMyProperty()) - .whereType() + .whereType() .toList(); - List get myDescriptors => - descriptors.map((descriptor) => MyGattDescriptor2(descriptor)).toList(); + List get myDescriptors => + descriptors.map((descriptor) => MyGATTDescriptor(descriptor)).toList(); } extension BlueZGattServiceX on BlueZGattService { UUID get myUUID => uuid.toMyUUID(); - List get myCharacteristics => characteristics - .map((characteristic) => MyGattCharacteristic2(characteristic)) + List get myCharacteristics => characteristics + .map((characteristic) => MyGATTCharacteristic(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 index 4db6ccc..81d57b7 100644 --- a/bluetooth_low_energy_linux/lib/src/my_central_manager.dart +++ b/bluetooth_low_energy_linux/lib/src/my_central_manager.dart @@ -5,29 +5,34 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla 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'; +import 'my_gatt.dart'; +import 'my_peripheral.dart'; -class MyCentralManager extends CentralManager { +final class BlueZDeviceServicesResolvedEventArgs extends EventArgs { + final BlueZDevice device; + + BlueZDeviceServicesResolvedEventArgs(this.device); +} + +final class MyCentralManager extends PlatformCentralManager { final BlueZClient _blueZClient; final StreamController _stateChangedController; final StreamController _discoveredController; - final StreamController + final StreamController _connectionStateChangedController; - final StreamController + final StreamController _characteristicNotifiedController; final StreamController _blueZServicesResolvedController; + final Map> _services; final Map _blueZCharacteristicPropertiesChangedSubscriptions; - final Map> _services; BluetoothLowEnergyState _state; + bool _discovering; + int _readCount; MyCentralManager() : _blueZClient = BlueZClient(), @@ -36,66 +41,92 @@ class MyCentralManager extends CentralManager { _connectionStateChangedController = StreamController.broadcast(), _characteristicNotifiedController = StreamController.broadcast(), _blueZServicesResolvedController = StreamController.broadcast(), - _blueZCharacteristicPropertiesChangedSubscriptions = {}, _services = {}, - _state = BluetoothLowEnergyState.unknown; + _blueZCharacteristicPropertiesChangedSubscriptions = {}, + _state = BluetoothLowEnergyState.unknown, + _discovering = false, + _readCount = 0; BlueZAdapter get _blueZAdapter => _blueZClient.adapters.first; + @override + BluetoothLowEnergyState get state => _state; + set state(BluetoothLowEnergyState value) { + if (_state == value) { + return; + } + logger.info('onStateChagned: $value'); + _state = value; + final eventArgs = BluetoothLowEnergyStateChangedEventArgs(value); + _stateChangedController.add(eventArgs); + } + @override Stream get stateChanged => _stateChangedController.stream; @override Stream get discovered => _discoveredController.stream; @override - Stream get connectionStateChanged => - _connectionStateChangedController.stream; + Stream + get connectionStateChanged => _connectionStateChangedController.stream; @override - Stream get characteristicNotified => + Stream get mtuChanged => + throw UnsupportedError('mtuChanged is not supported on Linux.'); + @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); + void initialize() { + _initialize(); } @override - Future getState() { - logger.info('getState'); - return Future.value(_state); + Future authorize() { + throw UnsupportedError('authorize is not supported on Linux.'); } @override - Future startDiscovery() async { + Future showAppSettings() { + throw UnsupportedError('showAppSettings is not supported on Linux.'); + } + + @override + Future startDiscovery({ + List? serviceUUIDs, + }) async { logger.info('startDiscovery'); + await _blueZAdapter.setDiscoveryFilter( + uuids: serviceUUIDs?.map((uuid) => '$uuid').toList(), + ); await _blueZAdapter.startDiscovery(); + _discovering = true; } @override Future stopDiscovery() async { logger.info('stopDiscovery'); await _blueZAdapter.stopDiscovery(); + _discovering = false; + } + + @override + Future> retrieveConnectedPeripherals() { + logger.info('retrieveConnectedPeripherals'); + final peripherals = _blueZClient.devices + .where((blueZDevice) => + blueZDevice.adapter.address == _blueZAdapter.address && + blueZDevice.connected) + .map((blueZDevice) => MyPeripheral(blueZDevice)) + .toList(); + return Future.value(peripherals); } @override Future connect(Peripheral peripheral) async { - if (peripheral is! MyPeripheral2) { + if (peripheral is! MyPeripheral) { throw TypeError(); } final blueZDevice = peripheral.blueZDevice; @@ -106,7 +137,7 @@ class MyCentralManager extends CentralManager { @override Future disconnect(Peripheral peripheral) async { - if (peripheral is! MyPeripheral2) { + if (peripheral is! MyPeripheral) { throw TypeError(); } final blueZDevice = peripheral.blueZDevice; @@ -115,9 +146,40 @@ class MyCentralManager extends CentralManager { await blueZDevice.disconnect(); } + @override + Future requestMTU( + Peripheral peripheral, { + required int mtu, + }) { + throw UnsupportedError('requestMTU is not supported on Linux.'); + } + + @override + Future getMaximumWriteLength( + Peripheral peripheral, { + required GATTCharacteristicWriteType type, + }) { + if (peripheral is! MyPeripheral) { + throw TypeError(); + } + final blueZDevice = peripheral.blueZDevice; + final blueZAddress = blueZDevice.address; + logger.info('getMaximumWriteLength: $blueZAddress'); + final blueZMTU = blueZDevice.gattServices + .firstWhere((service) => service.characteristics.isNotEmpty) + .characteristics + .first + .mtu; + if (blueZMTU == null) { + throw ArgumentError.notNull(); + } + final maximumWriteLength = (blueZMTU - 3).clamp(20, 512); + return Future.value(maximumWriteLength); + } + @override Future readRSSI(Peripheral peripheral) async { - if (peripheral is! MyPeripheral2) { + if (peripheral is! MyPeripheral) { throw TypeError(); } final blueZDevice = peripheral.blueZDevice; @@ -127,8 +189,8 @@ class MyCentralManager extends CentralManager { } @override - Future> discoverGATT(Peripheral peripheral) async { - if (peripheral is! MyPeripheral2) { + Future> discoverGATT(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { throw TypeError(); } final blueZDevice = peripheral.blueZDevice; @@ -148,44 +210,47 @@ class MyCentralManager extends CentralManager { @override Future readCharacteristic( - GattCharacteristic characteristic, + Peripheral peripheral, + GATTCharacteristic characteristic, ) async { - if (characteristic is! MyGattCharacteristic2) { + if (characteristic is! MyGATTCharacteristic) { throw TypeError(); } final blueZCharacteristic = characteristic.blueZCharacteristic; final blueZUUID = blueZCharacteristic.uuid; logger.info('readCharacteristic: $blueZUUID'); final blueZValue = await blueZCharacteristic.readValue(); + _readCount++; return Uint8List.fromList(blueZValue); } @override Future writeCharacteristic( - GattCharacteristic characteristic, { + Peripheral peripheral, + GATTCharacteristic characteristic, { required Uint8List value, - required GattCharacteristicWriteType type, + required GATTCharacteristicWriteType type, }) async { - if (characteristic is! MyGattCharacteristic2) { + if (characteristic is! MyGATTCharacteristic) { throw TypeError(); } final blueZCharacteristic = characteristic.blueZCharacteristic; final blueZUUID = blueZCharacteristic.uuid; - final trimmedValue = value.trimGATT(); final blueZType = type.toBlueZWriteType(); - logger.info('writeCharacteristic: $blueZUUID - $trimmedValue, $blueZType'); + logger.info('writeCharacteristic: $blueZUUID - $value, $blueZType'); await blueZCharacteristic.writeValue( - trimmedValue, + value, type: blueZType, ); } @override Future setCharacteristicNotifyState( - GattCharacteristic characteristic, { + Peripheral peripheral, + GATTCharacteristic characteristic, { required bool state, }) async { - if (characteristic is! MyGattCharacteristic2) { + if (characteristic is! MyGATTCharacteristic) { throw TypeError(); } final blueZCharacteristic = characteristic.blueZCharacteristic; @@ -200,8 +265,11 @@ class MyCentralManager extends CentralManager { } @override - Future readDescriptor(GattDescriptor descriptor) async { - if (descriptor is! MyGattDescriptor2) { + Future readDescriptor( + Peripheral peripheral, + GATTDescriptor descriptor, + ) async { + if (descriptor is! MyGATTDescriptor) { throw TypeError(); } final blueZDescriptor = descriptor.blueZDescriptor; @@ -213,17 +281,17 @@ class MyCentralManager extends CentralManager { @override Future writeDescriptor( - GattDescriptor descriptor, { + Peripheral peripheral, + GATTDescriptor descriptor, { required Uint8List value, }) async { - if (descriptor is! MyGattDescriptor2) { + if (descriptor is! MyGATTDescriptor) { throw TypeError(); } final blueZDescriptor = descriptor.blueZDescriptor; final blueZUUID = blueZDescriptor.uuid; - final trimmedValue = value.trimGATT(); - logger.info('writeDescriptor: $blueZUUID - $trimmedValue'); - await blueZDescriptor.writeValue(trimmedValue); + logger.info('writeDescriptor: $blueZUUID - $value'); + await blueZDescriptor.writeValue(value); } void _onBlueZAdapterPropertiesChanged(List blueZAdapterProperties) { @@ -231,13 +299,7 @@ class MyCentralManager extends CentralManager { 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); + state = _blueZAdapter.myState; break; default: break; @@ -250,12 +312,14 @@ class MyCentralManager extends CentralManager { if (blueZDevice.adapter.address != _blueZAdapter.address) { return; } - _onBlueZDiscovered(blueZDevice); + if (_discovering) { + _onBlueZDiscovered(blueZDevice); + } _beginBlueZDevicePropertiesChangedListener(blueZDevice); } void _onBlueZDiscovered(BlueZDevice blueZDevice) { - final peripheral = MyPeripheral2(blueZDevice); + final peripheral = MyPeripheral(blueZDevice); final rssi = blueZDevice.rssi; final advertisement = blueZDevice.myAdvertisement; final eventArgs = DiscoveredEventArgs( @@ -273,19 +337,24 @@ class MyCentralManager extends CentralManager { for (var blueZDeviceProperty in blueZDeviceProperties) { switch (blueZDeviceProperty) { case 'RSSI': - _onBlueZDiscovered(blueZDevice); + if (_discovering) { + _onBlueZDiscovered(blueZDevice); + } break; case 'Connected': - final peripheral = MyPeripheral2(blueZDevice); - final state = blueZDevice.connected; - final eventArgs = ConnectionStateChangedEventArgs( + final connected = blueZDevice.connected; + if (!connected) { + _endBlueZCharacteristicPropertiesChangedListener(blueZDevice); + } + final peripheral = MyPeripheral(blueZDevice); + final state = blueZDevice.connected + ? ConnectionState.connected + : ConnectionState.disconnected; + final eventArgs = PeripheralConnectionStateChangedEventArgs( peripheral, state, ); _connectionStateChangedController.add(eventArgs); - if (!state) { - _endBlueZCharacteristicPropertiesChangedListener(blueZDevice); - } break; case 'UUIDs': break; @@ -313,6 +382,7 @@ class MyCentralManager extends CentralManager { if (services == null) { return; } + final peripheral = MyPeripheral(blueZDevice); for (var service in services) { final characteristics = service.characteristics; for (var characteristic in characteristics) { @@ -325,8 +395,13 @@ class MyCentralManager extends CentralManager { in blueZCharacteristicProperties) { switch (blueZCharacteristicPropety) { case 'Value': + if (_readCount > 0) { + _readCount--; + return; + } final value = Uint8List.fromList(blueZCharacteristic.value); - final eventArgs = GattCharacteristicNotifiedEventArgs( + final eventArgs = GATTCharacteristicNotifiedEventArgs( + peripheral, characteristic, value, ); @@ -361,4 +436,30 @@ class MyCentralManager extends CentralManager { } } } + + Future _initialize() async { + // Here we use `Future()` to make it possible to change the `logLevel` before `initialize()`. + await Future(() async { + try { + logger.info('initialize'); + await _blueZClient.connect(); + if (_blueZClient.adapters.isEmpty) { + state = BluetoothLowEnergyState.unsupported; + } else { + 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); + } + } catch (e) { + logger.severe('initialize failed.', e); + } + }); + } } diff --git a/bluetooth_low_energy_linux/lib/src/my_event_args.dart b/bluetooth_low_energy_linux/lib/src/my_event_args.dart deleted file mode 100644 index 24a2a85..0000000 --- a/bluetooth_low_energy_linux/lib/src/my_event_args.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:bluez/bluez.dart'; - -class BlueZDeviceServicesResolvedEventArgs extends EventArgs { - final BlueZDevice device; - - BlueZDeviceServicesResolvedEventArgs(this.device); -} diff --git a/bluetooth_low_energy_linux/lib/src/my_gatt.dart b/bluetooth_low_energy_linux/lib/src/my_gatt.dart new file mode 100644 index 0000000..a5d006f --- /dev/null +++ b/bluetooth_low_energy_linux/lib/src/my_gatt.dart @@ -0,0 +1,74 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluez/bluez.dart'; + +import 'my_bluez.dart'; + +final class MyGATTDescriptor extends GATTDescriptor { + final BlueZGattDescriptor blueZDescriptor; + + MyGATTDescriptor(this.blueZDescriptor) + : super( + uuid: blueZDescriptor.myUUID, + ); + + @override + int get hashCode => blueZDescriptor.hashCode; + + @override + bool operator ==(Object other) { + return other is MyGATTDescriptor && + other.blueZDescriptor == blueZDescriptor; + } +} + +final class MyGATTCharacteristic extends GATTCharacteristic { + final BlueZGattCharacteristic blueZCharacteristic; + + MyGATTCharacteristic(this.blueZCharacteristic) + : super( + 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 MyGATTCharacteristic && + other.blueZCharacteristic == blueZCharacteristic; + } +} + +final class MyGATTService extends GATTService { + final BlueZGattService blueZService; + + MyGATTService(this.blueZService) + : super( + uuid: blueZService.myUUID, + isPrimary: blueZService.primary, + includedServices: [], + characteristics: blueZService.myCharacteristics, + ); + + @override + List get includedServices => + throw UnsupportedError('includedServices is not supported on Linux.'); + + @override + List get characteristics => + super.characteristics.cast(); + + @override + int get hashCode => blueZService.hashCode; + + @override + bool operator ==(Object other) { + return other is MyGATTService && other.blueZService == blueZService; + } +} diff --git a/bluetooth_low_energy_linux/lib/src/my_gatt_characteristic2.dart b/bluetooth_low_energy_linux/lib/src/my_gatt_characteristic2.dart deleted file mode 100644 index 8aca310..0000000 --- a/bluetooth_low_energy_linux/lib/src/my_gatt_characteristic2.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:bluez/bluez.dart'; - -import 'my_bluez.dart'; -import 'my_gatt_descriptor2.dart'; - -class MyGattCharacteristic2 extends MyGattCharacteristic { - final BlueZGattCharacteristic blueZCharacteristic; - - MyGattCharacteristic2(this.blueZCharacteristic) - : super( - 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 deleted file mode 100644 index 4cb4ba7..0000000 --- a/bluetooth_low_energy_linux/lib/src/my_gatt_descriptor2.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:bluez/bluez.dart'; - -import 'my_bluez.dart'; - -class MyGattDescriptor2 extends MyGattDescriptor { - final BlueZGattDescriptor blueZDescriptor; - - MyGattDescriptor2(this.blueZDescriptor) - : super( - 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 deleted file mode 100644 index bf7670a..0000000 --- a/bluetooth_low_energy_linux/lib/src/my_gatt_service2.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:bluez/bluez.dart'; - -import 'my_bluez.dart'; -import 'my_gatt_characteristic2.dart'; - -class MyGattService2 extends MyGattService { - final BlueZGattService blueZService; - - MyGattService2(this.blueZService) - : super( - 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_peripheral.dart similarity index 76% rename from bluetooth_low_energy_linux/lib/src/my_peripheral2.dart rename to bluetooth_low_energy_linux/lib/src/my_peripheral.dart index 77a5e22..17efba5 100644 --- a/bluetooth_low_energy_linux/lib/src/my_peripheral2.dart +++ b/bluetooth_low_energy_linux/lib/src/my_peripheral.dart @@ -3,10 +3,10 @@ import 'package:bluez/bluez.dart'; import 'my_bluez.dart'; -class MyPeripheral2 extends MyPeripheral { +final class MyPeripheral extends Peripheral { final BlueZDevice blueZDevice; - MyPeripheral2(this.blueZDevice) + MyPeripheral(this.blueZDevice) : super( uuid: blueZDevice.myUUID, ); diff --git a/bluetooth_low_energy_linux/pubspec.yaml b/bluetooth_low_energy_linux/pubspec.yaml index ea07e58..0976aca 100644 --- a/bluetooth_low_energy_linux/pubspec.yaml +++ b/bluetooth_low_energy_linux/pubspec.yaml @@ -1,26 +1,35 @@ name: bluetooth_low_energy_linux -description: Linux implementation of the bluetooth_low_energy plugin. -version: 5.0.2 +description: "Linux implementation of the bluetooth_low_energy plugin." +version: 6.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy +repository: https://github.com/yanshouwang/bluetooth_low_energy +issue_tracker: https://github.com/yanshouwang/bluetooth_low_energy/issues +topics: + - bluetooth + - bluetooth-low-energy + - ble +funding: + - https://paypal.me/yanshouwang5112 + - https://afdian.net/a/yanshouwang environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.0.0" + sdk: '>=3.0.0 <4.0.0' + flutter: '>=3.0.0' dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^5.0.2 - bluez: ^0.8.1 + bluetooth_low_energy_platform_interface: ^6.0.0 + bluez: ^0.8.2 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.0 + flutter_lints: ^4.0.0 flutter: plugin: implements: bluetooth_low_energy platforms: linux: - dartPluginClass: BluetoothLowEnergyLinux + dartPluginClass: BluetoothLowEnergyLinuxPlugin diff --git a/bluetooth_low_energy_linux/test/bluetooth_low_energy_linux_test.dart b/bluetooth_low_energy_linux/test/bluetooth_low_energy_linux_test.dart new file mode 100644 index 0000000..ab73b3a --- /dev/null +++ b/bluetooth_low_energy_linux/test/bluetooth_low_energy_linux_test.dart @@ -0,0 +1 @@ +void main() {} diff --git a/bluetooth_low_energy_platform_interface/.gitignore b/bluetooth_low_energy_platform_interface/.gitignore index 96486fd..ac5aa98 100644 --- a/bluetooth_low_energy_platform_interface/.gitignore +++ b/bluetooth_low_energy_platform_interface/.gitignore @@ -26,5 +26,4 @@ migrate_working_dir/ /pubspec.lock **/doc/api/ .dart_tool/ -.packages build/ diff --git a/bluetooth_low_energy_platform_interface/.metadata b/bluetooth_low_energy_platform_interface/.metadata index 0885ce9..1437de8 100644 --- a/bluetooth_low_energy_platform_interface/.metadata +++ b/bluetooth_low_energy_platform_interface/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: "efbf63d9c66b9f6ec30e9ad4611189aa80003d31" + revision: "5dcb86f68f239346676ceb1ed1ea385bd215fba1" channel: "stable" project_type: plugin @@ -13,8 +13,8 @@ project_type: plugin migration: platforms: - platform: root - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 # User provided section diff --git a/bluetooth_low_energy_platform_interface/CHANGELOG.md b/bluetooth_low_energy_platform_interface/CHANGELOG.md index bf817fd..adf829a 100644 --- a/bluetooth_low_energy_platform_interface/CHANGELOG.md +++ b/bluetooth_low_energy_platform_interface/CHANGELOG.md @@ -1,3 +1,191 @@ +## 6.0.0 + +* Add `BluetoothLowEnergyManager#authorize` method. +* Add `BluetoothLowEnergyManager#showAppSettings` method. +* Add `CentralManager#mtuChanged` event. +* [Add optional `serviceUUIDs` argument to the `CentralManager#startDiscovery` method.](https://github.com/yanshouwang/bluetooth_low_energy/issues/53) +* [Add `CentralManager#retrieveConnectedPeripherals` method.](https://github.com/yanshouwang/bluetooth_low_energy/issues/61) +* Add `CentralManager#getMaximumWriteLength` method. +* Add `PeripheralManager#connectionStateChanged` event. +* Add `PeripheralManager#mtuChanged` event. +* Add `PeripheralManager#characteristicReadRequested` event. +* Add `PeripheralManager#characteristicWriteRequested` event. +* Add `PeripheralManager#descriptorReadRequested` event. +* Add `PeripheralManager#descriptorWriteRequested` event. +* Add `PeripheralManager#getMaximumNotifyLength` method. +* Add `PeripheralManager#respondReadRequestWithValue` method. +* Add `PeripheralManager#respondReadRequestWithError` method. +* Add `PeripheralManager#respondWriteRequest` method. +* Add `PeripheralManager#respondWriteRequestWithError` method. +* Move the type of `Advertisement#manufacturerSpecificData` to `List`. +* Move `BluetoothLowEnergyManager#getState` to `BluetoothLowEnergyManager#state`. +* Move `PeripheralManager#clearServices` to `PeripheralManager#removeAllServices`. +* Remove `PeripheralManager#characteristicRead` event. +* Remove `PeripheralManager#characteristicWritten` event. + + +## 6.0.0-dev.19 + +* Add `int` type to `UUID#fromAddress`. +* Move the type of `Advertisement#manufacturerSpecificData` to `List`. + +## 6.0.0-dev.18 + +* Add `PeripheralManager#respondReadRequestWithValue`. +* Add `PeripheralManager#respondReadRequestWithError`. +* Add `PeripheralManager#respondWriteRequest`. +* Add `PeripheralManager#respondWriteRequestWithError`. +* Remove `PeripheralManager#respondCharacteristicReadRequestWithValue`. +* Remove `PeripheralManager#respondCharacteristicReadRequestWithError`. +* Remove `PeripheralManager#respondCharacteristicWriteRequest`. +* Remove `PeripheralManager#respondCharacteristicWriteRequestWithError`. +* Remove `PeripheralManager#respondDescriptorReadRequestWithValue`. +* Remove `PeripheralManager#respondDescriptorReadRequestWithError`. +* Remove `PeripheralManager#respondDescriptorWriteRequest`. +* Remove `PeripheralManager#respondDescriptorWriteRequestWithError`. + +## 6.0.0-dev.17 + +* Remove `Uint8List#trimGATT` method. + +## 6.0.0-dev.16 + +* Add `GATTService.isPrimary` field. +* Add `peripheral` argument to all methods. + +## 6.0.0-dev.15 + +* Add `GATTReadRequest` class. +* Add `GATTWriteRequest` class. +* Remove `ConnectionStateChangedEventArgs` class. +* Remove `MTUChangedEventArgs` class. +* Remove `GATTRequest` class. +* Remove `GATTDescriptorRequest` class. +* Remove `GATTDescriptorReadRequest` class. +* Remove `GATTDescriptorWriteRequest` class. +* Remove `GATTCharacteristicRequest` class. +* Remove `GATTCharacteristicReadRequest` class. +* Remove `GATTCharacteristicWriteRequest` class. + +## 6.0.0-dev.14 + +* Fix errors. + +## 6.0.0-dev.13 + +* Add `MutableGATTDescriptor#permissions` field. +* Fix errors. + +## 6.0.0-dev.12 + +* Add `GATTDescriptorReadRequestedEventArgs` class. +* Add `GATTDescriptorWriteRequestedEventArgs` class. +* Add `GATTRequest` class. +* Add `GATTDescriptorRequest` class. +* Add `GATTDescriptorReadRequest` class. +* Add `GATTDescriptorWriteRequest` class. +* Add `PeripheralManager#descriptorReadRequested` event. +* Add `PeripheralManager#descriptorWriteRequested` event. +* Add `PeripheralManager#respondDescriptorReadRequestWithValue` method. +* Add `PeripheralManager#respondDescriptorReadRequestWithError` method. +* Add `PeripheralManager#respondDescriptorWriteRequest` method. +* Add `PeripheralManager#respondDescriptorWriteRequestWithError` method. +* Fix errors. + +## 6.0.0-dev.11 + +* Add `ConnectionStateChangedEventArgs` class. +* Add `MTUChangedEventArgs` class. +* Add `CentralConnectionStateChangedEventArgs` class. +* Add `GATTCharacteristicReadRequestedEventArgs` class. +* Add `GATTCharacteristicWriteRequestedEventArgs` class. +* Add `CentralManager#getMaximumWriteLength` method. +* Add `PeripheralManager#connectionStateChanged` event. +* Add `PeripheralManager#characteristicReadRequested` event. +* Add `PeripheralManager#characteristicWriteRequested` event. +* Add `PeripheralManager#getMaximumNotifyLength` method. +* Add `PeripheralManager#respondCharacteristicReadRequestWithValue` method. +* Add `PeripheralManager#respondCharacteristicReadRequestWithError` method. +* Add `PeripheralManager#respondCharacteristicWriteRequest` method. +* Add `PeripheralManager#respondCharacteristicWriteRequestWithError` method. +* Add `ImmutableGATTCharacteristic` class. +* Add `GATTCharacteristicRequest` class. +* Add `GATTCharacteristicReadRequest` class. +* Add `GATTCharacteristicWriteRequest` class. +* Add `GATTCharacteristicPermission` enum. +* Add `GATTError` enum. +* Move `PeripheralManager#clearServices` to `PeripheralManager#removeAllServices`. +* Move `BaseBluetoothLowEnergyManager` to `PlatformBluetoothLowEnergyManager`. +* Move `BaseCentralManager` to `PlatformCentralManager`. +* Move `BasePeripheralManager` to `PlatformPeripheralManager`. +* Move `BaseGATTAttribute` to `GATTAttribute`. +* Move `BaseGATTDescriptor` to `GATTDescriptor`. +* Move `BaseGATTCharacteristic` to `GATTCharacteristic`. +* Move `BaseGATTService` to `GATTService`. +* Move `MutableGATTDescriptor` to `ImmutableGATTCharacteristic`. +* Remove `GATTCharacteristicReadEventArgs` class. +* Remove `GATTCharacteristicWrittenEventArgs` class. +* Remove `PeripheralManager#characteristicRead` event. +* Remove `PeripheralManager#characteristicWritten` event. +* Remove `MutableGATTService`. + +## 6.0.0-dev.10 + +* Fix bugs. + +## 6.0.0-dev.9 + +* Add `BluetoothLowEnergyManager#authorize` method. +* Add `BluetoothLowEnergyManager#showAppSettings` method. + +## 6.0.0-dev.8 + +* Remove `logLevel` argument from the `CentralManager` construstor. +* Remove `logLevel` argument from the `PeripheralManager` construstor. + +## 6.0.0-dev.7 + +* Add `logLevel` argument to the `CentralManager` construstor. +* Add `logLevel` argument to the `PeripheralManager` construstor. + +## 6.0.0-dev.6 + +* Move `ConnectionStateChangedEventArgs` to `PeripheralConnectionStateChangedEventArgs`. +* Move `MTUChangedEventArgs` to `PeripheralMTUChangedEventArgs`. +* Add `ConnectionState` enum. +* Add `CentralMTUChangedEventArgs` class. +* Add `PeripheralManager#mtuChanged` event. + +## 6.0.0-dev.5 + +* Use new capitalization rules. + +## 6.0.0-dev.4 + +* Add `CentralManager#mtuChanged` event. +* Add modifiers to all classes. +* Use new capitalization rules. + +## 6.0.0-dev.3 + +* Remove `abstract` keyword from `Central` class. +* Remove `abstract` keyword from `Peripheral` class. + +## 6.0.0-dev.2 + +* Remove `BluetoothLowEnergyManager#authorize` method. +* Move `BluetoothLowEnergyManager#getState` to `BluetoothLowEnergyManager#state`. + +## 6.0.0-dev.1 + +* Migrate `hybrid_core` to `hybrid_logging`. + +## 6.0.0-dev.0 + +* [Add `CentralManager#retrieveConnectedPeripherals` method.](https://github.com/yanshouwang/bluetooth_low_energy/issues/61) +* [Add optional `serviceUUIDs` argument to the `CentralManager#startDiscovery` method.](https://github.com/yanshouwang/bluetooth_low_energy/issues/53) +* Optimize project structure. + ## 5.0.2 * Revert GATT characteristic's `descriptors` arguments to required. diff --git a/bluetooth_low_energy_platform_interface/LICENSE b/bluetooth_low_energy_platform_interface/LICENSE index 752d28b..3977562 100644 --- a/bluetooth_low_energy_platform_interface/LICENSE +++ b/bluetooth_low_energy_platform_interface/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 yanshouwang +Copyright (c) 2024 hebei.dev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/bluetooth_low_energy_platform_interface/README.md b/bluetooth_low_energy_platform_interface/README.md index f97371b..bc88ff8 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 [`CentralManager`][2] with an implementation that performs the +extend [`PlatformCentralManager`][2] with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default -`CentralManager` by calling `CentralManager.instance = MyCentralManager()`, -extend [`PeripheralManager`][3] with an implementation that performs the +`PlatformCentralManager` by calling `PlatformCentralManager.instance = XXXCentralManager()`, +extend [`PlatformPeripheralManager`][3] with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default -`PeripheralManager` by calling `PeripheralManager.instance = MyPeripheralManager()`. +`PlatformPeripheralManager` by calling `PlatformPeripheralManager.instance = XXXPeripheralManager()`. # 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 46a68fd..e3946e9 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 @@ -7,31 +7,18 @@ /// [1]: https://pub.dev/packages/bluetooth_low_energy library; -export 'package:log_service/log_service.dart'; - export 'src/event_args.dart'; -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_event_args.dart'; -export 'src/central_manager.dart'; -export 'src/central.dart'; -export 'src/peripheral_event_args.dart'; -export 'src/peripheral_manager.dart'; -export 'src/peripheral.dart'; export 'src/uuid.dart'; -export 'src/advertisement.dart'; +export 'src/bluetooth_low_energy_state.dart'; export 'src/manufacturer_specific_data.dart'; -export 'src/gatt_service.dart'; -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_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'; +export 'src/advertisement.dart'; +export 'src/connection_state.dart'; +export 'src/gatt.dart'; + +export 'src/bluetooth_low_energy_manager.dart'; +export 'src/central_manager.dart'; +export 'src/peripheral_manager.dart'; + +export 'src/bluetooth_low_energy_peer.dart'; +export 'src/central.dart'; +export 'src/peripheral.dart'; diff --git a/bluetooth_low_energy_platform_interface/lib/src/advertisement.dart b/bluetooth_low_energy_platform_interface/lib/src/advertisement.dart index 3ea7d40..574c084 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/advertisement.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/advertisement.dart @@ -4,24 +4,33 @@ import 'manufacturer_specific_data.dart'; import 'uuid.dart'; /// The advertisement of the peripheral. -class Advertisement { +final class Advertisement { /// The name of the peripheral. + /// + /// This field is available on Android, iOS and macOS, throws [UnsupportedError] + /// on other platforms. final String? name; /// The GATT service uuids of the peripheral. final List serviceUUIDs; /// The GATT service data of the peripheral. + /// + /// This field is available on Android and Windows, throws [UnsupportedError] + /// on other platforms. final Map serviceData; /// The manufacturer specific data of the peripheral. - final ManufacturerSpecificData? manufacturerSpecificData; + /// + /// This field is available on Android and Windows, throws [UnsupportedError] + /// on other platforms. + final List manufacturerSpecificData; /// Constructs an [Advertisement]. Advertisement({ this.name, this.serviceUUIDs = const [], this.serviceData = const {}, - this.manufacturerSpecificData, + this.manufacturerSpecificData = const [], }); } diff --git a/bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_event_args.dart b/bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_event_args.dart deleted file mode 100644 index 4b1afb1..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_event_args.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'bluetooth_low_energy_state.dart'; -import 'event_args.dart'; - -/// The bluetooth low energy state changed event arguments. -class BluetoothLowEnergyStateChangedEventArgs extends EventArgs { - /// The new state of the bluetooth low energy. - final BluetoothLowEnergyState state; - - /// Constructs a [BluetoothLowEnergyStateChangedEventArgs]. - BluetoothLowEnergyStateChangedEventArgs(this.state); -} 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 754f887..c86e556 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 @@ -1,16 +1,51 @@ -import 'package:log_service/log_service.dart'; +import 'package:hybrid_logging/hybrid_logging.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; -import 'bluetooth_low_energy_event_args.dart'; import 'bluetooth_low_energy_state.dart'; +import 'event_args.dart'; + +/// The bluetooth low energy state changed event arguments. +final class BluetoothLowEnergyStateChangedEventArgs extends EventArgs { + /// The new state of the bluetooth low energy. + final BluetoothLowEnergyState state; + + /// Constructs a [BluetoothLowEnergyStateChangedEventArgs]. + BluetoothLowEnergyStateChangedEventArgs(this.state); +} /// The abstract base class that manages central and peripheral objects. -abstract class BluetoothLowEnergyManager implements LogController { +abstract interface class BluetoothLowEnergyManager implements LogController { + /// Gets the manager's state. + BluetoothLowEnergyState get state; + /// Tells the manager's state updated. Stream get stateChanged; - /// Sets up the manager. - Future setUp(); + /// Requests permissions to be granted to this application. These permissions + /// must be requested in your manifest, they should not be granted to your app, + /// and they should have protection level dangerous, regardless whether they + /// are declared by the platform or a third-party app. + /// + /// This method is available on Android, throws [UnsupportedError] on other + /// platforms. + Future authorize(); - /// Gets the manager's state. - Future getState(); + /// Show screen of details about a particular application. + /// + /// This method is available on Android and iOS, throws [UnsupportedError] on + /// other platforms. + Future showAppSettings(); +} + +/// The abstract base channel class that manages central and peripheral objects. +abstract base class PlatformBluetoothLowEnergyManager extends PlatformInterface + with TypeLogger, LoggerController + implements BluetoothLowEnergyManager { + /// Constructs a [PlatformBluetoothLowEnergyManager]. + PlatformBluetoothLowEnergyManager({ + required super.token, + }); + + /// Initializes the [PlatformBluetoothLowEnergyManager]. + void initialize(); } diff --git a/bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_peer.dart b/bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_peer.dart index 1e1e6fa..e59886a 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_peer.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/bluetooth_low_energy_peer.dart @@ -1,7 +1,19 @@ import 'uuid.dart'; /// An object that represents a remote device. -abstract class BluetoothLowEnergyPeer { +abstract base class BluetoothLowEnergyPeer { /// The UUID associated with the peer. - UUID get uuid; + final UUID uuid; + + BluetoothLowEnergyPeer({ + required this.uuid, + }); + + @override + int get hashCode => uuid.hashCode; + + @override + bool operator ==(Object other) { + return other is BluetoothLowEnergyPeer && other.uuid == uuid; + } } diff --git a/bluetooth_low_energy_platform_interface/lib/src/central.dart b/bluetooth_low_energy_platform_interface/lib/src/central.dart index a7448f7..f3d8347 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/central.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/central.dart @@ -1,4 +1,16 @@ import 'bluetooth_low_energy_peer.dart'; /// A remote device connected to a local app, which is acting as a peripheral. -abstract class Central extends BluetoothLowEnergyPeer {} +base class Central extends BluetoothLowEnergyPeer { + Central({ + 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/central_event_args.dart b/bluetooth_low_energy_platform_interface/lib/src/central_event_args.dart deleted file mode 100644 index 87cc55d..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/central_event_args.dart +++ /dev/null @@ -1,51 +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 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 f43c3d1..a12be35 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,215 @@ 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_event_args.dart'; -import 'gatt_characteristic.dart'; -import 'gatt_characteristic_write_type.dart'; -import 'gatt_descriptor.dart'; -import 'gatt_service.dart'; +import 'connection_state.dart'; +import 'event_args.dart'; +import 'gatt.dart'; import 'peripheral.dart'; +import 'uuid.dart'; + +/// The discovered event arguments. +final 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 connection state cahnged event arguments. +final class PeripheralConnectionStateChangedEventArgs extends EventArgs { + /// The peripheral which connection state changed. + final Peripheral peripheral; + + /// The connection state. + final ConnectionState state; + + /// Constructs a [PeripheralConnectionStateChangedEventArgs]. + PeripheralConnectionStateChangedEventArgs( + this.peripheral, + this.state, + ); +} + +/// The peripheral MTU changed event arguments. +final class PeripheralMTUChangedEventArgs extends EventArgs { + /// The peripheral which MTU changed. + final Peripheral peripheral; + + /// The MTU. + final int mtu; + + /// Constructs a [PeripheralMTUChangedEventArgs]. + PeripheralMTUChangedEventArgs( + this.peripheral, + this.mtu, + ); +} + +/// The GATT characteristic notified event arguments. +final class GATTCharacteristicNotifiedEventArgs extends EventArgs { + /// The peripheral which notified. + final Peripheral peripheral; + + /// The GATT characteristic which notified. + final GATTCharacteristic characteristic; + + /// The notified value. + final Uint8List value; + + /// Constructs a [GATTCharacteristicNotifiedEventArgs]. + GATTCharacteristicNotifiedEventArgs( + this.peripheral, + this.characteristic, + this.value, + ); +} /// An object that scans for, discovers, connects to, and manages peripherals. -/// -/// 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(); - +abstract interface class CentralManager implements BluetoothLowEnergyManager { static CentralManager? _instance; - /// The default instance of [CentralManager] to use. - static CentralManager get instance { + /// Gets the instance of [CentralManager] to use. + factory CentralManager() { + final instance = PlatformCentralManager.instance; + if (instance != _instance) { + instance.initialize(); + _instance = instance; + } + return instance; + } + + /// Tells the central manager discovered a peripheral while scanning for devices. + Stream get discovered; + + /// Tells that retrieving the specified peripheral's connection state changed. + Stream get connectionStateChanged; + + /// Callback indicating the MTU for a given device connection has changed. + /// + /// This callback is triggered in response to the BluetoothGatt#requestMtu + /// function, or in response to a connection event. + /// + /// This event is available on Android and Windows, throws [UnsupportedError] + /// on other platforms. + Stream get mtuChanged; + + /// Tells that retrieving the specified characteristic’s value changed. + Stream get characteristicNotified; + + /// Scans for peripherals that are advertising services. + /// + /// The [serviceUUIDs] argument is an array of [UUID] objects that the app is + /// interested in. Each [UUID] object represents the [UUID] of a service that + /// a peripheral advertises. + Future startDiscovery({ + List? serviceUUIDs, + }); + + /// Asks the central manager to stop scanning for peripherals. + Future stopDiscovery(); + + /// Returns a list of the peripherals connected to the system. + /// + /// This method is available on Android, iOS, macOS and Linux, throws + /// [UnsupportedError] on other platforms. + Future> retrieveConnectedPeripherals(); + + /// Establishes a local connection to a peripheral. + Future connect(Peripheral peripheral); + + /// Cancels an active or pending local connection to a peripheral. + Future disconnect(Peripheral peripheral); + + /// Request an MTU size used for a given connection. Please note that starting + /// from Android 14, the Android Bluetooth stack requests the BLE ATT MTU to + /// 517 bytes when the first GATT client requests an MTU, and disregards all + /// subsequent MTU requests. Check out [MTU is set to 517 for the first GATT + /// client requesting an MTU](https://developer.android.com/about/versions/14/behavior-changes-all#mtu-set-to-517) + /// for more information. + /// + /// This method is available on Android, throws [UnsupportedError] on other + /// platforms. + Future requestMTU( + Peripheral peripheral, { + required int mtu, + }); + + /// 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. + /// + /// This method is available on Android, iOS, macOS and Linux, throws + /// [UnsupportedError] on other platforms. + Future readRSSI(Peripheral peripheral); + + /// Discovers the GATT services, characteristics and descriptors of the peripheral. + Future> discoverGATT(Peripheral peripheral); + + /// Retrieves the value of a specified characteristic. + Future readCharacteristic( + Peripheral peripheral, + GATTCharacteristic characteristic, + ); + + /// Writes the value of a characteristic. + Future writeCharacteristic( + Peripheral peripheral, + GATTCharacteristic characteristic, { + required Uint8List value, + required GATTCharacteristicWriteType type, + }); + + /// Sets notifications or indications for the value of a specified characteristic. + Future setCharacteristicNotifyState( + Peripheral peripheral, + GATTCharacteristic characteristic, { + required bool state, + }); + + /// Retrieves the value of a specified characteristic descriptor. + Future readDescriptor( + Peripheral peripheral, + GATTDescriptor descriptor, + ); + + /// Writes the value of a characteristic descriptor. + Future writeDescriptor( + Peripheral peripheral, + GATTDescriptor descriptor, { + required Uint8List value, + }); +} + +/// Platform-specific implementations should implement this class to support +/// [PlatformCentralManager]. +abstract base class PlatformCentralManager + extends PlatformBluetoothLowEnergyManager implements CentralManager { + static final Object _token = Object(); + + static PlatformCentralManager? _instance; + + /// The default instance of [PlatformCentralManager] to use. + static PlatformCentralManager get instance { final instance = _instance; if (instance == null) { throw UnimplementedError( @@ -32,67 +219,13 @@ abstract class CentralManager extends PlatformInterface } /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [CentralManager] when + /// platform-specific class that extends [PlatformCentralManager] when /// they register themselves. - static set instance(CentralManager instance) { + static set instance(PlatformCentralManager 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 connection lost. - Stream get connectionStateChanged; - - /// Tells that retrieving the specified characteristic’s value changed. - Stream get characteristicNotified; - - /// Scans for peripherals that are advertising services. - Future startDiscovery(); - - /// Asks the central manager to stop scanning for peripherals. - Future stopDiscovery(); - - /// Establishes a local connection to a peripheral. - Future connect(Peripheral peripheral); - - /// Cancels an active or pending local connection to a peripheral. - Future disconnect(Peripheral peripheral); - - /// Retrieves the current RSSI value for the peripheral while connected to the central manager. - Future readRSSI(Peripheral peripheral); - - /// Discovers the GATT services, characteristics and descriptors of the peripheral. - Future> discoverGATT(Peripheral peripheral); - - /// Retrieves the value of a specified characteristic. - 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, - required GattCharacteristicWriteType type, - }); - - /// Sets notifications or indications for the value of a specified characteristic. - Future setCharacteristicNotifyState( - GattCharacteristic characteristic, { - required bool state, - }); - - /// Retrieves the value of a specified characteristic descriptor. - Future readDescriptor(GattDescriptor descriptor); - - /// Writes the value of a characteristic descriptor. - Future writeDescriptor( - GattDescriptor descriptor, { - required Uint8List value, - }); + /// Constructs a [PlatformCentralManager]. + PlatformCentralManager() : super(token: _token); } diff --git a/bluetooth_low_energy_platform_interface/lib/src/connection_state.dart b/bluetooth_low_energy_platform_interface/lib/src/connection_state.dart new file mode 100644 index 0000000..16a3333 --- /dev/null +++ b/bluetooth_low_energy_platform_interface/lib/src/connection_state.dart @@ -0,0 +1,8 @@ +/// The connection state of a remote device. +enum ConnectionState { + /// The remote device is in disconnected state. + disconnected, + + /// The remote device is in connected state. + connected, +} diff --git a/bluetooth_low_energy_platform_interface/lib/src/event_args.dart b/bluetooth_low_energy_platform_interface/lib/src/event_args.dart index 1e449c7..99ec5c8 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/event_args.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/event_args.dart @@ -1,2 +1,2 @@ /// The base event arguments. -abstract class EventArgs {} +base class EventArgs {} diff --git a/bluetooth_low_energy_platform_interface/lib/src/gatt.dart b/bluetooth_low_energy_platform_interface/lib/src/gatt.dart new file mode 100644 index 0000000..ef7046f --- /dev/null +++ b/bluetooth_low_energy_platform_interface/lib/src/gatt.dart @@ -0,0 +1,360 @@ +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +import 'uuid.dart'; + +/// A representation of common aspects of services offered by a peripheral. +abstract base class GATTAttribute { + /// The Bluetooth-specific UUID of the attribute. + final UUID uuid; + + /// Constructs a [GATTAttribute]. + GATTAttribute({ + required this.uuid, + }); +} + +/// An object that provides further information about a remote peripheral’s +/// characteristic. +abstract base class GATTDescriptor extends GATTAttribute { + /// Constructs a [GATTDescriptor]. + GATTDescriptor({ + required super.uuid, + }); + + /// Creates a mutable descriptor. + /// + /// [uuid] A 128-bit UUID that identifies the characteristic. You must use only + /// one of the two currently supported descriptor types: + /// CBUUIDCharacteristicUserDescriptionString or CBUUIDCharacteristicFormatString. + /// For more details about these descriptor types, see CBUUID. + factory GATTDescriptor.mutable({ + required UUID uuid, + required List permissions, + }) => + MutableGATTDescriptor( + uuid: uuid, + permissions: permissions, + ); + + /// Creates a immutable descriptor with a specified value. + /// + /// [uuid] A 128-bit UUID that identifies the characteristic. You must use only + /// one of the two currently supported descriptor types: + /// CBUUIDCharacteristicUserDescriptionString or CBUUIDCharacteristicFormatString. + /// For more details about these descriptor types, see CBUUID. + /// + /// [value] The descriptor value to cache. You must provide a non-nil value. + /// Once published, you can’t update the value dynamically. + factory GATTDescriptor.immutable({ + required UUID uuid, + required Uint8List value, + }) => + ImmutableGATTDescriptor( + uuid: uuid, + value: value, + ); +} + +/// A characteristic of a remote peripheral’s service. +abstract base class GATTCharacteristic extends GATTAttribute { + /// The properties of the characteristic. + final List properties; + + /// A list of the descriptors discovered in this characteristic. + final List descriptors; + + /// Constructs a [GATTCharacteristic]. + GATTCharacteristic({ + required super.uuid, + required this.properties, + required this.descriptors, + }); + + /// Creates a mutable characteristic with specified permissions, properties. + /// + /// [uuid] A 128-bit UUID that identifies the characteristic. + /// + /// [properties] The properties of the characteristic. + /// + /// [permissions] The permissions of the characteristic value. + factory GATTCharacteristic.mutable({ + required UUID uuid, + required List properties, + required List permissions, + required List descriptors, + }) => + MutableGATTCharacteristic( + uuid: uuid, + properties: properties, + permissions: permissions, + descriptors: descriptors, + ); + + /// Creates a immutable characteristic with a specified value. + /// + /// [uuid] A 128-bit UUID that identifies the characteristic. + /// + /// [value] The characteristic value to cache. You must provide a non-nil value. + /// Once published, you can’t update the value dynamically. + factory GATTCharacteristic.immutable({ + required UUID uuid, + required Uint8List value, + required List descriptors, + }) => + ImmutableGATTCharacteristic( + uuid: uuid, + value: value, + descriptors: descriptors, + ); +} + +/// A collection of data and associated behaviors that accomplish a function or +/// feature of a device. +base class GATTService extends GATTAttribute { + /// A Boolean value that indicates whether the type of service is primary or + /// secondary. + final bool isPrimary; + + /// A list of included services discovered in this service. + final List includedServices; + + /// A list of characteristics discovered in this service. + final List characteristics; + + /// Creates a newly initialized mutable service specified by UUID and service + /// type. + GATTService({ + required super.uuid, + required this.isPrimary, + required this.includedServices, + required this.characteristics, + }); +} + +/// An object that provides additional information about a local peripheral’s +/// characteristic. +final class MutableGATTDescriptor extends GATTDescriptor { + /// The permissions of the descriptor value. + final List permissions; + + /// Creates a mutable descriptor with a specified value. + /// + /// [uuid] A 128-bit UUID that identifies the characteristic. You must use only + /// one of the two currently supported descriptor types: + /// CBUUIDCharacteristicUserDescriptionString or CBUUIDCharacteristicFormatString. + /// For more details about these descriptor types, see CBUUID. + /// + /// [permissions] The permissions of the descriptor value. + MutableGATTDescriptor({ + required super.uuid, + required this.permissions, + }); +} + +/// An object that provides additional information about a local peripheral’s +/// characteristic. +final class ImmutableGATTDescriptor extends MutableGATTDescriptor { + /// The value of the descriptor. + final Uint8List value; + + /// Creates an immutable descriptor with a specified value. + /// + /// [uuid] A 128-bit UUID that identifies the characteristic. You must use only + /// one of the two currently supported descriptor types: + /// CBUUIDCharacteristicUserDescriptionString or CBUUIDCharacteristicFormatString. + /// For more details about these descriptor types, see CBUUID. + /// + /// [value] The descriptor value to cache. You must provide a non-nil value. + /// Once published, you can’t update the value dynamically. + ImmutableGATTDescriptor({ + required super.uuid, + required this.value, + }) : super( + permissions: [ + GATTCharacteristicPermission.read, + ], + ); +} + +/// A mutable characteristic of a local peripheral’s service. +final class MutableGATTCharacteristic extends GATTCharacteristic { + /// The permissions of the characteristic value. + final List permissions; + + /// Creates a mutable characteristic with specified permissions, properties. + /// + /// [uuid] A 128-bit UUID that identifies the characteristic. + /// + /// [properties] The properties of the characteristic. + /// + /// [permissions] The permissions of the characteristic value. + MutableGATTCharacteristic({ + required super.uuid, + required super.properties, + required this.permissions, + required super.descriptors, + }); +} + +/// An immutable characteristic of a local peripheral’s service. +final class ImmutableGATTCharacteristic extends MutableGATTCharacteristic { + /// The value of the characteristic. + final Uint8List value; + + /// Creates an immutable characteristic with a specified value. + /// + /// [uuid] A 128-bit UUID that identifies the characteristic. + /// + /// [value] The characteristic value to cache. You must provide a non-nil value. + /// Once published, you can’t update the value dynamically. + ImmutableGATTCharacteristic({ + required super.uuid, + required this.value, + required super.descriptors, + }) : super( + properties: [ + GATTCharacteristicProperty.read, + ], + permissions: [ + GATTCharacteristicPermission.read, + ], + ); +} + +/// A read request that uses the Attribute Protocol (ATT). +abstract base class GATTReadRequest { + /// The zero-based index of the first byte for the read request. + final int offset; + + /// Constructs a [GATTReadRequest]. + GATTReadRequest({ + required this.offset, + }); +} + +/// A write request that uses the Attribute Protocol (ATT). +abstract base class GATTWriteRequest { + /// The zero-based index of the first byte for the write request. + final int offset; + + /// The data that the central writes to the peripheral. + final Uint8List value; + + /// Constructs a [GATTWriteRequest]. + GATTWriteRequest({ + required this.offset, + required this.value, + }); +} + +/// Values that represent the possible properties of a characteristic. +enum GATTCharacteristicProperty { + /// A property that indicates a peripheral can read the characteristic’s value. + read, + + /// A property that indicates a peripheral can write the characteristic’s value, + /// with a response to indicate that the write succeeded. + write, + + /// A property that indicates a peripheral can write the characteristic’s value, + /// without a response to indicate that the write succeeded. + writeWithoutResponse, + + /// A property that indicates the peripheral permits notifications of the + /// characteristic’s value, without a response from the central to indicate + /// receipt of the notification. + notify, + + /// A property that indicates the peripheral permits notifications of the + /// characteristic’s value, with a response from the central to indicate receipt + /// of the notification. + indicate, +} + +/// Values that represent the read, write, and encryption permissions for a +/// characteristic’s value. +enum GATTCharacteristicPermission { + /// A permission that indicates a peripheral can read the attribute’s value. + read, + + /// A permission that indicates only trusted devices can read the attribute’s + /// value. + readEncrypted, + + /// A permission that indicates a peripheral can write the attribute’s value. + write, + + /// A permission that indicates only trusted devices can write the attribute’s + /// value. + writeEncrypted, +} + +/// Values representing the possible write types to a characteristic’s value. +enum GATTCharacteristicWriteType { + /// Write a characteristic value, with a response from the peripheral to indicate + /// whether the write was successful. + withResponse, + + /// Write a characteristic value, without any response from the peripheral to + /// indicate whether the write was successful. + withoutResponse, +} + +/// The possible errors returned by a GATT server (a remote peripheral) during +/// Bluetooth low energy ATT transactions. +enum GATTError { + /// The attribute handle is invalid on this peripheral. + invalidHandle, + + /// The permissions prohibit reading the attribute’s value. + readNotPermitted, + + /// The permissions prohibit writing the attribute’s value. + writeNotPermitted, + + /// The attribute Protocol Data Unit (PDU) is invalid. + invalidPDU, + + /// Reading or writing the attribute’s value failed for lack of authentication. + insufficientAuthentication, + + /// The attribute server doesn’t support the request received from the client. + requestNotSupported, + + /// The specified offset value was past the end of the attribute’s value. + invalidOffset, + + /// Reading or writing the attribute’s value failed for lack of authorization. + insufficientAuthorization, + + /// The prepare queue is full, as a result of there being too many write requests + /// in the queue. + prepareQueueFull, + + /// The attribute wasn’t found within the specified attribute handle range. + attributeNotFound, + + /// The ATT read blob request can’t read or write the attribute. + attributeNotLong, + + /// The encryption key size used for encrypting this link is insufficient. + insufficientEncryptionKeySize, + + /// The length of the attribute’s value is invalid for the intended operation. + invalidAttributeValueLength, + + /// The ATT request encountered an unlikely error and wasn’t completed. + unlikelyError, + + /// Reading or writing the attribute’s value failed for lack of encryption. + insufficientEncryption, + + /// The attribute type isn’t a supported grouping attribute as defined by a + /// higher-layer specification. + unsupportedGroupType, + + /// Resources are insufficient to complete the ATT request. + insufficientResources, +} diff --git a/bluetooth_low_energy_platform_interface/lib/src/gatt_attribute.dart b/bluetooth_low_energy_platform_interface/lib/src/gatt_attribute.dart deleted file mode 100644 index f028aca..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/gatt_attribute.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'uuid.dart'; - -/// A representation of common aspects of services offered by a peripheral. -abstract class GattAttribute { - /// The Bluetooth-specific UUID of the attribute. - UUID get uuid; -} diff --git a/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic.dart b/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic.dart deleted file mode 100644 index 456b291..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'dart:typed_data'; - -import 'gatt_attribute.dart'; -import 'gatt_characteristic_property.dart'; -import 'gatt_descriptor.dart'; -import 'my_gatt_characteristic.dart'; -import 'my_gatt_descriptor.dart'; -import 'uuid.dart'; - -/// A characteristic of a remote peripheral’s service. -abstract class GattCharacteristic extends GattAttribute { - /// The properties of the characteristic. - List get properties; - - /// A list of the descriptors discovered in this characteristic. - List get descriptors; - - /// Constructs a [GattCharacteristic]. - factory GattCharacteristic({ - required UUID uuid, - required List properties, - 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/gatt_characteristic_property.dart b/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic_property.dart deleted file mode 100644 index b4d7ab8..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic_property.dart +++ /dev/null @@ -1,17 +0,0 @@ -/// The properity for a GATT characteristic. -enum GattCharacteristicProperty { - /// The GATT characteristic is able to read. - read, - - /// The GATT characteristic is able to write. - write, - - /// The GATT characteristic is able to write without response. - writeWithoutResponse, - - /// The GATT characteristic is able to notify. - notify, - - /// The GATT characteristic is able to indicate. - indicate, -} diff --git a/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic_write_type.dart b/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic_write_type.dart deleted file mode 100644 index 8362537..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/gatt_characteristic_write_type.dart +++ /dev/null @@ -1,9 +0,0 @@ -/// The write type for a GATT characteristic. -enum GattCharacteristicWriteType { - // Write with response - withResponse, - // Write without response - withoutResponse, - // Write with response and waiting for confirmation - // reliable, -} diff --git a/bluetooth_low_energy_platform_interface/lib/src/gatt_descriptor.dart b/bluetooth_low_energy_platform_interface/lib/src/gatt_descriptor.dart deleted file mode 100644 index 1a8dbec..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/gatt_descriptor.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'dart:typed_data'; - -import 'gatt_attribute.dart'; -import 'my_gatt_descriptor.dart'; -import 'uuid.dart'; - -/// An object that provides further information about a remote peripheral’s characteristic. -abstract class GattDescriptor extends GattAttribute { - /// Constructs a [GattDescriptor]. - factory GattDescriptor({ - required UUID uuid, - Uint8List? value, - }) => - MyGattDescriptor( - uuid: uuid, - value: value, - ); -} diff --git a/bluetooth_low_energy_platform_interface/lib/src/gatt_service.dart b/bluetooth_low_energy_platform_interface/lib/src/gatt_service.dart deleted file mode 100644 index a4f9020..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/gatt_service.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'gatt_attribute.dart'; -import 'gatt_characteristic.dart'; -import 'my_gatt_characteristic.dart'; -import 'my_gatt_service.dart'; -import 'uuid.dart'; - -/// A collection of data and associated behaviors that accomplish a function or feature of a device. -abstract class GattService extends GattAttribute { - /// A list of characteristics discovered in this service. - List get characteristics; - - /// Constructs a [GattService]. - factory GattService({ - required UUID uuid, - required List characteristics, - }) => - MyGattService( - uuid: uuid, - characteristics: characteristics.cast(), - ); -} diff --git a/bluetooth_low_energy_platform_interface/lib/src/manufacturer_specific_data.dart b/bluetooth_low_energy_platform_interface/lib/src/manufacturer_specific_data.dart index c506d6e..258e437 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/manufacturer_specific_data.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/manufacturer_specific_data.dart @@ -1,7 +1,7 @@ import 'dart:typed_data'; /// The manufacturer specific data of the peripheral -class ManufacturerSpecificData { +final class ManufacturerSpecificData { /// The manufacturer id. final int id; 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 deleted file mode 100644 index 0d29c99..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/my_bluetooth_low_energy_peer.dart +++ /dev/null @@ -1,11 +0,0 @@ -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 deleted file mode 100644 index f27009b..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/my_central.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'central.dart'; -import 'my_bluetooth_low_energy_peer.dart'; - -class MyCentral extends MyBluetoothLowEnergyPeer implements Central { - MyCentral({ - 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_gatt_attribute.dart b/bluetooth_low_energy_platform_interface/lib/src/my_gatt_attribute.dart deleted file mode 100644 index 7add4ff..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/my_gatt_attribute.dart +++ /dev/null @@ -1,19 +0,0 @@ -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 deleted file mode 100644 index 24fc16c..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/my_gatt_characteristic.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'dart:typed_data'; - -import 'gatt_characteristic.dart'; -import 'gatt_characteristic_property.dart'; -import 'my_gatt_attribute.dart'; -import 'my_gatt_descriptor.dart'; - -class MyGattCharacteristic extends MyGattAttribute - implements GattCharacteristic { - Uint8List _value; - @override - final List properties; - @override - final List descriptors; - - MyGattCharacteristic({ - required super.uuid, - required this.properties, - Uint8List? value, - required this.descriptors, - }) : _value = value?.trimGATT() ?? Uint8List(0); - - Uint8List get value => _value; - 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 deleted file mode 100644 index 5054e67..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/my_gatt_descriptor.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'dart:typed_data'; - -import 'gatt_descriptor.dart'; -import 'my_gatt_attribute.dart'; - -class MyGattDescriptor extends MyGattAttribute implements GattDescriptor { - Uint8List _value; - - MyGattDescriptor({ - required super.uuid, - Uint8List? value, - }) : _value = value?.trimGATT() ?? Uint8List(0); - - Uint8List get value => _value; - 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 deleted file mode 100644 index b1a21a0..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/my_gatt_service.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'gatt_service.dart'; -import 'my_gatt_attribute.dart'; -import 'my_gatt_characteristic.dart'; - -class MyGattService extends MyGattAttribute implements GattService { - @override - final List characteristics; - - MyGattService({ - required super.uuid, - required this.characteristics, - }); -} diff --git a/bluetooth_low_energy_platform_interface/lib/src/my_peripheral.dart b/bluetooth_low_energy_platform_interface/lib/src/my_peripheral.dart deleted file mode 100644 index 47d9d13..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/my_peripheral.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'my_bluetooth_low_energy_peer.dart'; -import 'peripheral.dart'; - -class MyPeripheral extends MyBluetoothLowEnergyPeer implements Peripheral { - MyPeripheral({ - 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/peripheral.dart b/bluetooth_low_energy_platform_interface/lib/src/peripheral.dart index 58061ad..290a511 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/peripheral.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/peripheral.dart @@ -1,4 +1,16 @@ import 'bluetooth_low_energy_peer.dart'; /// A remote peripheral device. -abstract class Peripheral extends BluetoothLowEnergyPeer {} +base class Peripheral extends BluetoothLowEnergyPeer { + Peripheral({ + 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/peripheral_event_args.dart b/bluetooth_low_energy_platform_interface/lib/src/peripheral_event_args.dart deleted file mode 100644 index ddef1c7..0000000 --- a/bluetooth_low_energy_platform_interface/lib/src/peripheral_event_args.dart +++ /dev/null @@ -1,61 +0,0 @@ -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 674e967..e24bb4b 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/peripheral_manager.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/peripheral_manager.dart @@ -1,27 +1,266 @@ 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 'peripheral_event_args.dart'; +import 'connection_state.dart'; +import 'event_args.dart'; +import 'gatt.dart'; + +/// The central connection state cahnged event arguments. +final class CentralConnectionStateChangedEventArgs extends EventArgs { + /// The central which connection state changed. + final Central central; + + /// The connection state. + final ConnectionState state; + + /// Constructs a [CentralConnectionStateChangedEventArgs]. + CentralConnectionStateChangedEventArgs( + this.central, + this.state, + ); +} + +/// The central MTU changed event arguments. +final class CentralMTUChangedEventArgs extends EventArgs { + /// The central which MTU changed. + final Central central; + + /// The MTU. + final int mtu; + + /// Constructs a [CentralMTUChangedEventArgs]. + CentralMTUChangedEventArgs( + this.central, + this.mtu, + ); +} + +/// The GATT characteristic read requested event arguments. +final class GATTCharacteristicReadRequestedEventArgs extends EventArgs { + /// The central which read this characteristic. + final Central central; + + /// The characteristic to read the value of. + final GATTCharacteristic characteristic; + + /// The read request. + final GATTReadRequest request; + + /// Constructs a [GATTCharacteristicReadRequestedEventArgs]. + GATTCharacteristicReadRequestedEventArgs( + this.central, + this.characteristic, + this.request, + ); +} + +/// The GATT characteristic write requested event arguments. +final class GATTCharacteristicWriteRequestedEventArgs extends EventArgs { + /// The central which wrote this characteristic. + final Central central; + + /// The characteristic to write the value of. + final GATTCharacteristic characteristic; + + /// The write request. + final GATTWriteRequest request; + + /// Constructs a [GATTCharacteristicWriteRequestedEventArgs]. + GATTCharacteristicWriteRequestedEventArgs( + this.central, + this.characteristic, + this.request, + ); +} + +/// The GATT characteristic notify state changed event arguments. +final class GATTCharacteristicNotifyStateChangedEventArgs extends EventArgs { + /// 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, + ); +} + +/// The GATT descriptor read requested event arguments. +final class GATTDescriptorReadRequestedEventArgs extends EventArgs { + /// The central which read this descriptor. + final Central central; + + /// The descriptor to read the value of. + final GATTDescriptor descriptor; + + /// The read request. + final GATTReadRequest request; + + /// Constructs a [GATTDescriptorReadRequestedEventArgs]. + GATTDescriptorReadRequestedEventArgs( + this.central, + this.descriptor, + this.request, + ); +} + +/// The GATT descriptor write requested event arguments. +final class GATTDescriptorWriteRequestedEventArgs extends EventArgs { + /// The central which wrote this descriptor. + final Central central; + + /// The descriptor to write the value of. + final GATTDescriptor descriptor; + + /// The write request. + final GATTWriteRequest request; + + /// Constructs a [GATTDescriptorWriteRequestedEventArgs]. + GATTDescriptorWriteRequestedEventArgs( + this.central, + this.descriptor, + this.request, + ); +} /// An object that manages and advertises peripheral services exposed by this app. -/// -/// Platform-specific implementations should implement this class to support [PeripheralManager]. -abstract class PeripheralManager extends PlatformInterface - with LoggerProvider, LoggerController +abstract interface class PeripheralManager implements BluetoothLowEnergyManager { - static final Object _token = Object(); - static PeripheralManager? _instance; - /// The default instance of [PeripheralManager] to use. - static PeripheralManager get instance { + /// Gets the instance of [PeripheralManager] to use. + factory PeripheralManager() { + final instance = PlatformPeripheralManager.instance; + if (instance != _instance) { + instance.initialize(); + _instance = instance; + } + return instance; + } + + /// Callback indicating when a remote device has been connected or disconnected. + /// + /// This event is available on Android, throws [UnsupportedError] on other + /// platforms. + Stream get connectionStateChanged; + + /// Callback indicating the MTU for a given device connection has changed. + /// + /// This callback will be invoked if a remote client has requested to change + /// the MTU for a given connection. + /// + /// This event is available on Android and Windows, throws [UnsupportedError] + /// on other platforms. + Stream get mtuChanged; + + /// Tells that the local peripheral device received an Attribute Protocol (ATT) + /// read request for a characteristic with a dynamic value. + Stream + get characteristicReadRequested; + + /// Tells that the local peripheral device received an Attribute Protocol (ATT) + /// write request for a characteristic with a dynamic value. + Stream + get characteristicWriteRequested; + + /// Tells that the peripheral manager received a characteristic’s notify changed. + Stream + get characteristicNotifyStateChanged; + + /// Tells that the local peripheral device received an Attribute Protocol (ATT) + /// read request for a descriptor with a dynamic value. + /// + /// This event is available on Android and Windows, throws [UnsupportedError] + /// on other platforms. + Stream get descriptorReadRequested; + + /// Tells that the local peripheral device received an Attribute Protocol (ATT) + /// write request for a descriptor with a dynamic value. + /// + /// This event is available on Android and Windows, throws [UnsupportedError] + /// on other platforms. + Stream get descriptorWriteRequested; + + /// Publishes a service and any of its associated characteristics and characteristic + /// descriptors to the local GATT database. + Future addService(GATTService service); + + /// Removes a specified published service from the local GATT database. + Future removeService(GATTService service); + + /// Removes all published services from the local GATT database. + Future removeAllServices(); + + /// Advertises peripheral manager data. + Future startAdvertising(Advertisement advertisement); + + /// Stops advertising peripheral manager data. + Future stopAdvertising(); + + /// The maximum amount of data, in bytes, that the central can receive in a + /// single notification or indication. + Future getMaximumNotifyLength(Central central); + + /// Responds to a read request from a connected central. + Future respondReadRequestWithValue( + GATTReadRequest request, { + required Uint8List value, + }); + + /// Responds to a read request from a connected central. + Future respondReadRequestWithError( + GATTReadRequest request, { + required GATTError error, + }); + + /// Responds to a write request from a connected central. + Future respondWriteRequest(GATTWriteRequest request); + + /// Responds to a write request from a connected central. + Future respondWriteRequestWithError( + GATTWriteRequest request, { + required GATTError error, + }); + + /// Send an updated characteristic value to one or more subscribed centrals, + /// using a notification or indication. + /// + /// [central] A central (represented by CBCentral objects) that have subscribed + /// to receive updates of the characteristic’s value. The manager ignores any + /// centrals that haven’t subscribed to the characteristic’s value. + /// + /// [characteristic] The characteristic whose value has changed. + /// + /// [value] The characteristic value you want to send via a notification or + /// indication. + Future notifyCharacteristic( + Central central, + GATTCharacteristic characteristic, { + required Uint8List value, + }); +} + +/// Platform-specific implementations should implement this class to support +/// [PlatformPeripheralManager]. +abstract base class PlatformPeripheralManager + extends PlatformBluetoothLowEnergyManager implements PeripheralManager { + static final Object _token = Object(); + + static PlatformPeripheralManager? _instance; + + /// The default instance of [PlatformPeripheralManager] to use. + static PlatformPeripheralManager get instance { final instance = _instance; if (instance == null) { throw UnimplementedError( @@ -31,50 +270,13 @@ abstract class PeripheralManager extends PlatformInterface } /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [PeripheralManager] when + /// platform-specific class that extends [PlatformPeripheralManager] when /// they register themselves. - static set instance(PeripheralManager instance) { + static set instance(PlatformPeripheralManager 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 characteristicWritten; - - /// Tells that the peripheral manager received a characteristic’s notify changed. - Stream - get characteristicNotifyStateChanged; - - /// Publishes a service and any of its associated characteristics and characteristic descriptors to the local GATT database. - Future addService(GattService service); - - /// Removes a specified published service from the local GATT database. - Future removeService(GattService service); - - /// Removes all published services from the local GATT database. - Future clearServices(); - - /// Advertises peripheral manager data. - Future startAdvertising(Advertisement advertisement); - - /// Stops advertising peripheral manager data. - Future stopAdvertising(); - - /// Retrieves the value of a specified characteristic. - Future readCharacteristic(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, - }); + /// Constructs a [PlatformPeripheralManager]. + PlatformPeripheralManager() : super(token: _token); } diff --git a/bluetooth_low_energy_platform_interface/lib/src/uuid.dart b/bluetooth_low_energy_platform_interface/lib/src/uuid.dart index 49ade9d..6e69e47 100644 --- a/bluetooth_low_energy_platform_interface/lib/src/uuid.dart +++ b/bluetooth_low_energy_platform_interface/lib/src/uuid.dart @@ -1,5 +1,5 @@ /// 128 bit universally unique identifier used in Bluetooth. -class UUID { +final class UUID { /// The value of the UUID in 16 bytes. final List value; @@ -46,7 +46,8 @@ class UUID { 0xfb ]; - /// Creates a new UUID from the string format encoding (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx where xx is a hexadecimal number). + /// Creates a new UUID from the string format encoding (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + /// where xx is a hexadecimal number). factory UUID.fromString(String value) { // 16 or 32 bits UUID. if (value.length == 4 || value.length == 8) { @@ -96,11 +97,17 @@ class UUID { } /// Creates a new UUID form MAC address. - factory UUID.fromAddress(String address) { - final node = address.splitMapJoin( - ':', - onMatch: (m) => '', - ); + /// + /// The address type must be String or int. + factory UUID.fromAddress(Object address) { + final node = address is String + ? address.splitMapJoin( + ':', + onMatch: (m) => '', + ) + : address is int + ? (address & 0xFFFFFFFFFFFF).toRadixString(16).padLeft(12, '0') + : throw TypeError(); // We don't know the timestamp of the bluetooth device, use nil UUID as prefix. return UUID.fromString("00000000-0000-0000-0000-$node"); } diff --git a/bluetooth_low_energy_platform_interface/pubspec.yaml b/bluetooth_low_energy_platform_interface/pubspec.yaml index 9afa4f8..540fa78 100644 --- a/bluetooth_low_energy_platform_interface/pubspec.yaml +++ b/bluetooth_low_energy_platform_interface/pubspec.yaml @@ -1,19 +1,28 @@ name: bluetooth_low_energy_platform_interface -description: A common platform interface for the bluetooth_low_energy plugin. -version: 5.0.2 +description: "A common platform interface for the bluetooth_low_energy plugin." +version: 6.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy +repository: https://github.com/yanshouwang/bluetooth_low_energy +issue_tracker: https://github.com/yanshouwang/bluetooth_low_energy/issues +topics: + - bluetooth + - bluetooth-low-energy + - ble +funding: + - https://paypal.me/yanshouwang5112 + - https://afdian.net/a/yanshouwang environment: sdk: '>=3.0.0 <4.0.0' - flutter: ">=3.0.0" + flutter: '>=3.0.0' dependencies: flutter: sdk: flutter - plugin_platform_interface: ^2.1.7 - log_service: ^1.0.0 + plugin_platform_interface: ^2.0.2 + hybrid_logging: ^1.0.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.0 + flutter_lints: ^4.0.0 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 deleted file mode 100644 index 435d6f7..0000000 --- a/bluetooth_low_energy_platform_interface/test/my_gatt_attribute_test.dart +++ /dev/null @@ -1,46 +0,0 @@ -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 index 2e1d356..98d2b92 100644 --- a/bluetooth_low_energy_platform_interface/test/uuid_test.dart +++ b/bluetooth_low_energy_platform_interface/test/uuid_test.dart @@ -3,7 +3,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { test( - 'Create UUID form MAC address with colons.', + 'UUID#fromAddress by String with colons.', () { const address = 'AA:BB:CC:DD:EE:FF'; final actual = UUID.fromAddress(address); @@ -12,7 +12,7 @@ void main() { }, ); test( - 'Create UUID form MAC address without colons.', + 'UUID#fromAddress by String without colons.', () { const address = 'AABBCCDDEEFF'; final actual = UUID.fromAddress(address); @@ -20,4 +20,13 @@ void main() { expect(actual, matcher); }, ); + test( + 'UUID#fromAddress by int.', + () { + const address = 0xAABBCCDDEEFF; + final actual = UUID.fromAddress(address); + final matcher = UUID.fromString('00000000-0000-0000-0000-AABBCCDDEEFF'); + expect(actual, matcher); + }, + ); } diff --git a/bluetooth_low_energy_windows/.gitignore b/bluetooth_low_energy_windows/.gitignore index 96486fd..ac5aa98 100644 --- a/bluetooth_low_energy_windows/.gitignore +++ b/bluetooth_low_energy_windows/.gitignore @@ -26,5 +26,4 @@ migrate_working_dir/ /pubspec.lock **/doc/api/ .dart_tool/ -.packages build/ diff --git a/bluetooth_low_energy_windows/.metadata b/bluetooth_low_energy_windows/.metadata index f382618..dda3a39 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: "d211f42860350d914a5ad8102f9ec32764dc6d06" + revision: "5dcb86f68f239346676ceb1ed1ea385bd215fba1" channel: "stable" project_type: plugin @@ -13,11 +13,11 @@ project_type: plugin migration: platforms: - platform: root - create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 - base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 - platform: windows - create_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 - base_revision: d211f42860350d914a5ad8102f9ec32764dc6d06 + create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 + base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 # User provided section diff --git a/bluetooth_low_energy_windows/CHANGELOG.md b/bluetooth_low_energy_windows/CHANGELOG.md index 712a68d..84a1bee 100644 --- a/bluetooth_low_energy_windows/CHANGELOG.md +++ b/bluetooth_low_energy_windows/CHANGELOG.md @@ -1,3 +1,64 @@ +## 6.0.0 + +* Implement `PeripheralManager` API. +* Add `CentralManager#mtuChanged` event. +* Add `serviceUUIDs` argument to `CentralManager#startDiscovery` method. +* Add `CentralManager#getMaximumWriteLength` method. +* Move `BluetoothLowEnergyManager#getState` to `BluetoothLowEnergyManager#state`. +* Move `CentralManger.instance` to factory constructor. +* Remove `BluetoothLowEnergyManager#setUp` method. +* Rewrite example with MVVM. +* Fix the issue that [`Cannot access value of empty optional`](https://github.com/yanshouwang/bluetooth_low_energy/issues/63) +* Fix known issues. + +## 6.0.0-dev.9 + +* Implement `PeripheralManager` API. +* Rewrite example with MVVM. +* Fix known issues. + +## 6.0.0-dev.8 + +* Fix example errors. + +## 6.0.0-dev.7 + +* Move organizatioin. + +## 6.0.0-dev.6 + +* Fix the issue that [`Cannot access value of empty optional`](https://github.com/yanshouwang/bluetooth_low_energy/issues/63) + +## 6.0.0-dev.5 + +* Implement new APIs. + +## 6.0.0-dev.4 + +* Add `CentralManager#getMaximumWriteLength` method. +* Add `GATTService#includedServices` field. + +## 6.0.0-dev.3 + +* Fix errors. + +## 6.0.0-dev.2 + +* Make it possible to change the `logLevel` before `initialize()`. + +## 6.0.0-dev.1 + +* Add `CentralManager#mtuChanged` event. +* Add modifiers to all classes. +* Use new capitalization rules. + +## 6.0.0-dev.0 + +* Add `serviceUUIDs` argument to `CentralManager#startDiscovery` method. +* Move `BluetoothLowEnergyManager#getState` to `BluetoothLowEnergyManager#state`. +* Move `CentralManger.instance` to factory constructor. +* Remove `BluetoothLowEnergyManager#setUp` method. + ## 5.0.3 * Change flutter minimum version to 3.0.0. diff --git a/bluetooth_low_energy_windows/LICENSE b/bluetooth_low_energy_windows/LICENSE index 752d28b..3977562 100644 --- a/bluetooth_low_energy_windows/LICENSE +++ b/bluetooth_low_energy_windows/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 yanshouwang +Copyright (c) 2024 hebei.dev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/bluetooth_low_energy_windows/example/.gitignore b/bluetooth_low_energy_windows/example/.gitignore index 24476c5..29a3a50 100644 --- a/bluetooth_low_energy_windows/example/.gitignore +++ b/bluetooth_low_energy_windows/example/.gitignore @@ -27,7 +27,6 @@ migrate_working_dir/ .dart_tool/ .flutter-plugins .flutter-plugins-dependencies -.packages .pub-cache/ .pub/ /build/ diff --git a/bluetooth_low_energy_windows/example/README.md b/bluetooth_low_energy_windows/example/README.md index 13063a6..fb07e73 100644 --- a/bluetooth_low_energy_windows/example/README.md +++ b/bluetooth_low_energy_windows/example/README.md @@ -1,6 +1,6 @@ -# bluetooth_low_energy_example +# bluetooth_low_energy_windows_example -Demonstrates how to use the bluetooth_low_energy plugin. +Demonstrates how to use the bluetooth_low_energy_windows plugin. ## Getting Started diff --git a/bluetooth_low_energy_windows/example/integration_test/plugin_integration_test.dart b/bluetooth_low_energy_windows/example/integration_test/plugin_integration_test.dart index c03e5a8..d53d2ae 100644 --- a/bluetooth_low_energy_windows/example/integration_test/plugin_integration_test.dart +++ b/bluetooth_low_energy_windows/example/integration_test/plugin_integration_test.dart @@ -6,17 +6,8 @@ // For more information about Flutter integration tests, please see // https://docs.flutter.dev/cookbook/testing/integration/introduction -// import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - // testWidgets('getPlatformVersion test', (WidgetTester tester) async { - // final BluetoothLowEnergy plugin = BluetoothLowEnergy(); - // final String? version = await plugin.getPlatformVersion(); - // // The version string depends on the host platform running the test, so - // // just assert that some non-empty string is returned. - // expect(version?.isNotEmpty, true); - // }); } diff --git a/bluetooth_low_energy_windows/example/lib/main.dart b/bluetooth_low_energy_windows/example/lib/main.dart index 30150dc..a588ab4 100644 --- a/bluetooth_low_energy_windows/example/lib/main.dart +++ b/bluetooth_low_energy_windows/example/lib/main.dart @@ -1,12 +1,10 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:developer'; -import 'dart:typed_data'; -import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; -import 'package:convert/convert.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; +import 'package:logging/logging.dart'; + +import 'router_config.dart'; void main() { runZonedGuarded(onStartUp, onCrashed); @@ -14,9 +12,7 @@ void main() { void onStartUp() async { Logger.root.onRecord.listen(onLogRecord); - WidgetsFlutterBinding.ensureInitialized(); - await CentralManager.instance.setUp(); - // await peripheralManager.setUp(); + hierarchicalLoggingEnabled = true; runApp(const MyApp()); } @@ -37,791 +33,19 @@ void onLogRecord(LogRecord record) { ); } -class MyApp extends StatefulWidget { +class MyApp extends StatelessWidget { const MyApp({super.key}); - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData.light( - useMaterial3: true, - ).copyWith( + return MaterialApp.router( + routerConfig: routerConfig, + theme: ThemeData.light().copyWith( materialTapTargetSize: MaterialTapTargetSize.padded, ), - home: const HomeView(), - routes: { - 'peripheral': (context) { - final route = ModalRoute.of(context); - final eventArgs = route!.settings.arguments as DiscoveredEventArgs; - return PeripheralView( - eventArgs: eventArgs, - ); - }, - }, - ); - } -} - -class HomeView extends StatelessWidget { - const HomeView({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - body: buildBody(context), - ); - } - - Widget buildBody(BuildContext context) { - return const ScannerView(); - } -} - -class ScannerView extends StatefulWidget { - const ScannerView({super.key}); - - @override - State createState() => _ScannerViewState(); -} - -class _ScannerViewState extends State { - late final ValueNotifier state; - late final ValueNotifier discovering; - late final ValueNotifier> discoveredEventArgs; - late final StreamSubscription stateChangedSubscription; - late final StreamSubscription discoveredSubscription; - - @override - void initState() { - super.initState(); - state = ValueNotifier(BluetoothLowEnergyState.unknown); - discovering = ValueNotifier(false); - discoveredEventArgs = ValueNotifier([]); - stateChangedSubscription = CentralManager.instance.stateChanged.listen( - (eventArgs) { - state.value = eventArgs.state; - }, - ); - discoveredSubscription = CentralManager.instance.discovered.listen( - (eventArgs) { - final items = discoveredEventArgs.value; - final i = items.indexWhere( - (item) => item.peripheral == eventArgs.peripheral, - ); - if (i < 0) { - discoveredEventArgs.value = [...items, eventArgs]; - } else { - items[i] = eventArgs; - discoveredEventArgs.value = [...items]; - } - }, - ); - setUp(); - } - - void setUp() async { - state.value = await CentralManager.instance.getState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), - ); - } - - PreferredSizeWidget buildAppBar(BuildContext context) { - return AppBar( - title: const Text('Scanner'), - actions: [ - ValueListenableBuilder( - valueListenable: state, - builder: (context, state, child) { - return ValueListenableBuilder( - valueListenable: discovering, - builder: (context, discovering, child) { - return TextButton( - onPressed: state == BluetoothLowEnergyState.poweredOn - ? () async { - if (discovering) { - await stopDiscovery(); - } else { - await startDiscovery(); - } - } - : null, - child: Text( - discovering ? 'END' : 'BEGIN', - ), - ); - }, - ); - }, - ), - ], - ); - } - - Future startDiscovery() async { - discoveredEventArgs.value = []; - await CentralManager.instance.startDiscovery(); - discovering.value = true; - } - - Future stopDiscovery() async { - await CentralManager.instance.stopDiscovery(); - discovering.value = false; - } - - Widget buildBody(BuildContext context) { - return ValueListenableBuilder( - valueListenable: discoveredEventArgs, - builder: (context, discoveredEventArgs, child) { - // final items = discoveredEventArgs; - final items = discoveredEventArgs - .where((eventArgs) => eventArgs.advertisement.name != null) - .toList(); - return ListView.separated( - itemBuilder: (context, i) { - final theme = Theme.of(context); - final item = items[i]; - final uuid = item.peripheral.uuid; - final rssi = item.rssi; - final advertisement = item.advertisement; - final name = advertisement.name; - return ListTile( - onTap: () async { - final discovering = this.discovering.value; - if (discovering) { - await stopDiscovery(); - } - if (!mounted) { - throw UnimplementedError(); - } - await Navigator.of(context).pushNamed( - 'peripheral', - arguments: item, - ); - if (discovering) { - await startDiscovery(); - } - }, - onLongPress: () async { - await showModalBottomSheet( - context: context, - builder: (context) { - return BottomSheet( - onClosing: () {}, - clipBehavior: Clip.antiAlias, - builder: (context) { - final manufacturerSpecificData = - advertisement.manufacturerSpecificData; - return ListView.separated( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 40.0, - ), - itemBuilder: (context, i) { - const idWidth = 80.0; - if (i == 0) { - return const Row( - children: [ - SizedBox( - width: idWidth, - child: Text('ID'), - ), - Expanded( - child: Text('DATA'), - ), - ], - ); - } else { - final id = - '0x${manufacturerSpecificData!.id.toRadixString(16).padLeft(4, '0')}'; - final value = - hex.encode(manufacturerSpecificData.data); - return Row( - children: [ - SizedBox( - width: idWidth, - child: Text(id), - ), - Expanded( - child: Text(value), - ), - ], - ); - } - }, - separatorBuilder: (context, i) { - return const Divider(); - }, - itemCount: manufacturerSpecificData == null ? 1 : 2, - ); - }, - ); - }, - ); - }, - title: Text(name ?? 'N/A'), - subtitle: Text( - '$uuid', - style: theme.textTheme.bodySmall, - softWrap: false, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - RssiWidget(rssi), - Text('$rssi'), - ], - ), - ); - }, - separatorBuilder: (context, i) { - return const Divider( - height: 0.0, - ); - }, - itemCount: items.length, - ); - }, - ); - } - - @override - void dispose() { - super.dispose(); - stateChangedSubscription.cancel(); - discoveredSubscription.cancel(); - state.dispose(); - discovering.dispose(); - discoveredEventArgs.dispose(); - } -} - -class PeripheralView extends StatefulWidget { - final DiscoveredEventArgs eventArgs; - - const PeripheralView({ - super.key, - required this.eventArgs, - }); - - @override - State createState() => _PeripheralViewState(); -} - -class _PeripheralViewState extends 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> logs; - late final TextEditingController writeController; - late final StreamSubscription connectionStateChangedSubscription; - late final StreamSubscription characteristicNotifiedSubscription; - - @override - void initState() { - super.initState(); - eventArgs = widget.eventArgs; - connectionState = ValueNotifier(false); - services = ValueNotifier([]); - characteristics = ValueNotifier([]); - service = ValueNotifier(null); - characteristic = ValueNotifier(null); - writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); - logs = ValueNotifier([]); - writeController = TextEditingController(); - connectionStateChangedSubscription = - CentralManager.instance.connectionStateChanged.listen( - (eventArgs) { - if (eventArgs.peripheral != this.eventArgs.peripheral) { - return; - } - final connectionState = eventArgs.connectionState; - this.connectionState.value = connectionState; - if (!connectionState) { - services.value = []; - characteristics.value = []; - service.value = null; - characteristic.value = null; - logs.value = []; - } - }, - ); - characteristicNotifiedSubscription = - CentralManager.instance.characteristicNotified.listen( - (eventArgs) { - // final characteristic = this.characteristic.value; - // if (eventArgs.characteristic != characteristic) { - // return; - // } - const type = LogType.notify; - final log = Log(type, eventArgs.value); - logs.value = [ - ...logs.value, - log, - ]; - }, - ); - } - - @override - Widget build(BuildContext context) { - return PopScope( - onPopInvoked: (didPop) async { - if (connectionState.value) { - final peripheral = eventArgs.peripheral; - await CentralManager.instance.disconnect(peripheral); - } - }, - child: Scaffold( - appBar: buildAppBar(context), - body: buildBody(context), + darkTheme: ThemeData.dark().copyWith( + materialTapTargetSize: MaterialTapTargetSize.padded, ), ); } - - PreferredSizeWidget buildAppBar(BuildContext context) { - final title = eventArgs.advertisement.name ?? ''; - return AppBar( - title: Text(title), - actions: [ - ValueListenableBuilder( - valueListenable: connectionState, - builder: (context, state, child) { - return TextButton( - onPressed: () async { - final peripheral = eventArgs.peripheral; - if (state) { - await CentralManager.instance.disconnect(peripheral); - } else { - await CentralManager.instance.connect(peripheral); - services.value = - await CentralManager.instance.discoverGATT(peripheral); - } - }, - child: Text(state ? 'DISCONNECT' : 'CONNECT'), - ); - }, - ), - ], - ); - } - - Widget buildBody(BuildContext context) { - final theme = Theme.of(context); - return Container( - margin: const EdgeInsets.symmetric( - horizontal: 16.0, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ValueListenableBuilder( - valueListenable: services, - builder: (context, services, child) { - final items = services.map((service) { - return DropdownMenuItem( - value: service, - child: Text( - '${service.uuid}', - style: theme.textTheme.bodyMedium, - ), - ); - }).toList(); - return ValueListenableBuilder( - valueListenable: service, - builder: (context, service, child) { - return DropdownButton( - isExpanded: true, - items: items, - hint: const Text('CHOOSE A SERVICE'), - value: service, - onChanged: (service) async { - this.service.value = service; - characteristic.value = null; - if (service == null) { - return; - } - characteristics.value = service.characteristics; - }, - ); - }, - ); - }, - ), - ValueListenableBuilder( - valueListenable: characteristics, - builder: (context, characteristics, child) { - final items = characteristics.map((characteristic) { - return DropdownMenuItem( - value: characteristic, - child: Text( - '${characteristic.uuid}', - style: theme.textTheme.bodyMedium, - ), - ); - }).toList(); - return ValueListenableBuilder( - valueListenable: characteristic, - builder: (context, characteristic, child) { - return DropdownButton( - isExpanded: true, - items: items, - 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; - } - }, - ); - }, - ); - }, - ), - Expanded( - child: 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 = 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, - ); - }, - ), - ), - 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: [ - ElevatedButton( - onPressed: characteristic != null && canNotify - ? () async { - await CentralManager.instance - .setCharacteristicNotifyState( - characteristic, - state: true, - ); - } - : null, - child: const Text('NOTIFY'), - ), - const SizedBox(width: 8.0), - ElevatedButton( - onPressed: characteristic != null && canRead - ? () async { - final value = await CentralManager.instance - .readCharacteristic(characteristic); - const type = LogType.read; - final log = Log(type, value); - logs.value = [...logs.value, log]; - } - : null, - child: const Text('READ'), - ) - ], - ), - ), - 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), - ], - ); - }, - ), - ], - ), - ); - } - - @override - void dispose() { - super.dispose(); - connectionStateChangedSubscription.cancel(); - characteristicNotifiedSubscription.cancel(); - connectionState.dispose(); - services.dispose(); - characteristics.dispose(); - service.dispose(); - characteristic.dispose(); - writeType.dispose(); - logs.dispose(); - writeController.dispose(); - } -} - -class Log { - final DateTime time; - final LogType type; - final Uint8List value; - final String? detail; - - Log( - this.type, - this.value, [ - this.detail, - ]) : time = DateTime.now(); - - @override - String toString() { - final type = this.type.toString().split('.').last; - final formatter = DateFormat.Hms(); - final time = formatter.format(this.time); - final message = hex.encode(value); - if (detail == null) { - return '[$type]$time: $message'; - } else { - return '[$type]$time: $message /* $detail */'; - } - } -} - -enum LogType { - read, - write, - notify, - error, -} - -class RssiWidget extends StatelessWidget { - final int rssi; - - const RssiWidget( - this.rssi, { - super.key, - }); - - @override - Widget build(BuildContext context) { - final IconData icon; - if (rssi > -70) { - icon = Icons.wifi_rounded; - } else if (rssi > -100) { - icon = Icons.wifi_2_bar_rounded; - } else { - icon = Icons.wifi_1_bar_rounded; - } - return Icon(icon); - } } diff --git a/bluetooth_low_energy_windows/example/lib/models.dart b/bluetooth_low_energy_windows/example/lib/models.dart new file mode 100644 index 0000000..627f4d2 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/models.dart @@ -0,0 +1 @@ +export 'models/log.dart'; diff --git a/bluetooth_low_energy_windows/example/lib/models/log.dart b/bluetooth_low_energy_windows/example/lib/models/log.dart new file mode 100644 index 0000000..2bcfbf7 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/models/log.dart @@ -0,0 +1,10 @@ +class Log { + final DateTime time; + final String type; + final String message; + + Log({ + required this.type, + required this.message, + }) : time = DateTime.now(); +} diff --git a/bluetooth_low_energy_windows/example/lib/router_config.dart b/bluetooth_low_energy_windows/example/lib/router_config.dart new file mode 100644 index 0000000..1d344c7 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/router_config.dart @@ -0,0 +1,85 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:collection/collection.dart'; +import 'package:go_router/go_router.dart'; + +import 'view_models.dart'; +import 'views.dart'; + +final routerConfig = GoRouter( + redirect: (context, state) { + if (state.matchedLocation == '/') { + return '/central'; + } + return null; + }, + routes: [ + StatefulShellRoute( + // builder: (context, state, navigationShell) { + // return HomeView(navigationShell: navigationShell); + // }, + builder: (context, state, navigationShell) => navigationShell, + navigatorContainerBuilder: (context, navigationShell, children) { + final navigators = children.mapIndexed( + (index, element) { + if (index == 0) { + return ViewModelBinding( + viewBuilder: (context) => element, + viewModelBuilder: (context) => CentralManagerViewModel(), + ); + } else { + return element; + } + }, + ).toList(); + return HomeView( + navigationShell: navigationShell, + navigators: navigators, + ); + }, + branches: [ + StatefulShellBranch( + routes: [ + GoRoute( + path: '/central', + builder: (context, state) { + return const CentralManagerView(); + }, + routes: [ + GoRoute( + path: ':uuid', + builder: (context, state) { + final uuidValue = state.pathParameters['uuid']!; + final uuid = UUID.fromString(uuidValue); + final viewModel = + ViewModel.of(context); + final eventArgs = viewModel.discoveries.firstWhere( + (discovery) => discovery.peripheral.uuid == uuid); + return ViewModelBinding( + viewBuilder: (context) => PeripheralView(), + viewModelBuilder: (context) => + PeripheralViewModel(eventArgs), + ); + }, + ), + ], + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: '/peripheral', + builder: (context, state) { + return ViewModelBinding( + viewBuilder: (context) => const PeripheralManagerView(), + viewModelBuilder: (context) => PeripheralManagerViewModel(), + ); + }, + ), + ], + ), + ], + ), + ], +); diff --git a/bluetooth_low_energy_windows/example/lib/view_models.dart b/bluetooth_low_energy_windows/example/lib/view_models.dart new file mode 100644 index 0000000..6c0c1fc --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/view_models.dart @@ -0,0 +1,6 @@ +export 'view_models/central_manager_view_model.dart'; +export 'view_models/peripheral_view_model.dart'; +export 'view_models/service_view_model.dart'; +export 'view_models/characteristic_view_model.dart'; +export 'view_models/descriptor_view_model.dart'; +export 'view_models/peripheral_manager_view_model.dart'; diff --git a/bluetooth_low_energy_windows/example/lib/view_models/central_manager_view_model.dart b/bluetooth_low_energy_windows/example/lib/view_models/central_manager_view_model.dart new file mode 100644 index 0000000..4d64938 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/view_models/central_manager_view_model.dart @@ -0,0 +1,67 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:logging/logging.dart'; + +class CentralManagerViewModel extends ViewModel { + final CentralManager _manager; + final List _discoveries; + bool _discovering; + + late final StreamSubscription _stateChangedSubscription; + late final StreamSubscription _discoveredSubscription; + + CentralManagerViewModel() + : _manager = CentralManager()..logLevel = Level.INFO, + _discoveries = [], + _discovering = false { + _stateChangedSubscription = _manager.stateChanged.listen((eventArgs) { + notifyListeners(); + }); + _discoveredSubscription = _manager.discovered.listen((eventArgs) { + final peripheral = eventArgs.peripheral; + final index = _discoveries.indexWhere((i) => i.peripheral == peripheral); + if (index < 0) { + _discoveries.add(eventArgs); + } else { + _discoveries[index] = eventArgs; + } + notifyListeners(); + }); + } + + BluetoothLowEnergyState get state => _manager.state; + bool get discovering => _discovering; + List get discoveries => _discoveries; + + Future startDiscovery({ + List? serviceUUIDs, + }) async { + if (_discovering) { + return; + } + _discoveries.clear(); + await _manager.startDiscovery( + serviceUUIDs: serviceUUIDs, + ); + _discovering = true; + notifyListeners(); + } + + Future stopDiscovery() async { + if (!_discovering) { + return; + } + await _manager.stopDiscovery(); + _discovering = false; + notifyListeners(); + } + + @override + void dispose() { + _stateChangedSubscription.cancel(); + _discoveredSubscription.cancel(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/view_models/characteristic_view_model.dart b/bluetooth_low_energy_windows/example/lib/view_models/characteristic_view_model.dart new file mode 100644 index 0000000..e97c4b9 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/view_models/characteristic_view_model.dart @@ -0,0 +1,145 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_windows_example/models.dart'; +import 'package:clover/clover.dart'; + +import 'descriptor_view_model.dart'; + +class CharacteristicViewModel extends ViewModel { + final CentralManager _manager; + final Peripheral _peripheral; + final GATTCharacteristic _characteristic; + final List _descriptorViewModels; + final List _logs; + + GATTCharacteristicWriteType _writeType; + bool _notifyState; + + late final StreamSubscription _characteristicNotifiedSubscription; + + CharacteristicViewModel({ + required CentralManager manager, + required Peripheral peripheral, + required GATTCharacteristic characteristic, + }) : _manager = manager, + _peripheral = peripheral, + _characteristic = characteristic, + _descriptorViewModels = characteristic.descriptors + .map((descriptor) => DescriptorViewModel(descriptor)) + .toList(), + _logs = [], + _writeType = GATTCharacteristicWriteType.withResponse, + _notifyState = false { + if (!canWrite && canWriteWithoutResponse) { + _writeType = GATTCharacteristicWriteType.withoutResponse; + } + _characteristicNotifiedSubscription = + _manager.characteristicNotified.listen((eventArgs) { + if (eventArgs.characteristic != _characteristic) { + return; + } + final log = Log( + type: 'Notified', + message: '[${eventArgs.value.length}] ${eventArgs.value}', + ); + _logs.add(log); + notifyListeners(); + }); + } + + UUID get uuid => _characteristic.uuid; + bool get canRead => + _characteristic.properties.contains(GATTCharacteristicProperty.read); + bool get canWrite => + _characteristic.properties.contains(GATTCharacteristicProperty.write); + bool get canWriteWithoutResponse => _characteristic.properties + .contains(GATTCharacteristicProperty.writeWithoutResponse); + bool get canNotify => + _characteristic.properties.contains(GATTCharacteristicProperty.notify) || + _characteristic.properties.contains(GATTCharacteristicProperty.indicate); + List get descriptorViewModels => _descriptorViewModels; + List get logs => _logs; + GATTCharacteristicWriteType get writeType => _writeType; + bool get notifyState => _notifyState; + + Future read() async { + final value = await _manager.readCharacteristic( + _peripheral, + _characteristic, + ); + final log = Log( + type: 'Read', + message: '[${value.length}] $value', + ); + _logs.add(log); + notifyListeners(); + } + + void setWriteType(GATTCharacteristicWriteType type) { + if (type == GATTCharacteristicWriteType.withResponse && !canWrite) { + throw ArgumentError.value(type); + } + if (type == GATTCharacteristicWriteType.withoutResponse && + !canWriteWithoutResponse) { + throw ArgumentError.value(type); + } + _writeType = type; + notifyListeners(); + } + + Future write(Uint8List value) async { + // Fragments the value by maximumWriteLength. + final fragmentSize = await _manager.getMaximumWriteLength( + _peripheral, + type: writeType, + ); + var start = 0; + while (start < value.length) { + final end = start + fragmentSize; + final fragmentedValue = + end < value.length ? value.sublist(start, end) : value.sublist(start); + final type = writeType; + await _manager.writeCharacteristic( + _peripheral, + _characteristic, + value: fragmentedValue, + type: type, + ); + final log = Log( + type: type == GATTCharacteristicWriteType.withResponse + ? 'Write' + : 'Write without response', + message: '[${value.length}] $value', + ); + _logs.add(log); + notifyListeners(); + start = end; + } + } + + Future setNotifyState(bool state) async { + await _manager.setCharacteristicNotifyState( + _peripheral, + _characteristic, + state: state, + ); + _notifyState = state; + notifyListeners(); + } + + void clearLogs() { + _logs.clear(); + notifyListeners(); + } + + @override + void dispose() { + _characteristicNotifiedSubscription.cancel(); + for (var descriptorViewModel in descriptorViewModels) { + descriptorViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/view_models/descriptor_view_model.dart b/bluetooth_low_energy_windows/example/lib/view_models/descriptor_view_model.dart new file mode 100644 index 0000000..a03bce4 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/view_models/descriptor_view_model.dart @@ -0,0 +1,10 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; + +class DescriptorViewModel extends ViewModel { + final GATTDescriptor _descriptor; + + DescriptorViewModel(this._descriptor); + + UUID get uuid => _descriptor.uuid; +} diff --git a/bluetooth_low_energy_windows/example/lib/view_models/peripheral_manager_view_model.dart b/bluetooth_low_energy_windows/example/lib/view_models/peripheral_manager_view_model.dart new file mode 100644 index 0000000..9a87c94 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/view_models/peripheral_manager_view_model.dart @@ -0,0 +1,162 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_windows_example/models.dart'; +import 'package:clover/clover.dart'; +import 'package:logging/logging.dart'; + +class PeripheralManagerViewModel extends ViewModel { + final PeripheralManager _manager; + final List _logs; + bool _advertising; + + late final StreamSubscription _stateChangedSubscription; + late final StreamSubscription _characteristicReadRequestedSubscription; + late final StreamSubscription _characteristicWriteRequestedSubscription; + late final StreamSubscription _characteristicNotifyStateChangedSubscription; + + PeripheralManagerViewModel() + : _manager = PeripheralManager()..logLevel = Level.INFO, + _logs = [], + _advertising = false { + _stateChangedSubscription = _manager.stateChanged.listen((eventArgs) { + notifyListeners(); + }); + _characteristicReadRequestedSubscription = + _manager.characteristicReadRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final log = Log( + type: 'Characteristic read requested', + message: '${central.uuid}, ${characteristic.uuid}, $offset', + ); + _logs.add(log); + notifyListeners(); + final elements = List.generate(100, (i) => i % 256); + final value = Uint8List.fromList(elements); + final trimmedValue = value.sublist(offset); + await _manager.respondReadRequestWithValue( + request, + value: trimmedValue, + ); + }); + _characteristicWriteRequestedSubscription = + _manager.characteristicWriteRequested.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final request = eventArgs.request; + final offset = request.offset; + final value = request.value; + final log = Log( + type: 'Characteristic write requested', + message: + '[${value.length}] ${central.uuid}, ${characteristic.uuid}, $offset, $value', + ); + _logs.add(log); + notifyListeners(); + await _manager.respondWriteRequest(request); + }); + _characteristicNotifyStateChangedSubscription = + _manager.characteristicNotifyStateChanged.listen((eventArgs) async { + final central = eventArgs.central; + final characteristic = eventArgs.characteristic; + final state = eventArgs.state; + final log = Log( + type: 'Characteristic notify state changed', + message: '${central.uuid}, ${characteristic.uuid}, $state', + ); + _logs.add(log); + notifyListeners(); + // Write someting to the central when notify started. + if (state) { + final maximumNotifyLength = + await _manager.getMaximumNotifyLength(central); + final elements = List.generate(maximumNotifyLength, (i) => i % 256); + final value = Uint8List.fromList(elements); + await _manager.notifyCharacteristic( + central, + characteristic, + value: value, + ); + } + }); + } + + BluetoothLowEnergyState get state => _manager.state; + bool get advertising => _advertising; + List get logs => _logs; + + Future startAdvertising() async { + if (_advertising) { + return; + } + await _manager.removeAllServices(); + final elements = List.generate(100, (i) => i % 256); + final value = Uint8List.fromList(elements); + final service = GATTService( + uuid: UUID.short(100), + isPrimary: true, + includedServices: [], + characteristics: [ + GATTCharacteristic.immutable( + uuid: UUID.short(200), + value: value, + descriptors: [], + ), + GATTCharacteristic.mutable( + uuid: UUID.short(201), + properties: [ + GATTCharacteristicProperty.read, + GATTCharacteristicProperty.write, + GATTCharacteristicProperty.writeWithoutResponse, + GATTCharacteristicProperty.notify, + GATTCharacteristicProperty.indicate, + ], + permissions: [ + GATTCharacteristicPermission.read, + GATTCharacteristicPermission.write, + ], + descriptors: [], + ), + ], + ); + await _manager.addService(service); + final advertisement = Advertisement( + manufacturerSpecificData: [ + ManufacturerSpecificData( + id: 0x2e19, + data: Uint8List.fromList([0x01, 0x02, 0x03]), + ) + ], + ); + await _manager.startAdvertising(advertisement); + _advertising = true; + notifyListeners(); + } + + Future stopAdvertising() async { + if (!_advertising) { + return; + } + await _manager.stopAdvertising(); + _advertising = false; + notifyListeners(); + } + + void clearLogs() { + _logs.clear(); + notifyListeners(); + } + + @override + void dispose() { + _stateChangedSubscription.cancel(); + _characteristicReadRequestedSubscription.cancel(); + _characteristicWriteRequestedSubscription.cancel(); + _characteristicNotifyStateChangedSubscription.cancel(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/view_models/peripheral_view_model.dart b/bluetooth_low_energy_windows/example/lib/view_models/peripheral_view_model.dart new file mode 100644 index 0000000..52c47e0 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/view_models/peripheral_view_model.dart @@ -0,0 +1,83 @@ +import 'dart:async'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; +import 'package:hybrid_logging/hybrid_logging.dart'; + +import 'service_view_model.dart'; + +class PeripheralViewModel extends ViewModel with TypeLogger { + final CentralManager _manager; + final Peripheral _peripheral; + final String? _name; + bool _connected; + List _serviceViewModels; + + late final StreamSubscription _connectionStateChangedSubscription; + late final StreamSubscription _mtuChangedChangedSubscription; + + PeripheralViewModel(DiscoveredEventArgs eventArgs) + : _manager = CentralManager(), + _peripheral = eventArgs.peripheral, + _name = eventArgs.advertisement.name, + _connected = false, + _serviceViewModels = [] { + _connectionStateChangedSubscription = + _manager.connectionStateChanged.listen((eventArgs) { + if (eventArgs.peripheral != _peripheral) { + return; + } + if (eventArgs.state == ConnectionState.connected) { + _connected = true; + } else { + _connected = false; + _serviceViewModels = []; + } + notifyListeners(); + }); + _mtuChangedChangedSubscription = _manager.mtuChanged.listen((eventArgs) { + if (eventArgs.peripheral != _peripheral) { + return; + } + logger.info('MTU chanaged: ${eventArgs.mtu}'); + }); + } + + UUID get uuid => _peripheral.uuid; + String? get name => _name; + bool get connected => _connected; + List get serviceViewModels => _serviceViewModels; + + Future connect() async { + await _manager.connect(_peripheral); + } + + Future disconnect() async { + await _manager.disconnect(_peripheral); + } + + Future discoverGATT() async { + final services = await _manager.discoverGATT(_peripheral); + _serviceViewModels = services + .map((service) => ServiceViewModel( + manager: _manager, + peripheral: _peripheral, + service: service, + )) + .toList(); + notifyListeners(); + } + + @override + void dispose() { + if (connected) { + disconnect(); + } + _connectionStateChangedSubscription.cancel(); + _mtuChangedChangedSubscription.cancel(); + for (var serviceViewModel in serviceViewModels) { + serviceViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/view_models/service_view_model.dart b/bluetooth_low_energy_windows/example/lib/view_models/service_view_model.dart new file mode 100644 index 0000000..0dfd888 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/view_models/service_view_model.dart @@ -0,0 +1,46 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:clover/clover.dart'; + +import 'characteristic_view_model.dart'; + +class ServiceViewModel extends ViewModel { + final GATTService _service; + + final List _includedServiceViewModels; + final List _characteristicViewModels; + + ServiceViewModel({ + required CentralManager manager, + required Peripheral peripheral, + required GATTService service, + }) : _service = service, + _includedServiceViewModels = service.includedServices + .map((service) => ServiceViewModel( + manager: manager, + peripheral: peripheral, + service: service, + )) + .toList(), + _characteristicViewModels = service.characteristics + .map((characteristic) => CharacteristicViewModel( + manager: manager, + peripheral: peripheral, + characteristic: characteristic, + )) + .toList(); + + UUID get uuid => _service.uuid; + bool get isPrimary => _service.isPrimary; + List get includedServiceViewModels => + _includedServiceViewModels; + List get characteristicViewModels => + _characteristicViewModels; + + @override + void dispose() { + for (var characteristicViewModel in characteristicViewModels) { + characteristicViewModel.dispose(); + } + super.dispose(); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/views.dart b/bluetooth_low_energy_windows/example/lib/views.dart new file mode 100644 index 0000000..b0727cb --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/views.dart @@ -0,0 +1,4 @@ +export 'views/home_view.dart'; +export 'views/central_manager_view.dart'; +export 'views/peripheral_manager_view.dart'; +export 'views/peripheral_view.dart'; diff --git a/bluetooth_low_energy_windows/example/lib/views/advertisement_view.dart b/bluetooth_low_energy_windows/example/lib/views/advertisement_view.dart new file mode 100644 index 0000000..2082064 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/views/advertisement_view.dart @@ -0,0 +1,69 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:convert/convert.dart'; +import 'package:flutter/material.dart'; + +class AdvertisementView extends StatelessWidget { + final Advertisement advertisement; + + const AdvertisementView({ + super.key, + required this.advertisement, + }); + + @override + Widget build(BuildContext context) { + final manufacturerSpecificData = advertisement.manufacturerSpecificData; + return LayoutBuilder( + builder: (context, constraints) { + const idWidth = 100.0; + final valueWidth = constraints.maxWidth - idWidth - 16.0 * 2.0; + return DataTable( + columnSpacing: 0.0, + horizontalMargin: 16.0, + columns: [ + DataColumn( + label: Container( + width: idWidth, + alignment: Alignment.center, + child: const Text('Id'), + ), + ), + DataColumn( + label: Container( + width: valueWidth, + alignment: Alignment.center, + child: const Text('Value'), + ), + ), + ], + rows: manufacturerSpecificData.map((item) { + final id = '0x${item.id.toRadixString(16).padLeft(4, '0')}'; + final value = hex.encode(item.data); + return DataRow( + cells: [ + DataCell( + Container( + width: idWidth, + alignment: Alignment.center, + child: Text(id), + ), + ), + DataCell( + Container( + width: valueWidth, + alignment: Alignment.center, + child: Text( + value, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ); + }).toList(), + ); + }, + ); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/views/central_manager_view.dart b/bluetooth_low_energy_windows/example/lib/views/central_manager_view.dart new file mode 100644 index 0000000..cf584cf --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/views/central_manager_view.dart @@ -0,0 +1,117 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_windows_example/view_models.dart'; +import 'package:bluetooth_low_energy_windows_example/widgets.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import 'advertisement_view.dart'; + +class CentralManagerView extends StatelessWidget { + const CentralManagerView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final discovering = viewModel.discovering; + return Scaffold( + appBar: AppBar( + title: const Text('Central Manager'), + actions: [ + TextButton( + onPressed: state == BluetoothLowEnergyState.poweredOn + ? () async { + if (discovering) { + await viewModel.stopDiscovery(); + } else { + await viewModel.startDiscovery(); + } + } + : null, + child: Text(discovering ? 'END' : 'BEGIN'), + ), + ], + ), + body: buildBody(context), + ); + } + + Widget buildBody(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + if (state == BluetoothLowEnergyState.poweredOn) { + final discoveries = viewModel.discoveries; + return ListView.separated( + itemBuilder: (context, index) { + final theme = Theme.of(context); + final discovery = discoveries[index]; + final uuid = discovery.peripheral.uuid; + final name = discovery.advertisement.name; + final rssi = discovery.rssi; + return ListTile( + onTap: () { + onTapDissovery(context, discovery); + }, + onLongPress: () { + onLongPressDiscovery(context, discovery); + }, + title: Text(name ?? ''), + subtitle: Text( + '$uuid', + style: theme.textTheme.bodySmall, + softWrap: false, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + RSSIIndicator(rssi), + Text('$rssi'), + ], + ), + ); + }, + separatorBuilder: (context, i) { + return const Divider( + height: 0.0, + ); + }, + itemCount: discoveries.length, + ); + } else { + return Center( + child: Text( + '$state', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + } + + void onTapDissovery( + BuildContext context, DiscoveredEventArgs discovery) async { + final viewModel = ViewModel.of(context); + if (viewModel.discovering) { + await viewModel.stopDiscovery(); + if (!context.mounted) { + return; + } + } + final uuid = discovery.peripheral.uuid; + context.go('/central/$uuid'); + } + + void onLongPressDiscovery( + BuildContext context, DiscoveredEventArgs discovery) async { + await showModalBottomSheet( + context: context, + builder: (context) { + return AdvertisementView( + advertisement: discovery.advertisement, + ); + }, + ); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/views/characteristic_tree_node_view.dart b/bluetooth_low_energy_windows/example/lib/views/characteristic_tree_node_view.dart new file mode 100644 index 0000000..1029ab0 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/views/characteristic_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_windows_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class CharacteristicTreeNodeView extends StatelessWidget { + const CharacteristicTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), + ); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/views/characteristic_view.dart b/bluetooth_low_energy_windows/example/lib/views/characteristic_view.dart new file mode 100644 index 0000000..08590bc --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/views/characteristic_view.dart @@ -0,0 +1,178 @@ +import 'dart:convert'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_windows_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +import 'log_view.dart'; + +class CharacteristicView extends StatefulWidget { + const CharacteristicView({super.key}); + + @override + State createState() => _CharacteristicViewState(); +} + +class _CharacteristicViewState extends State { + late final TextEditingController _textController; + + @override + void initState() { + super.initState(); + _textController = TextEditingController(); + } + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final logs = viewModel.logs; + return Column( + children: [ + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + return OverflowBox( + alignment: Alignment.topCenter, + maxHeight: constraints.maxHeight + 1.0, + child: Row( + children: [ + Expanded( + child: InputDecorator( + decoration: InputDecoration( + contentPadding: const EdgeInsets.all(12.0), + border: OutlineInputBorder( + borderRadius: BorderRadius.vertical( + top: const Radius.circular(12.0), + bottom: viewModel.canWrite || + viewModel.canWriteWithoutResponse + ? Radius.zero + : const Radius.circular(12.0), + ), + ), + ), + child: ListView.builder( + itemBuilder: (context, i) { + final log = logs[i]; + return LogView( + log: log, + ); + }, + itemCount: logs.length, + ), + ), + ), + Column( + children: [ + Visibility( + visible: viewModel.canNotify, + child: viewModel.notifyState + ? IconButton.filled( + onPressed: () async { + await viewModel.setNotifyState(false); + }, + icon: + const Icon(Symbols.notifications_active), + ) + : IconButton.filledTonal( + onPressed: () async { + await viewModel.setNotifyState(true); + }, + icon: const Icon(Symbols.notifications_off), + ), + ), + Visibility( + visible: viewModel.canRead, + child: IconButton.filled( + onPressed: () async { + await viewModel.read(); + }, + icon: const Icon(Symbols.arrow_downward), + ), + ), + Visibility( + visible: viewModel.canWrite || + viewModel.canWriteWithoutResponse, + child: viewModel.writeType == + GATTCharacteristicWriteType.withResponse + ? IconButton.filled( + onPressed: viewModel.canWriteWithoutResponse + ? () { + viewModel.setWriteType( + GATTCharacteristicWriteType + .withoutResponse); + } + : null, + icon: const Icon(Symbols.swap_vert), + ) + : IconButton.filledTonal( + onPressed: viewModel.canWrite + ? () { + viewModel.setWriteType( + GATTCharacteristicWriteType + .withResponse); + } + : null, + icon: const Icon(Symbols.arrow_upward), + ), + ), + IconButton.filled( + onPressed: () => viewModel.clearLogs(), + icon: const Icon(Symbols.delete), + ), + ], + ), + ], + ), + ); + }, + ), + ), + Visibility( + visible: viewModel.canWrite || viewModel.canWriteWithoutResponse, + child: Row( + children: [ + Expanded( + child: TextField( + controller: _textController, + decoration: const InputDecoration( + contentPadding: EdgeInsets.symmetric( + horizontal: 12.0, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.vertical( + bottom: Radius.circular(12.0), + ), + ), + ), + ), + ), + ValueListenableBuilder( + valueListenable: _textController, + builder: (context, tev, child) { + final text = tev.text; + return IconButton.filled( + onPressed: text.isEmpty + ? null + : () async { + final value = utf8.encode(text); + await viewModel.write(value); + }, + icon: const Icon(Symbols.pets), + ); + }, + ), + ], + ), + ), + ], + ); + } + + @override + void dispose() { + _textController.dispose(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/views/descriptor_tree_node_view.dart b/bluetooth_low_energy_windows/example/lib/views/descriptor_tree_node_view.dart new file mode 100644 index 0000000..5b2697d --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/views/descriptor_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_windows_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class DescriptorTreeNodeView extends StatelessWidget { + const DescriptorTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.tertiary, + ), + ); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/views/home_view.dart b/bluetooth_low_energy_windows/example/lib/views/home_view.dart new file mode 100644 index 0000000..9b7d146 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/views/home_view.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class HomeView extends StatefulWidget { + final StatefulNavigationShell navigationShell; + final List navigators; + + const HomeView({ + super.key, + required this.navigationShell, + required this.navigators, + }); + + @override + State createState() => _HomeViewState(); +} + +class _HomeViewState extends State { + late final PageController _controller; + + @override + void initState() { + super.initState(); + _controller = PageController( + initialPage: widget.navigationShell.currentIndex, + ); + } + + @override + Widget build(BuildContext context) { + final navigationShell = widget.navigationShell; + final navigators = widget.navigators; + return Scaffold( + // body: navigationShell, + body: PageView.builder( + controller: _controller, + onPageChanged: (index) { + // Ignore tap events. + if (index == navigationShell.currentIndex) { + return; + } + navigationShell.goBranch( + index, + initialLocation: false, + ); + }, + itemBuilder: (context, index) { + return navigators[index]; + }, + itemCount: navigators.length, + ), + bottomNavigationBar: BottomNavigationBar( + onTap: (index) { + navigationShell.goBranch( + index, + initialLocation: index == navigationShell.currentIndex, + ); + }, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.radar), + label: 'Central', + ), + BottomNavigationBarItem( + icon: Icon(Icons.sensors), + label: 'Peripheral', + ), + ], + currentIndex: widget.navigationShell.currentIndex, + ), + ); + } + + @override + void didUpdateWidget(covariant HomeView oldWidget) { + super.didUpdateWidget(oldWidget); + final navigationShell = widget.navigationShell; + final page = _controller.page ?? _controller.initialPage; + final index = page.round(); + // Ignore swipe events. + if (index == navigationShell.currentIndex) { + return; + } + _controller.animateToPage( + navigationShell.currentIndex, + duration: const Duration(milliseconds: 300), + curve: Curves.linear, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/views/log_view.dart b/bluetooth_low_energy_windows/example/lib/views/log_view.dart new file mode 100644 index 0000000..16078b8 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/views/log_view.dart @@ -0,0 +1,43 @@ +import 'package:bluetooth_low_energy_windows_example/models.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class LogView extends StatelessWidget { + final Log log; + + const LogView({ + super.key, + required this.log, + }); + + @override + Widget build(BuildContext context) { + final formatter = DateFormat.Hms(); + final time = formatter.format(log.time); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich( + TextSpan( + text: '[$time] ', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.error, + ), + children: [ + TextSpan( + text: log.type, + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + )), + ], + ), + ), + Text( + log.message, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/views/peripheral_manager_view.dart b/bluetooth_low_energy_windows/example/lib/views/peripheral_manager_view.dart new file mode 100644 index 0000000..379405b --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/views/peripheral_manager_view.dart @@ -0,0 +1,69 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; +import 'package:bluetooth_low_energy_windows_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/material_symbols_icons.dart'; + +import 'log_view.dart'; + +class PeripheralManagerView extends StatelessWidget { + const PeripheralManagerView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + final advertising = viewModel.advertising; + return Scaffold( + appBar: AppBar( + title: const Text('Peripheral Manager'), + actions: [ + TextButton( + onPressed: state == BluetoothLowEnergyState.poweredOn + ? () async { + if (advertising) { + await viewModel.stopAdvertising(); + } else { + await viewModel.startAdvertising(); + } + } + : null, + child: Text(advertising ? 'END' : 'BEGIN'), + ), + ], + ), + body: buildBody(context), + floatingActionButton: state == BluetoothLowEnergyState.poweredOn + ? FloatingActionButton( + onPressed: () => viewModel.clearLogs(), + child: const Icon(Symbols.delete), + ) + : null, + ); + } + + Widget buildBody(BuildContext context) { + final viewModel = ViewModel.of(context); + final state = viewModel.state; + if (state == BluetoothLowEnergyState.poweredOn) { + final logs = viewModel.logs; + return ListView.builder( + padding: const EdgeInsets.all(16.0), + itemBuilder: (context, i) { + final log = logs[i]; + return LogView( + log: log, + ); + }, + itemCount: logs.length, + ); + } else { + return Center( + child: Text( + '$state', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + } +} diff --git a/bluetooth_low_energy_windows/example/lib/views/peripheral_view.dart b/bluetooth_low_energy_windows/example/lib/views/peripheral_view.dart new file mode 100644 index 0000000..a645e26 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/views/peripheral_view.dart @@ -0,0 +1,107 @@ +import 'package:bluetooth_low_energy_windows_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_simple_treeview/flutter_simple_treeview.dart'; + +import 'characteristic_tree_node_view.dart'; +import 'characteristic_view.dart'; +import 'descriptor_tree_node_view.dart'; +import 'service_tree_node_view.dart'; + +class PeripheralView extends StatelessWidget { + final TreeController _treeController; + + PeripheralView({super.key}) + : _treeController = TreeController( + allNodesExpanded: false, + ); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + final connected = viewModel.connected; + final serviceViewModels = viewModel.serviceViewModels; + return Scaffold( + appBar: AppBar( + title: Text(viewModel.name ?? '${viewModel.uuid}'), + actions: [ + TextButton( + onPressed: () async { + if (connected) { + await viewModel.disconnect(); + } else { + await viewModel.connect(); + await viewModel.discoverGATT(); + } + }, + child: Text(connected ? 'DISCONNECT' : 'CONNECT'), + ), + ], + ), + body: SingleChildScrollView( + child: TreeView( + treeController: _treeController, + indent: 0.0, + nodes: _buildServiceTreeNodes(serviceViewModels), + ), + ), + ); + } + + List _buildServiceTreeNodes(List viewModels) { + return viewModels.map((viewModel) { + final includedServiceViewModels = viewModel.includedServiceViewModels; + final characteristicViewModels = viewModel.characteristicViewModels; + return TreeNode( + children: [ + ..._buildServiceTreeNodes(includedServiceViewModels), + ..._buildCharacteristicTreeNodes(characteristicViewModels), + ], + content: InheritedViewModel( + view: const ServiceTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } + + List _buildCharacteristicTreeNodes( + List viewModels) { + return viewModels.map((viewModel) { + final descriptorViewModels = viewModel.descriptorViewModels; + return TreeNode( + children: [ + TreeNode( + content: Expanded( + child: Container( + margin: const EdgeInsets.only(right: 40.0), + height: 360.0, + child: InheritedViewModel( + view: const CharacteristicView(), + viewModel: viewModel, + ), + ), + ), + ), + ..._buildDescriptorTreeNodes(descriptorViewModels), + ], + content: InheritedViewModel( + view: const CharacteristicTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } + + List _buildDescriptorTreeNodes( + List viewModels) { + return viewModels.map((viewModel) { + return TreeNode( + content: InheritedViewModel( + view: const DescriptorTreeNodeView(), + viewModel: viewModel, + ), + ); + }).toList(); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/views/service_tree_node_view.dart b/bluetooth_low_energy_windows/example/lib/views/service_tree_node_view.dart new file mode 100644 index 0000000..152c5a5 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/views/service_tree_node_view.dart @@ -0,0 +1,18 @@ +import 'package:bluetooth_low_energy_windows_example/view_models.dart'; +import 'package:clover/clover.dart'; +import 'package:flutter/material.dart'; + +class ServiceTreeNodeView extends StatelessWidget { + const ServiceTreeNodeView({super.key}); + + @override + Widget build(BuildContext context) { + final viewModel = ViewModel.of(context); + return Text( + '${viewModel.uuid}', + style: Theme.of(context).textTheme.titleSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ); + } +} diff --git a/bluetooth_low_energy_windows/example/lib/widgets.dart b/bluetooth_low_energy_windows/example/lib/widgets.dart new file mode 100644 index 0000000..baccf61 --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/widgets.dart @@ -0,0 +1 @@ +export 'widgets/rssi_indicator.dart'; diff --git a/bluetooth_low_energy_windows/example/lib/widgets/rssi_indicator.dart b/bluetooth_low_energy_windows/example/lib/widgets/rssi_indicator.dart new file mode 100644 index 0000000..ddd59df --- /dev/null +++ b/bluetooth_low_energy_windows/example/lib/widgets/rssi_indicator.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class RSSIIndicator extends StatelessWidget { + final int rssi; + + const RSSIIndicator( + this.rssi, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final IconData icon; + if (rssi > -70) { + icon = Icons.wifi_rounded; + } else if (rssi > -100) { + icon = Icons.wifi_2_bar_rounded; + } else { + icon = Icons.wifi_1_bar_rounded; + } + return Icon(icon); + } +} diff --git a/bluetooth_low_energy_windows/example/pubspec.lock b/bluetooth_low_energy_windows/example/pubspec.lock index 13529f1..4148d8c 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: "5af74eb8f97a896dfdbcff2da284d4fe5b4e2e49ebb2f46f826ac1810f66e4d7" + sha256: bc2e8d97c141653e5747bcb3cdc9fe956541b6ecc6e5f158b99a2f3abc2d946a url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "6.0.0" bluetooth_low_energy_windows: dependency: "direct main" description: path: ".." relative: true source: path - version: "5.0.3" + version: "6.0.0" boolean_selector: dependency: transitive description: @@ -48,8 +48,16 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + clover: + dependency: "direct main" + description: + name: clover + sha256: eba28e69b32f174a51c093eef098cf5ae646470322727081d5d3d8f66c786487 + url: "https://pub.dev" + source: hosted + version: "3.0.0" collection: - dependency: transitive + dependency: "direct main" description: name: collection sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a @@ -68,10 +76,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" fake_async: dependency: transitive description: @@ -102,7 +110,15 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + flutter_simple_treeview: + dependency: "direct main" + description: + name: flutter_simple_treeview + sha256: ad4978d2668dd078d3a09966832da111bef9102dd636e572c50c80133b7ff4d9 url: "https://pub.dev" source: hosted version: "3.0.2" @@ -111,11 +127,32 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter source: sdk version: "0.0.0" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "6ad5662b014c06c20fa46ab78715c96b2222a7fe4f87bf77e0289592c2539e86" + url: "https://pub.dev" + source: hosted + version: "14.1.3" + hybrid_logging: + dependency: "direct main" + description: + name: hybrid_logging + sha256: "54248d52ce68c14702a42fbc4083bac5c6be30f6afad8a41be4bbadd197b8af5" + url: "https://pub.dev" + source: hosted + version: "1.0.0" integration_test: dependency: "direct dev" description: flutter @@ -133,44 +170,36 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" - log_service: - dependency: transitive - description: - name: log_service - sha256: "21124936899e227d1779268077921d46d57456e2592d1562e455be273594e2e4" - url: "https://pub.dev" - source: hosted - version: "1.0.0" + version: "4.0.0" logging: - dependency: transitive + dependency: "direct main" description: name: logging sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" @@ -193,14 +222,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.0" + material_symbols_icons: + dependency: "direct main" + description: + name: material_symbols_icons + sha256: "520b3340d877d78f94ff1fc63e4d5667020811d0dac089ecd19373cf4538e293" + url: "https://pub.dev" + source: hosted + version: "4.2744.0" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" path: dependency: transitive description: @@ -290,10 +327,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -314,10 +351,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" webdriver: dependency: transitive description: @@ -327,5 +364,5 @@ packages: source: hosted version: "3.0.3" sdks: - dart: ">=3.2.0-0 <4.0.0" - flutter: ">=3.0.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/bluetooth_low_energy_windows/example/pubspec.yaml b/bluetooth_low_energy_windows/example/pubspec.yaml index 880a455..6dd1744 100644 --- a/bluetooth_low_energy_windows/example/pubspec.yaml +++ b/bluetooth_low_energy_windows/example/pubspec.yaml @@ -1,5 +1,5 @@ -name: bluetooth_low_energy_example -description: Demonstrates how to use the bluetooth_low_energy plugin. +name: bluetooth_low_energy_windows_example +description: "Demonstrates how to use the bluetooth_low_energy_windows plugin." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev @@ -17,10 +17,10 @@ dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^5.0.0 + bluetooth_low_energy_platform_interface: ^6.0.0 bluetooth_low_energy_windows: # When depending on this package from a real application you should use: - # bluetooth_low_energy: ^x.y.z + # bluetooth_low_energy_windows: ^x.y.z # See https://dart.dev/tools/pub/dependencies#version-constraints # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. @@ -28,9 +28,16 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - convert: ^3.1.1 + cupertino_icons: ^1.0.6 + clover: ^3.0.0 + go_router: ^14.1.3 + logging: ^1.2.0 + hybrid_logging: ^1.0.0 intl: ^0.19.0 + collection: ^1.18.0 + convert: ^3.1.1 + flutter_simple_treeview: ^3.0.2 + material_symbols_icons: ^4.2744.0 dev_dependencies: integration_test: @@ -43,7 +50,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: ^3.0.0 + flutter_lints: ^4.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/test/widget_test.dart b/bluetooth_low_energy_windows/example/test/widget_test.dart index b15e87d..2a2b819 100644 --- a/bluetooth_low_energy_windows/example/test/widget_test.dart +++ b/bluetooth_low_energy_windows/example/test/widget_test.dart @@ -5,23 +5,4 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:bluetooth_low_energy_example/main.dart'; - -void main() { - testWidgets('Verify Platform version', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that platform version is retrieved. - expect( - find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data!.startsWith('Running on:'), - ), - findsOneWidget, - ); - }); -} +void main() {} diff --git a/bluetooth_low_energy_windows/example/windows/CMakeLists.txt b/bluetooth_low_energy_windows/example/windows/CMakeLists.txt index 231ec05..7c19e6a 100644 --- a/bluetooth_low_energy_windows/example/windows/CMakeLists.txt +++ b/bluetooth_low_energy_windows/example/windows/CMakeLists.txt @@ -1,10 +1,10 @@ # Project-level configuration. cmake_minimum_required(VERSION 3.14) -project(bluetooth_low_energy_example LANGUAGES CXX) +project(bluetooth_low_energy_windows_example LANGUAGES CXX) # The name of the executable created for the application. Change this to change # the on-disk name of your application. -set(BINARY_NAME "bluetooth_low_energy_example") +set(BINARY_NAME "bluetooth_low_energy_windows_example") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. @@ -53,7 +53,7 @@ add_subdirectory(${FLUTTER_MANAGED_DIR}) add_subdirectory("runner") # Enable the test target. -set(include_bluetooth_low_energy_tests TRUE) +set(include_bluetooth_low_energy_windows_tests TRUE) # Generated plugin build rules, which manage building the plugins and adding # them to the application. @@ -89,6 +89,12 @@ if(PLUGIN_BUNDLED_LIBRARIES) COMPONENT Runtime) endif() +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 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 d051da9..37c2554 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,9 +6,9 @@ #include "generated_plugin_registrant.h" -#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { - BluetoothLowEnergyWindowsCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("BluetoothLowEnergyWindowsCApi")); + BluetoothLowEnergyWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("BluetoothLowEnergyWindowsPluginCApi")); } diff --git a/bluetooth_low_energy_windows/example/windows/runner/Runner.rc b/bluetooth_low_energy_windows/example/windows/runner/Runner.rc index cdbc0d4..78520f1 100644 --- a/bluetooth_low_energy_windows/example/windows/runner/Runner.rc +++ b/bluetooth_low_energy_windows/example/windows/runner/Runner.rc @@ -89,13 +89,13 @@ BEGIN BEGIN BLOCK "040904e4" BEGIN - VALUE "CompanyName", "dev.yanshouwang" "\0" - VALUE "FileDescription", "bluetooth_low_energy_example" "\0" + VALUE "CompanyName", "dev.hebei" "\0" + VALUE "FileDescription", "bluetooth_low_energy_windows_example" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "bluetooth_low_energy_example" "\0" - VALUE "LegalCopyright", "Copyright (C) 2023 dev.yanshouwang. All rights reserved." "\0" - VALUE "OriginalFilename", "bluetooth_low_energy_example.exe" "\0" - VALUE "ProductName", "bluetooth_low_energy_example" "\0" + VALUE "InternalName", "bluetooth_low_energy_windows_example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 dev.hebei. All rights reserved." "\0" + VALUE "OriginalFilename", "bluetooth_low_energy_windows_example.exe" "\0" + VALUE "ProductName", "bluetooth_low_energy_windows_example" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" END END diff --git a/bluetooth_low_energy_windows/example/windows/runner/main.cpp b/bluetooth_low_energy_windows/example/windows/runner/main.cpp index eafecf3..7d0f07a 100644 --- a/bluetooth_low_energy_windows/example/windows/runner/main.cpp +++ b/bluetooth_low_energy_windows/example/windows/runner/main.cpp @@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(10, 10); Win32Window::Size size(1280, 720); - if (!window.Create(L"bluetooth_low_energy_example", origin, size)) { + if (!window.Create(L"bluetooth_low_energy_windows_example", origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true); diff --git a/bluetooth_low_energy_windows/example/windows/runner/utils.cpp b/bluetooth_low_energy_windows/example/windows/runner/utils.cpp index b2b0873..3a0b465 100644 --- a/bluetooth_low_energy_windows/example/windows/runner/utils.cpp +++ b/bluetooth_low_energy_windows/example/windows/runner/utils.cpp @@ -45,13 +45,13 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) { if (utf16_string == nullptr) { return std::string(); } - int target_length = ::WideCharToMultiByte( + unsigned int target_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, nullptr, 0, nullptr, nullptr) -1; // remove the trailing null character int input_length = (int)wcslen(utf16_string); std::string utf8_string; - if (target_length <= 0 || target_length > utf8_string.max_size()) { + if (target_length == 0 || target_length > utf8_string.max_size()) { return utf8_string; } utf8_string.resize(target_length); 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 9a987e1..b840ed1 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,11 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'src/my_central_manager.dart'; +import 'src/my_peripheral_manager.dart'; -abstract class BluetoothLowEnergyWindows { +abstract class BluetoothLowEnergyWindowsPlugin { static void registerWith() { - CentralManager.instance = MyCentralManager(); + PlatformCentralManager.instance = MyCentralManager(); + PlatformPeripheralManager.instance = MyPeripheralManager(); } } diff --git a/bluetooth_low_energy_windows/lib/src/my_api.dart b/bluetooth_low_energy_windows/lib/src/my_api.dart index 1c13485..c6e33bd 100644 --- a/bluetooth_low_energy_windows/lib/src/my_api.dart +++ b/bluetooth_low_energy_windows/lib/src/my_api.dart @@ -3,11 +3,9 @@ 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'; +import 'my_central.dart'; +import 'my_gatt.dart'; +import 'my_peripheral.dart'; // ToObject extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { @@ -15,6 +13,8 @@ extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { switch (this) { case MyBluetoothLowEnergyStateArgs.unknown: return BluetoothLowEnergyState.unknown; + case MyBluetoothLowEnergyStateArgs.unsupported: + return BluetoothLowEnergyState.unsupported; case MyBluetoothLowEnergyStateArgs.disabled: return BluetoothLowEnergyState.unsupported; case MyBluetoothLowEnergyStateArgs.off: @@ -25,230 +25,276 @@ extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { } } -extension MyGattCharacteristicPropertyArgsX - on MyGattCharacteristicPropertyArgs { - GattCharacteristicProperty toProperty() { - return GattCharacteristicProperty.values[index]; +extension MyConnectionStateArgsX on MyConnectionStateArgs { + ConnectionState toState() { + switch (this) { + case MyConnectionStateArgs.disconnected: + return ConnectionState.disconnected; + case MyConnectionStateArgs.connected: + return ConnectionState.connected; + } + } +} + +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, + id: idArgs, + data: dataArgs, ); } } 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, + name: nameArgs, + serviceUUIDs: serviceUUIDsArgs + .cast() + .map((args) => UUID.fromString(args)) + .toList(), + serviceData: serviceDataArgs.cast().map( + (uuidArgs, dataArgs) { + final uuid = UUID.fromString(uuidArgs); + return MapEntry(uuid, dataArgs); + }, + ), + manufacturerSpecificData: manufacturerSpecificDataArgs + .cast() + .map((args) => args.toManufacturerSpecificData()) + .toList(), ); } } extension MyPeripheralArgsX on MyPeripheralArgs { MyPeripheral toPeripheral() { - final uuid = addressArgs.toUUID(); return MyPeripheral( - uuid: uuid, + addressArgs: addressArgs, + uuid: UUID.fromAddress(addressArgs), ); } } -extension MyGattDescriptorArgsX on MyGattDescriptorArgs { - MyGattDescriptor2 toDescriptor2(MyPeripheral peripheral) { - final handle = handleArgs; - final uuid = uuidArgs.toUUID(); - return MyGattDescriptor2( - peripheral: peripheral, - handle: handle, - uuid: uuid, +extension MyGATTDescriptorArgsX on MyGATTDescriptorArgs { + MyGATTDescriptor toDescriptor() { + return MyGATTDescriptor( + handleArgs: handleArgs, + uuid: UUID.fromString(uuidArgs), ); } } -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]; +extension MyGATTCharacteristicArgsX on MyGATTCharacteristicArgs { + MyGATTCharacteristic toCharacteristic() { + return MyGATTCharacteristic( + handleArgs: handleArgs, + uuid: UUID.fromString(uuidArgs), + 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, + }).toList(), + descriptors: descriptorsArgs + .cast() + .map((args) => args.toDescriptor()) + .toList(), ); } } -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 MyGATTServiceArgsX on MyGATTServiceArgs { + MyGATTService toService() { + return MyGATTService( + handleArgs: handleArgs, + uuid: UUID.fromString(uuidArgs), + isPrimary: isPrimaryArgs, + includedServices: includedServicesArgs + .cast() + .map((args) => args.toService()) + .toList(), + characteristics: characteristicsArgs + .cast() + .map((args) => args.toCharacteristic()) + .toList(), ); } } -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); +extension MyCentralArgsX on MyCentralArgs { + MyCentral toCentral() { + return MyCentral( + addressArgs: addressArgs, + uuid: UUID.fromAddress(addressArgs), + ); } } // ToArgs -extension GattCharacteristicPropertyX on GattCharacteristicProperty { - MyGattCharacteristicPropertyArgs toArgs() { - return MyGattCharacteristicPropertyArgs.values[index]; +extension UUIDX on UUID { + String toArgs() { + return toString(); } } -extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType { - MyGattCharacteristicWriteTypeArgs toArgs() { - return MyGattCharacteristicWriteTypeArgs.values[index]; +extension GATTCharacteristicWriteTypeX on GATTCharacteristicWriteType { + MyGATTCharacteristicWriteTypeArgs toArgs() { + return MyGATTCharacteristicWriteTypeArgs.values[index]; + } +} + +extension GATTCharacteristicPropertyX on GATTCharacteristicProperty { + MyGATTCharacteristicPropertyArgs toArgs() { + return MyGATTCharacteristicPropertyArgs.values[index]; + } +} + +extension GATTCharacteristicPermissionsX on List { + MyGATTProtectionLevelArgs? toReadArgs() { + return contains(GATTCharacteristicPermission.readEncrypted) + ? MyGATTProtectionLevelArgs.entryptionRequired + : contains(GATTCharacteristicPermission.read) + ? MyGATTProtectionLevelArgs.plain + : null; + } + + MyGATTProtectionLevelArgs? toWriteArgs() { + return contains(GATTCharacteristicPermission.writeEncrypted) + ? MyGATTProtectionLevelArgs.entryptionRequired + : contains(GATTCharacteristicPermission.write) + ? MyGATTProtectionLevelArgs.plain + : null; + } +} + +extension GATTErrorX on GATTError { + MyGATTProtocolErrorArgs toArgs() { + switch (this) { + case GATTError.invalidHandle: + return MyGATTProtocolErrorArgs.invalidHandle; + case GATTError.readNotPermitted: + return MyGATTProtocolErrorArgs.readNotPermitted; + case GATTError.writeNotPermitted: + return MyGATTProtocolErrorArgs.writeNotPermitted; + case GATTError.invalidPDU: + return MyGATTProtocolErrorArgs.invalidPDU; + case GATTError.insufficientAuthentication: + return MyGATTProtocolErrorArgs.insufficientAuthentication; + case GATTError.requestNotSupported: + return MyGATTProtocolErrorArgs.requestNotSupported; + case GATTError.invalidOffset: + return MyGATTProtocolErrorArgs.invalidOffset; + case GATTError.insufficientAuthorization: + return MyGATTProtocolErrorArgs.insufficientAuthorization; + case GATTError.prepareQueueFull: + return MyGATTProtocolErrorArgs.prepareQueueFull; + case GATTError.attributeNotFound: + return MyGATTProtocolErrorArgs.attributeNotFound; + case GATTError.attributeNotLong: + return MyGATTProtocolErrorArgs.attributeNotLong; + case GATTError.insufficientEncryptionKeySize: + return MyGATTProtocolErrorArgs.insufficientEncryptionKeySize; + case GATTError.invalidAttributeValueLength: + return MyGATTProtocolErrorArgs.invalidAttributeValueLength; + case GATTError.unlikelyError: + return MyGATTProtocolErrorArgs.unlikelyError; + case GATTError.insufficientEncryption: + return MyGATTProtocolErrorArgs.insufficientEncryption; + case GATTError.unsupportedGroupType: + return MyGATTProtocolErrorArgs.unsupportedGroupType; + case GATTError.insufficientResources: + return MyGATTProtocolErrorArgs.insufficientResources; + } } } extension ManufacturerSpecificDataX on ManufacturerSpecificData { MyManufacturerSpecificDataArgs toArgs() { - final idArgs = id; - final dataArgs = data; return MyManufacturerSpecificDataArgs( - idArgs: idArgs, - dataArgs: dataArgs, + idArgs: id, + dataArgs: data, ); } } 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(); + // When configuring the publisher object, you can't add restricted section types + // (BluetoothLEAdvertisementPublisher.Advertisement.Flags and + // BluetoothLEAdvertisementPublisher.Advertisement.LocalName). Trying to set those property values results in a + // runtime exception. You can still set the manufacturer data section, or any other sections not defined by the list of + // restrictions. + // See: https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementpublisher.advertisement?view=winrt-22621 + if (name != null) { + throw UnsupportedError('name is not supported on Windows.'); + } return MyAdvertisementArgs( - nameArgs: nameArgs, - serviceUUIDsArgs: serviceUUIDsArgs, - serviceDataArgs: serviceDataArgs, - manufacturerSpecificDataArgs: manufacturerSpecificDataArgs, + nameArgs: null, + serviceUUIDsArgs: serviceUUIDs.map((uuid) => uuid.toArgs()).toList(), + serviceDataArgs: serviceData.map((uuid, data) { + final uuidArgs = uuid.toArgs(); + return MapEntry(uuidArgs, data); + }), + manufacturerSpecificDataArgs: + manufacturerSpecificData.map((data) => data.toArgs()).toList(), ); } } -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 MutableGATTDescriptorX on MutableGATTDescriptor { + MyMutableGATTDescriptorArgs toArgs() { + return MyMutableGATTDescriptorArgs( + hashCodeArgs: hashCode, + uuidArgs: uuid.toArgs(), + valueArgs: this is ImmutableGATTDescriptor + ? (this as ImmutableGATTDescriptor).value + : null, + readProtectionLevelArgs: permissions.toReadArgs(), + writeProtectionLevelArgs: permissions.toWriteArgs(), ); } } -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 MutableGATTCharacteristicX on MutableGATTCharacteristic { + MyMutableGATTCharacteristicArgs toArgs() { + return MyMutableGATTCharacteristicArgs( + hashCodeArgs: hashCode, + uuidArgs: uuid.toArgs(), + valueArgs: this is ImmutableGATTCharacteristic + ? (this as ImmutableGATTCharacteristic).value + : null, + propertyNumbersArgs: properties.map((property) { + final propertyArgs = property.toArgs(); + return propertyArgs.index; + }).toList(), + readProtectionLevelArgs: permissions.toReadArgs(), + writeProtectionLevelArgs: permissions.toWriteArgs(), + descriptorsArgs: descriptors + .cast() + .map((descriptor) => descriptor.toArgs()) + .toList(), ); } } -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 GATTServiceX on GATTService { + MyMutableGATTServiceArgs toArgs() { + return MyMutableGATTServiceArgs( + hashCodeArgs: hashCode, + uuidArgs: uuid.toArgs(), + isPrimaryArgs: isPrimary, + includedServicesArgs: + includedServices.map((service) => service.toArgs()).toList(), + characteristicsArgs: characteristics + .cast() + .map((characteristic) => characteristic.toArgs()) + .toList(), ); } } - -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 index 3b26fa6..1e76df8 100644 --- a/bluetooth_low_energy_windows/lib/src/my_api.g.dart +++ b/bluetooth_low_energy_windows/lib/src/my_api.g.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v15.0.2), do not edit directly. +// Autogenerated from Pigeon (v19.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -27,12 +27,27 @@ List wrapResponse({Object? result, PlatformException? error, bool empty enum MyBluetoothLowEnergyStateArgs { unknown, + unsupported, disabled, off, on, } -enum MyGattCharacteristicPropertyArgs { +enum MyAdvertisementTypeArgs { + connectableUndirected, + connectableDirected, + scannableUndirected, + nonConnectableUndirected, + scanResponse, + extended, +} + +enum MyConnectionStateArgs { + disconnected, + connected, +} + +enum MyGATTCharacteristicPropertyArgs { read, write, writeWithoutResponse, @@ -40,17 +55,49 @@ enum MyGattCharacteristicPropertyArgs { indicate, } -enum MyGattCharacteristicWriteTypeArgs { +enum MyGATTCharacteristicWriteTypeArgs { withResponse, withoutResponse, } -enum MyGattCharacteristicNotifyStateArgs { +enum MyGATTCharacteristicNotifyStateArgs { none, notify, indicate, } +enum MyGATTProtectionLevelArgs { + plain, + authenticationRequired, + entryptionRequired, + encryptionAndAuthenticationRequired, +} + +enum MyGATTProtocolErrorArgs { + invalidHandle, + readNotPermitted, + writeNotPermitted, + invalidPDU, + insufficientAuthentication, + requestNotSupported, + invalidOffset, + insufficientAuthorization, + prepareQueueFull, + attributeNotFound, + attributeNotLong, + insufficientEncryptionKeySize, + invalidAttributeValueLength, + unlikelyError, + insufficientEncryption, + unsupportedGroupType, + insufficientResources, +} + +enum MyCacheModeArgs { + cached, + uncached, +} + class MyManufacturerSpecificDataArgs { MyManufacturerSpecificDataArgs({ required this.idArgs, @@ -82,7 +129,7 @@ class MyAdvertisementArgs { this.nameArgs, required this.serviceUUIDsArgs, required this.serviceDataArgs, - this.manufacturerSpecificDataArgs, + required this.manufacturerSpecificDataArgs, }); String? nameArgs; @@ -91,14 +138,14 @@ class MyAdvertisementArgs { Map serviceDataArgs; - MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs; + List manufacturerSpecificDataArgs; Object encode() { return [ nameArgs, serviceUUIDsArgs, serviceDataArgs, - manufacturerSpecificDataArgs?.encode(), + manufacturerSpecificDataArgs, ]; } @@ -108,9 +155,7 @@ class 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, + manufacturerSpecificDataArgs: (result[3] as List?)!.cast(), ); } } @@ -157,39 +202,34 @@ class MyPeripheralArgs { } } -class MyGattDescriptorArgs { - MyGattDescriptorArgs({ +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) { + static MyGATTDescriptorArgs decode(Object result) { result as List; - return MyGattDescriptorArgs( + return MyGATTDescriptorArgs( handleArgs: result[0]! as int, uuidArgs: result[1]! as String, - valueArgs: result[2] as Uint8List?, ); } } -class MyGattCharacteristicArgs { - MyGattCharacteristicArgs({ +class MyGATTCharacteristicArgs { + MyGATTCharacteristicArgs({ required this.handleArgs, required this.uuidArgs, required this.propertyNumbersArgs, @@ -202,7 +242,7 @@ class MyGattCharacteristicArgs { List propertyNumbersArgs; - List descriptorsArgs; + List descriptorsArgs; Object encode() { return [ @@ -213,21 +253,23 @@ class MyGattCharacteristicArgs { ]; } - static MyGattCharacteristicArgs decode(Object result) { + static MyGATTCharacteristicArgs decode(Object result) { result as List; - return MyGattCharacteristicArgs( + return MyGATTCharacteristicArgs( handleArgs: result[0]! as int, uuidArgs: result[1]! as String, propertyNumbersArgs: (result[2] as List?)!.cast(), - descriptorsArgs: (result[3] as List?)!.cast(), + descriptorsArgs: (result[3] as List?)!.cast(), ); } } -class MyGattServiceArgs { - MyGattServiceArgs({ +class MyGATTServiceArgs { + MyGATTServiceArgs({ required this.handleArgs, required this.uuidArgs, + required this.isPrimaryArgs, + required this.includedServicesArgs, required this.characteristicsArgs, }); @@ -235,37 +277,253 @@ class MyGattServiceArgs { String uuidArgs; - List characteristicsArgs; + bool isPrimaryArgs; + + List includedServicesArgs; + + List characteristicsArgs; Object encode() { return [ handleArgs, uuidArgs, + isPrimaryArgs, + includedServicesArgs, characteristicsArgs, ]; } - static MyGattServiceArgs decode(Object result) { + static MyGATTServiceArgs decode(Object result) { result as List; - return MyGattServiceArgs( + return MyGATTServiceArgs( handleArgs: result[0]! as int, uuidArgs: result[1]! as String, - characteristicsArgs: (result[2] as List?)!.cast(), + isPrimaryArgs: result[2]! as bool, + includedServicesArgs: (result[3] as List?)!.cast(), + characteristicsArgs: (result[4] as List?)!.cast(), ); } } -class _MyCentralManagerHostApiCodec extends StandardMessageCodec { - const _MyCentralManagerHostApiCodec(); +class MyMutableGATTDescriptorArgs { + MyMutableGATTDescriptorArgs({ + required this.hashCodeArgs, + required this.uuidArgs, + this.valueArgs, + this.readProtectionLevelArgs, + this.writeProtectionLevelArgs, + }); + + int hashCodeArgs; + + String uuidArgs; + + Uint8List? valueArgs; + + MyGATTProtectionLevelArgs? readProtectionLevelArgs; + + MyGATTProtectionLevelArgs? writeProtectionLevelArgs; + + Object encode() { + return [ + hashCodeArgs, + uuidArgs, + valueArgs, + readProtectionLevelArgs?.index, + writeProtectionLevelArgs?.index, + ]; + } + + static MyMutableGATTDescriptorArgs decode(Object result) { + result as List; + return MyMutableGATTDescriptorArgs( + hashCodeArgs: result[0]! as int, + uuidArgs: result[1]! as String, + valueArgs: result[2] as Uint8List?, + readProtectionLevelArgs: result[3] != null + ? MyGATTProtectionLevelArgs.values[result[3]! as int] + : null, + writeProtectionLevelArgs: result[4] != null + ? MyGATTProtectionLevelArgs.values[result[4]! as int] + : null, + ); + } +} + +class MyMutableGATTCharacteristicArgs { + MyMutableGATTCharacteristicArgs({ + required this.hashCodeArgs, + required this.uuidArgs, + this.valueArgs, + required this.propertyNumbersArgs, + this.readProtectionLevelArgs, + this.writeProtectionLevelArgs, + required this.descriptorsArgs, + }); + + int hashCodeArgs; + + String uuidArgs; + + Uint8List? valueArgs; + + List propertyNumbersArgs; + + MyGATTProtectionLevelArgs? readProtectionLevelArgs; + + MyGATTProtectionLevelArgs? writeProtectionLevelArgs; + + List descriptorsArgs; + + Object encode() { + return [ + hashCodeArgs, + uuidArgs, + valueArgs, + propertyNumbersArgs, + readProtectionLevelArgs?.index, + writeProtectionLevelArgs?.index, + descriptorsArgs, + ]; + } + + static MyMutableGATTCharacteristicArgs decode(Object result) { + result as List; + return MyMutableGATTCharacteristicArgs( + hashCodeArgs: result[0]! as int, + uuidArgs: result[1]! as String, + valueArgs: result[2] as Uint8List?, + propertyNumbersArgs: (result[3] as List?)!.cast(), + readProtectionLevelArgs: result[4] != null + ? MyGATTProtectionLevelArgs.values[result[4]! as int] + : null, + writeProtectionLevelArgs: result[5] != null + ? MyGATTProtectionLevelArgs.values[result[5]! as int] + : null, + descriptorsArgs: (result[6] as List?)!.cast(), + ); + } +} + +class MyMutableGATTServiceArgs { + MyMutableGATTServiceArgs({ + required this.hashCodeArgs, + required this.uuidArgs, + required this.isPrimaryArgs, + required this.includedServicesArgs, + required this.characteristicsArgs, + }); + + int hashCodeArgs; + + String uuidArgs; + + bool isPrimaryArgs; + + List includedServicesArgs; + + List characteristicsArgs; + + Object encode() { + return [ + hashCodeArgs, + uuidArgs, + isPrimaryArgs, + includedServicesArgs, + characteristicsArgs, + ]; + } + + static MyMutableGATTServiceArgs decode(Object result) { + result as List; + return MyMutableGATTServiceArgs( + hashCodeArgs: result[0]! as int, + uuidArgs: result[1]! as String, + isPrimaryArgs: result[2]! as bool, + includedServicesArgs: (result[3] as List?)!.cast(), + characteristicsArgs: (result[4] as List?)!.cast(), + ); + } +} + +class MyGATTReadRequestArgs { + MyGATTReadRequestArgs({ + required this.idArgs, + required this.offsetArgs, + required this.lengthArgs, + }); + + int idArgs; + + int offsetArgs; + + int lengthArgs; + + Object encode() { + return [ + idArgs, + offsetArgs, + lengthArgs, + ]; + } + + static MyGATTReadRequestArgs decode(Object result) { + result as List; + return MyGATTReadRequestArgs( + idArgs: result[0]! as int, + offsetArgs: result[1]! as int, + lengthArgs: result[2]! as int, + ); + } +} + +class MyGATTWriteRequestArgs { + MyGATTWriteRequestArgs({ + required this.idArgs, + required this.offsetArgs, + required this.valueArgs, + required this.typeArgs, + }); + + int idArgs; + + int offsetArgs; + + Uint8List valueArgs; + + MyGATTCharacteristicWriteTypeArgs typeArgs; + + Object encode() { + return [ + idArgs, + offsetArgs, + valueArgs, + typeArgs.index, + ]; + } + + static MyGATTWriteRequestArgs decode(Object result) { + result as List; + return MyGATTWriteRequestArgs( + idArgs: result[0]! as int, + offsetArgs: result[1]! as int, + valueArgs: result[2]! as Uint8List, + typeArgs: MyGATTCharacteristicWriteTypeArgs.values[result[3]! as int], + ); + } +} + +class _MyCentralManagerHostAPICodec extends StandardMessageCodec { + const _MyCentralManagerHostAPICodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is MyGattCharacteristicArgs) { + if (value is MyGATTCharacteristicArgs) { buffer.putUint8(128); writeValue(buffer, value.encode()); - } else if (value is MyGattDescriptorArgs) { + } else if (value is MyGATTDescriptorArgs) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is MyGattServiceArgs) { + } else if (value is MyGATTServiceArgs) { buffer.putUint8(130); writeValue(buffer, value.encode()); } else { @@ -277,29 +535,32 @@ class _MyCentralManagerHostApiCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 128: - return MyGattCharacteristicArgs.decode(readValue(buffer)!); + return MyGATTCharacteristicArgs.decode(readValue(buffer)!); case 129: - return MyGattDescriptorArgs.decode(readValue(buffer)!); + return MyGATTDescriptorArgs.decode(readValue(buffer)!); case 130: - return MyGattServiceArgs.decode(readValue(buffer)!); + return MyGATTServiceArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } } } -class MyCentralManagerHostApi { - /// Constructor for [MyCentralManagerHostApi]. The [binaryMessenger] named argument is +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; + MyCentralManagerHostAPI({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : __pigeon_binaryMessenger = binaryMessenger, + __pigeon_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec pigeonChannelCodec = _MyCentralManagerHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyCentralManagerHostAPICodec(); - Future setUp() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.setUp'; + final String __pigeon_messageChannelSuffix; + + Future initialize() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.initialize$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -320,8 +581,8 @@ class MyCentralManagerHostApi { } } - Future startDiscovery() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.startDiscovery'; + Future getState() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getState$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -337,13 +598,40 @@ class MyCentralManagerHostApi { 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 MyBluetoothLowEnergyStateArgs.values[__pigeon_replyList[0]! as int]; + } + } + + Future startDiscovery(List serviceUUIDsArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.startDiscovery$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([serviceUUIDsArgs]) 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 String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.stopDiscovery$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -365,7 +653,7 @@ class MyCentralManagerHostApi { } Future connect(int addressArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.connect'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.connect$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -387,7 +675,7 @@ class MyCentralManagerHostApi { } Future disconnect(int addressArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.disconnect'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.disconnect$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -408,8 +696,8 @@ class MyCentralManagerHostApi { } } - Future> discoverServices(int addressArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverServices'; + Future getMTU(int addressArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getMTU$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -431,19 +719,19 @@ class MyCentralManagerHostApi { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)!.cast(); + return (__pigeon_replyList[0] as int?)!; } } - Future> discoverCharacteristics(int addressArgs, int handleArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverCharacteristics'; + Future> getServices(int addressArgs, MyCacheModeArgs modeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getServices$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send([addressArgs, handleArgs]) as List?; + await __pigeon_channel.send([addressArgs, modeArgs.index]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -458,19 +746,19 @@ class MyCentralManagerHostApi { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)!.cast(); + return (__pigeon_replyList[0] as List?)!.cast(); } } - Future> discoverDescriptors(int addressArgs, int handleArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverDescriptors'; + Future> getIncludedServices(int addressArgs, int handleArgs, MyCacheModeArgs modeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getIncludedServices$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send([addressArgs, handleArgs]) as List?; + await __pigeon_channel.send([addressArgs, handleArgs, modeArgs.index]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -485,19 +773,73 @@ class MyCentralManagerHostApi { message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as List?)!.cast(); + return (__pigeon_replyList[0] as List?)!.cast(); } } - Future readCharacteristic(int addressArgs, int handleArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.readCharacteristic'; + Future> getCharacteristics(int addressArgs, int handleArgs, MyCacheModeArgs modeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getCharacteristics$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send([addressArgs, handleArgs]) as List?; + await __pigeon_channel.send([addressArgs, handleArgs, modeArgs.index]) 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> getDescriptors(int addressArgs, int handleArgs, MyCacheModeArgs modeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getDescriptors$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs, modeArgs.index]) 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, MyCacheModeArgs modeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.readCharacteristic$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([addressArgs, handleArgs, modeArgs.index]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -516,15 +858,15 @@ class MyCentralManagerHostApi { } } - Future writeCharacteristic(int addressArgs, int handleArgs, Uint8List valueArgs, int typeNumberArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.writeCharacteristic'; + Future writeCharacteristic(int addressArgs, int handleArgs, Uint8List valueArgs, MyGATTCharacteristicWriteTypeArgs typeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.writeCharacteristic$__pigeon_messageChannelSuffix'; 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?; + await __pigeon_channel.send([addressArgs, handleArgs, valueArgs, typeArgs.index]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -538,15 +880,15 @@ class MyCentralManagerHostApi { } } - Future setCharacteristicNotifyState(int addressArgs, int handleArgs, int stateNumberArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.setCharacteristicNotifyState'; + Future setCharacteristicNotifyState(int addressArgs, int handleArgs, MyGATTCharacteristicNotifyStateArgs stateArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.setCharacteristicNotifyState$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send([addressArgs, handleArgs, stateNumberArgs]) as List?; + await __pigeon_channel.send([addressArgs, handleArgs, stateArgs.index]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -560,15 +902,15 @@ class MyCentralManagerHostApi { } } - Future readDescriptor(int addressArgs, int handleArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.readDescriptor'; + Future readDescriptor(int addressArgs, int handleArgs, MyCacheModeArgs modeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.readDescriptor$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send([addressArgs, handleArgs]) as List?; + await __pigeon_channel.send([addressArgs, handleArgs, modeArgs.index]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -588,7 +930,7 @@ class MyCentralManagerHostApi { } Future writeDescriptor(int addressArgs, int handleArgs, Uint8List valueArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.writeDescriptor'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.writeDescriptor$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -610,186 +952,23 @@ class MyCentralManagerHostApi { } } -class _MyCentralManagerFlutterApiCodec extends StandardMessageCodec { - const _MyCentralManagerFlutterApiCodec(); +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 MyGATTCharacteristicArgs) { + 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(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) { + } else if (value is MyPeripheralArgs) { buffer.putUint8(132); writeValue(buffer, value.encode()); } else { @@ -803,31 +982,244 @@ class _MyPeripheralManagerHostApiCodec extends StandardMessageCodec { case 128: return MyAdvertisementArgs.decode(readValue(buffer)!); case 129: - return MyGattCharacteristicArgs.decode(readValue(buffer)!); + return MyGATTCharacteristicArgs.decode(readValue(buffer)!); case 130: - return MyGattDescriptorArgs.decode(readValue(buffer)!); + return MyGATTDescriptorArgs.decode(readValue(buffer)!); case 131: - return MyGattServiceArgs.decode(readValue(buffer)!); - case 132: return MyManufacturerSpecificDataArgs.decode(readValue(buffer)!); + case 132: + return MyPeripheralArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } } } -class MyPeripheralManagerHostApi { - /// Constructor for [MyPeripheralManagerHostApi]. The [binaryMessenger] named argument is +abstract class MyCentralManagerFlutterAPI { + static const MessageCodec pigeonChannelCodec = _MyCentralManagerFlutterAPICodec(); + + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); + + void onDiscovered(MyPeripheralArgs peripheralArgs, int rssiArgs, int timestampArgs, MyAdvertisementTypeArgs typeArgs, MyAdvertisementArgs advertisementArgs); + + void onConnectionStateChanged(MyPeripheralArgs peripheralArgs, MyConnectionStateArgs stateArgs); + + void onMTUChanged(MyPeripheralArgs peripheralArgs, int mtuArgs); + + void onCharacteristicNotified(MyPeripheralArgs peripheralArgs, MyGATTCharacteristicArgs characteristicArgs, Uint8List valueArgs); + + static void setUp(MyCentralManagerFlutterAPI? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onStateChanged$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onStateChanged was null.'); + final List args = (message as List?)!; + final MyBluetoothLowEnergyStateArgs? arg_stateArgs = args[0] == null ? null : MyBluetoothLowEnergyStateArgs.values[args[0]! as int]; + assert(arg_stateArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onStateChanged was null, expected non-null MyBluetoothLowEnergyStateArgs.'); + try { + api.onStateChanged(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.onDiscovered$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.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 int? arg_timestampArgs = (args[2] as int?); + assert(arg_timestampArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onDiscovered was null, expected non-null int.'); + final MyAdvertisementTypeArgs? arg_typeArgs = args[3] == null ? null : MyAdvertisementTypeArgs.values[args[3]! as int]; + assert(arg_typeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onDiscovered was null, expected non-null MyAdvertisementTypeArgs.'); + final MyAdvertisementArgs? arg_advertisementArgs = (args[4] 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_timestampArgs!, arg_typeArgs!, 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$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.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_windows.MyCentralManagerFlutterAPI.onConnectionStateChanged was null, expected non-null MyPeripheralArgs.'); + final MyConnectionStateArgs? arg_stateArgs = args[1] == null ? null : MyConnectionStateArgs.values[args[1]! as int]; + assert(arg_stateArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onConnectionStateChanged was null, expected non-null MyConnectionStateArgs.'); + try { + api.onConnectionStateChanged(arg_peripheralArgs!, 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.onMTUChanged$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onMTUChanged 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.onMTUChanged was null, expected non-null MyPeripheralArgs.'); + final int? arg_mtuArgs = (args[1] as int?); + assert(arg_mtuArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onMTUChanged was null, expected non-null int.'); + try { + api.onMTUChanged(arg_peripheralArgs!, 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_windows.MyCentralManagerFlutterAPI.onCharacteristicNotified$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onCharacteristicNotified 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.onCharacteristicNotified was null, expected non-null MyPeripheralArgs.'); + final MyGATTCharacteristicArgs? arg_characteristicArgs = (args[1] as MyGATTCharacteristicArgs?); + assert(arg_characteristicArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onCharacteristicNotified was null, expected non-null MyGATTCharacteristicArgs.'); + 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_peripheralArgs!, arg_characteristicArgs!, 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 MyManufacturerSpecificDataArgs) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MyMutableGATTCharacteristicArgs) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is MyMutableGATTDescriptorArgs) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is MyMutableGATTServiceArgs) { + 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 MyManufacturerSpecificDataArgs.decode(readValue(buffer)!); + case 130: + return MyMutableGATTCharacteristicArgs.decode(readValue(buffer)!); + case 131: + return MyMutableGATTDescriptorArgs.decode(readValue(buffer)!); + case 132: + return MyMutableGATTServiceArgs.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; + MyPeripheralManagerHostAPI({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : __pigeon_binaryMessenger = binaryMessenger, + __pigeon_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerHostApiCodec(); + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerHostAPICodec(); - Future setUp() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.setUp'; + final String __pigeon_messageChannelSuffix; + + Future initialize() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.initialize$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -848,8 +1240,35 @@ class MyPeripheralManagerHostApi { } } - Future addService(MyGattServiceArgs serviceArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.addService'; + Future getState() async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.getState$__pigeon_messageChannelSuffix'; + 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 if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return MyBluetoothLowEnergyStateArgs.values[__pigeon_replyList[0]! as int]; + } + } + + Future addService(MyMutableGATTServiceArgs serviceArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.addService$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -870,37 +1289,15 @@ class MyPeripheralManagerHostApi { } } - Future removeService(int handleArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.removeService'; + Future removeService(int hashCodeArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.removeService$__pigeon_messageChannelSuffix'; 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?; + await __pigeon_channel.send([hashCodeArgs]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -915,7 +1312,7 @@ class MyPeripheralManagerHostApi { } Future startAdvertising(MyAdvertisementArgs advertisementArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.startAdvertising'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.startAdvertising$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -937,7 +1334,7 @@ class MyPeripheralManagerHostApi { } Future stopAdvertising() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.stopAdvertising'; + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.stopAdvertising$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, @@ -958,15 +1355,42 @@ class MyPeripheralManagerHostApi { } } - Future sendReadCharacteristicReply(int addressArgs, int handleArgs, bool statusArgs, Uint8List valueArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.sendReadCharacteristicReply'; + Future getMaxNotificationSize(int addressArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.getMaxNotificationSize$__pigeon_messageChannelSuffix'; 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?; + 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 int?)!; + } + } + + Future respondReadRequestWithValue(int idArgs, Uint8List valueArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.respondReadRequestWithValue$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([idArgs, valueArgs]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -980,15 +1404,15 @@ class MyPeripheralManagerHostApi { } } - Future sendWriteCharacteristicReply(int addressArgs, int handleArgs, bool statusArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.sendWriteCharacteristicReply'; + Future respondReadRequestWithProtocolError(int idArgs, MyGATTProtocolErrorArgs errorArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.respondReadRequestWithProtocolError$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send([addressArgs, handleArgs, statusArgs]) as List?; + await __pigeon_channel.send([idArgs, errorArgs.index]) as List?; if (__pigeon_replyList == null) { throw _createConnectionError(__pigeon_channelName); } else if (__pigeon_replyList.length > 1) { @@ -1002,15 +1426,59 @@ class MyPeripheralManagerHostApi { } } - Future notifyCharacteristic(int addressArgs, int handleArgs, Uint8List valueArgs) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.notifyCharacteristic'; + Future respondWriteRequest(int idArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.respondWriteRequest$__pigeon_messageChannelSuffix'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, pigeonChannelCodec, binaryMessenger: __pigeon_binaryMessenger, ); final List? __pigeon_replyList = - await __pigeon_channel.send([addressArgs, handleArgs, valueArgs]) as List?; + await __pigeon_channel.send([idArgs]) 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 respondWriteRequestWithProtocolError(int idArgs, MyGATTProtocolErrorArgs errorArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.respondWriteRequestWithProtocolError$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([idArgs, errorArgs.index]) 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 notifyValue(int addressArgs, int hashCodeArgs, Uint8List valueArgs) async { + final String __pigeon_channelName = 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.notifyValue$__pigeon_messageChannelSuffix'; + 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) { @@ -1025,13 +1493,22 @@ class MyPeripheralManagerHostApi { } } -class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { - const _MyPeripheralManagerFlutterApiCodec(); +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 if (value is MyCentralArgs) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is MyGATTReadRequestArgs) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is MyGATTWriteRequestArgs) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -1042,130 +1519,233 @@ class _MyPeripheralManagerFlutterApiCodec extends StandardMessageCodec { switch (type) { case 128: return MyCentralArgs.decode(readValue(buffer)!); + case 129: + return MyCentralArgs.decode(readValue(buffer)!); + case 130: + return MyGATTReadRequestArgs.decode(readValue(buffer)!); + case 131: + return MyGATTWriteRequestArgs.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } } } -abstract class MyPeripheralManagerFlutterApi { - static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerFlutterApiCodec(); +abstract class MyPeripheralManagerFlutterAPI { + static const MessageCodec pigeonChannelCodec = _MyPeripheralManagerFlutterAPICodec(); - void onStateChanged(int stateNumberArgs); + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); - void onReadCharacteristicCommandReceived(MyCentralArgs centralArgs, int handleArgs); + void onMTUChanged(MyCentralArgs centralArgs, int mtuArgs); - void onWriteCharacteristicCommandReceived(MyCentralArgs centralArgs, int handleArgs, Uint8List valueArgs); + void onCharacteristicReadRequest(MyCentralArgs centralArgs, int hashCodeArgs, MyGATTReadRequestArgs requestArgs); - void onCharacteristicNotifyStateChanged(MyCentralArgs centralArgs, int handleArgs, bool stateArgs); + void onCharacteristicWriteRequest(MyCentralArgs centralArgs, int hashCodeArgs, MyGATTWriteRequestArgs requestArgs); - static void setup(MyPeripheralManagerFlutterApi? api, {BinaryMessenger? binaryMessenger}) { + void onCharacteristicSubscribedClientsChanged(int hashCodeArgs, List centralsArgs); + + void onDescriptorReadRequest(MyCentralArgs centralArgs, int hashCodeArgs, MyGATTReadRequestArgs requestArgs); + + void onDescriptorWriteRequest(MyCentralArgs centralArgs, int hashCodeArgs, MyGATTWriteRequestArgs requestArgs); + + static void setUp(MyPeripheralManagerFlutterAPI? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onStateChanged', pigeonChannelCodec, + 'dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onStateChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { __pigeon_channel.setMessageHandler(null); } else { __pigeon_channel.setMessageHandler((Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onStateChanged was 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?); + final MyBluetoothLowEnergyStateArgs? arg_stateArgs = args[0] == null ? null : MyBluetoothLowEnergyStateArgs.values[args[0]! as int]; assert(arg_stateArgs != null, - 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onStateChanged was null, expected non-null MyBluetoothLowEnergyStateArgs.'); try { - api.onCharacteristicNotifyStateChanged(arg_centralArgs!, arg_handleArgs!, arg_stateArgs!); + api.onStateChanged(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.MyPeripheralManagerFlutterAPI.onMTUChanged$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.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_windows.MyPeripheralManagerFlutterAPI.onMTUChanged was null, expected non-null MyCentralArgs.'); + final int? arg_mtuArgs = (args[1] as int?); + assert(arg_mtuArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onMTUChanged was null, expected non-null int.'); + try { + api.onMTUChanged(arg_centralArgs!, 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_windows.MyPeripheralManagerFlutterAPI.onCharacteristicReadRequest$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.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_windows.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_windows.MyPeripheralManagerFlutterAPI.onCharacteristicReadRequest was null, expected non-null int.'); + final MyGATTReadRequestArgs? arg_requestArgs = (args[2] as MyGATTReadRequestArgs?); + assert(arg_requestArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onCharacteristicReadRequest was null, expected non-null MyGATTReadRequestArgs.'); + try { + api.onCharacteristicReadRequest(arg_centralArgs!, arg_hashCodeArgs!, arg_requestArgs!); + 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.onCharacteristicWriteRequest$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.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_windows.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_windows.MyPeripheralManagerFlutterAPI.onCharacteristicWriteRequest was null, expected non-null int.'); + final MyGATTWriteRequestArgs? arg_requestArgs = (args[2] as MyGATTWriteRequestArgs?); + assert(arg_requestArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onCharacteristicWriteRequest was null, expected non-null MyGATTWriteRequestArgs.'); + try { + api.onCharacteristicWriteRequest(arg_centralArgs!, arg_hashCodeArgs!, arg_requestArgs!); + 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.onCharacteristicSubscribedClientsChanged$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onCharacteristicSubscribedClientsChanged was null.'); + final List args = (message as List?)!; + final int? arg_hashCodeArgs = (args[0] as int?); + assert(arg_hashCodeArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onCharacteristicSubscribedClientsChanged was null, expected non-null int.'); + final List? arg_centralsArgs = (args[1] as List?)?.cast(); + assert(arg_centralsArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onCharacteristicSubscribedClientsChanged was null, expected non-null List.'); + try { + api.onCharacteristicSubscribedClientsChanged(arg_hashCodeArgs!, arg_centralsArgs!); + 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.onDescriptorReadRequest$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onDescriptorReadRequest 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.onDescriptorReadRequest 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_windows.MyPeripheralManagerFlutterAPI.onDescriptorReadRequest was null, expected non-null int.'); + final MyGATTReadRequestArgs? arg_requestArgs = (args[2] as MyGATTReadRequestArgs?); + assert(arg_requestArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onDescriptorReadRequest was null, expected non-null MyGATTReadRequestArgs.'); + try { + api.onDescriptorReadRequest(arg_centralArgs!, arg_hashCodeArgs!, arg_requestArgs!); + 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.onDescriptorWriteRequest$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest 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.onDescriptorWriteRequest 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_windows.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest was null, expected non-null int.'); + final MyGATTWriteRequestArgs? arg_requestArgs = (args[2] as MyGATTWriteRequestArgs?); + assert(arg_requestArgs != null, + 'Argument for dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest was null, expected non-null MyGATTWriteRequestArgs.'); + try { + api.onDescriptorWriteRequest(arg_centralArgs!, arg_hashCodeArgs!, arg_requestArgs!); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/bluetooth_low_energy_windows/lib/src/my_central.dart b/bluetooth_low_energy_windows/lib/src/my_central.dart new file mode 100644 index 0000000..89cb333 --- /dev/null +++ b/bluetooth_low_energy_windows/lib/src/my_central.dart @@ -0,0 +1,10 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +final class MyCentral extends Central { + final int addressArgs; + + MyCentral({ + required this.addressArgs, + required super.uuid, + }); +} diff --git a/bluetooth_low_energy_windows/lib/src/my_central_manager.dart b/bluetooth_low_energy_windows/lib/src/my_central_manager.dart index b0a9cc4..c3f3f3f 100644 --- a/bluetooth_low_energy_windows/lib/src/my_central_manager.dart +++ b/bluetooth_low_energy_windows/lib/src/my_central_manager.dart @@ -1,67 +1,80 @@ 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_gatt_characteristic2.dart'; -import 'my_gatt_descriptor2.dart'; +import 'my_api.g.dart'; +import 'my_gatt.dart'; +import 'my_peripheral.dart'; -class MyCentralManager extends CentralManager - implements MyCentralManagerFlutterApi { - final MyCentralManagerHostApi _api; +final class MyCentralManager extends PlatformCentralManager + implements MyCentralManagerFlutterAPI { + final MyCentralManagerHostAPI _api; final StreamController _stateChangedController; final StreamController _discoveredController; - final StreamController + final StreamController _connectionStateChangedController; - final StreamController + final StreamController _mtuChangedController; + final StreamController _characteristicNotifiedController; - - final Map _peripherals; - final Map> _characteristics; + final Map _discoveriesArgs; BluetoothLowEnergyState _state; MyCentralManager() - : _api = MyCentralManagerHostApi(), + : _api = MyCentralManagerHostAPI(), _stateChangedController = StreamController.broadcast(), _discoveredController = StreamController.broadcast(), _connectionStateChangedController = StreamController.broadcast(), + _mtuChangedController = StreamController.broadcast(), _characteristicNotifiedController = StreamController.broadcast(), - _peripherals = {}, - _characteristics = {}, + _discoveriesArgs = {}, _state = BluetoothLowEnergyState.unknown; + @override + BluetoothLowEnergyState get state => _state; @override Stream get stateChanged => _stateChangedController.stream; @override Stream get discovered => _discoveredController.stream; @override - Stream get connectionStateChanged => - _connectionStateChangedController.stream; + Stream + get connectionStateChanged => _connectionStateChangedController.stream; @override - Stream get characteristicNotified => + Stream get mtuChanged => + _mtuChangedController.stream; + @override + Stream get characteristicNotified => _characteristicNotifiedController.stream; @override - Future setUp() async { - logger.info('setUp'); - await _api.setUp(); - MyCentralManagerFlutterApi.setup(this); + void initialize() { + MyCentralManagerFlutterAPI.setUp(this); + _initialize(); } @override - Future getState() { - logger.info('getState'); - return Future.value(_state); + Future authorize() { + throw UnsupportedError('authorize is not supported on Windows.'); } @override - Future startDiscovery() async { - logger.info('startDiscovery'); - await _api.startDiscovery(); + Future showAppSettings() { + throw UnsupportedError('showAppSettings is not supported on Windows.'); + } + + @override + Future startDiscovery({ + List? serviceUUIDs, + }) async { + _discoveriesArgs.clear(); + final serviceUUIDsArgs = + serviceUUIDs?.map((uuid) => uuid.toArgs()).toList() ?? []; + logger.info('startDiscovery: $serviceUUIDsArgs'); + await _api.startDiscovery(serviceUUIDsArgs); } @override @@ -70,12 +83,18 @@ class MyCentralManager extends CentralManager await _api.stopDiscovery(); } + @override + Future> retrieveConnectedPeripherals() { + throw UnsupportedError( + 'retrieveConnectedPeripherals is not supported on Windows.'); + } + @override Future connect(Peripheral peripheral) async { if (peripheral is! MyPeripheral) { throw TypeError(); } - final addressArgs = peripheral.uuid.toAddressArgs(); + final addressArgs = peripheral.addressArgs; logger.info('connect: $addressArgs'); await _api.connect(addressArgs); } @@ -85,161 +104,158 @@ class MyCentralManager extends CentralManager if (peripheral is! MyPeripheral) { throw TypeError(); } - final addressArgs = peripheral.uuid.toAddressArgs(); + final addressArgs = peripheral.addressArgs; 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(); + Future requestMTU( + Peripheral peripheral, { + required int mtu, + }) { + throw UnsupportedError('requestMTU is not supported on Windows.'); } @override - Future> discoverGATT(Peripheral peripheral) async { + Future getMaximumWriteLength( + Peripheral peripheral, { + required GATTCharacteristicWriteType type, + }) 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 addressArgs = peripheral.addressArgs; + logger.info('getMTU: $addressArgs'); + final mtuArgs = await _api.getMTU(addressArgs); + final maximumWriteLength = (mtuArgs - 3).clamp(20, 512); + return maximumWriteLength; + } + + @override + Future readRSSI(Peripheral peripheral) async { + throw UnsupportedError('readRSSI is not supported on Windows.'); + } + + @override + Future> discoverGATT(Peripheral peripheral) async { + if (peripheral is! MyPeripheral) { + throw TypeError(); } - 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 - }; + final servicesArgs = await _getServices( + peripheral.addressArgs, + MyCacheModeArgs.uncached, + ); + final services = servicesArgs.map((args) => args.toService()).toList(); return services; } @override Future readCharacteristic( - GattCharacteristic characteristic, + Peripheral peripheral, + GATTCharacteristic characteristic, ) async { - if (characteristic is! MyGattCharacteristic2) { + if (peripheral is! MyPeripheral || + characteristic is! MyGATTCharacteristic) { 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); + final addressArgs = peripheral.addressArgs; + final handleArgs = characteristic.handleArgs; + const modeArgs = MyCacheModeArgs.uncached; + logger.info('readCharacteristic: $addressArgs.$handleArgs - $modeArgs'); + final value = await _api.readCharacteristic( + addressArgs, + handleArgs, + modeArgs, + ); return value; } @override Future writeCharacteristic( - GattCharacteristic characteristic, { + Peripheral peripheral, + GATTCharacteristic characteristic, { required Uint8List value, - required GattCharacteristicWriteType type, + required GATTCharacteristicWriteType type, }) async { - if (characteristic is! MyGattCharacteristic2) { + if (peripheral is! MyPeripheral || + characteristic is! MyGATTCharacteristic) { throw TypeError(); } - final peripheral = characteristic.peripheral; - final addressArgs = peripheral.uuid.toAddressArgs(); - final handleArgs = characteristic.handle; - final trimmedValueArgs = value.trimGATT(); + final addressArgs = peripheral.addressArgs; + final handleArgs = characteristic.handleArgs; + final valueArgs = value; final typeArgs = type.toArgs(); - final typeNumberArgs = typeArgs.index; logger.info( - 'writeCharacteristic: $addressArgs.$handleArgs - $trimmedValueArgs, $typeArgs'); + 'writeCharacteristic: $addressArgs.$handleArgs - $valueArgs, $typeArgs'); await _api.writeCharacteristic( addressArgs, handleArgs, - trimmedValueArgs, - typeNumberArgs, + valueArgs, + typeArgs, ); } @override Future setCharacteristicNotifyState( - GattCharacteristic characteristic, { + Peripheral peripheral, + GATTCharacteristic characteristic, { required bool state, }) async { - if (characteristic is! MyGattCharacteristic2) { + if (peripheral is! MyPeripheral || + characteristic is! MyGATTCharacteristic) { throw TypeError(); } - final peripheral = characteristic.peripheral; - final addressArgs = peripheral.uuid.toAddressArgs(); - final handleArgs = characteristic.handle; + final addressArgs = peripheral.addressArgs; + final handleArgs = characteristic.handleArgs; final stateArgs = state - ? characteristic.properties.contains(GattCharacteristicProperty.notify) - ? MyGattCharacteristicNotifyStateArgs.notify - : MyGattCharacteristicNotifyStateArgs.indicate - : MyGattCharacteristicNotifyStateArgs.none; - final stateNumberArgs = stateArgs.index; + ? characteristic.properties.contains(GATTCharacteristicProperty.notify) + ? MyGATTCharacteristicNotifyStateArgs.notify + : MyGATTCharacteristicNotifyStateArgs.indicate + : MyGATTCharacteristicNotifyStateArgs.none; logger.info( 'setCharacteristicNotifyState: $addressArgs.$handleArgs - $stateArgs'); await _api.setCharacteristicNotifyState( addressArgs, handleArgs, - stateNumberArgs, + stateArgs, ); } @override - Future readDescriptor(GattDescriptor descriptor) async { - if (descriptor is! MyGattDescriptor2) { + Future readDescriptor( + Peripheral peripheral, + GATTDescriptor descriptor, + ) async { + if (peripheral is! MyPeripheral || descriptor is! MyGATTDescriptor) { 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); + final addressArgs = peripheral.addressArgs; + final handleArgs = descriptor.handleArgs; + const modeArgs = MyCacheModeArgs.uncached; + logger.info('readDescriptor: $addressArgs.$handleArgs - $modeArgs'); + final value = await _api.readDescriptor(addressArgs, handleArgs, modeArgs); return value; } @override Future writeDescriptor( - GattDescriptor descriptor, { + Peripheral peripheral, + GATTDescriptor descriptor, { required Uint8List value, }) async { - if (descriptor is! MyGattDescriptor2) { + if (peripheral is! MyPeripheral || descriptor is! MyGATTDescriptor) { 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); + final addressArgs = peripheral.addressArgs; + final handleArgs = descriptor.handleArgs; + final valueArgs = value; + logger.info('writeDescriptor: $addressArgs.$handleArgs - $valueArgs'); + await _api.writeDescriptor(addressArgs, handleArgs, valueArgs); } @override - void onStateChanged(int stateNumberArgs) { - final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs]; + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs) { logger.info('onStateChanged: $stateArgs'); final state = stateArgs.toState(); if (_state == state) { @@ -254,64 +270,289 @@ class MyCentralManager extends CentralManager void onDiscovered( MyPeripheralArgs peripheralArgs, int rssiArgs, + int timestampArgs, + MyAdvertisementTypeArgs typeArgs, 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); + logger.info( + 'onDiscovered: $addressArgs - $rssiArgs, $timestampArgs, $typeArgs, $advertisementArgs'); + if (typeArgs == MyAdvertisementTypeArgs.connectableDirected || + typeArgs == MyAdvertisementTypeArgs.nonConnectableUndirected || + typeArgs == MyAdvertisementTypeArgs.extended) { + // No need to wait SCAN_REQ. + final peripheral = peripheralArgs.toPeripheral(); + final rssi = rssiArgs; + final advertisement = advertisementArgs.toAdvertisement(); + final eventArgs = DiscoveredEventArgs( + peripheral, + rssi, + advertisement, + ); + _discoveredController.add(eventArgs); + } else { + final oldDiscoveryArgs = _discoveriesArgs.remove(addressArgs); + final newDiscoveryArgs = MyDiscoveryArgs( + peripheralArgs, + rssiArgs, + timestampArgs, + typeArgs, + advertisementArgs, + ); + // TODO: Should we ignore this? + final ignored = oldDiscoveryArgs == null || + _checkDiscoveryArgs(oldDiscoveryArgs, newDiscoveryArgs); + if (ignored) { + // Note that ADV_IND will be ignored if the advertiser never reply the + // SCAN_REQ. + _discoveriesArgs[addressArgs] = newDiscoveryArgs; + } else { + final peripheral = oldDiscoveryArgs.peripheralArgs.toPeripheral(); + final rssi = oldDiscoveryArgs.rssiArgs; + final oldAdvertisement = + typeArgs == MyAdvertisementTypeArgs.scanResponse + ? oldDiscoveryArgs.advertisementArgs.toAdvertisement() + : advertisementArgs.toAdvertisement(); + final newAdvertisement = + typeArgs == MyAdvertisementTypeArgs.scanResponse + ? advertisementArgs.toAdvertisement() + : oldDiscoveryArgs.advertisementArgs.toAdvertisement(); + final name = newAdvertisement.name?.isNotEmpty == true + ? newAdvertisement.name + : oldAdvertisement.name; + final serviceUUIDs = { + ...oldAdvertisement.serviceUUIDs, + ...newAdvertisement.serviceUUIDs, + }.toList(); + final serviceData = { + ...oldAdvertisement.serviceData, + ...newAdvertisement.serviceData, + }; + final manufacturerSpecificData = [ + ...oldAdvertisement.manufacturerSpecificData, + ...newAdvertisement.manufacturerSpecificData, + ]; + final advertisement = Advertisement( + name: name, + serviceUUIDs: serviceUUIDs, + serviceData: serviceData, + manufacturerSpecificData: manufacturerSpecificData, + ); + final eventArgs = DiscoveredEventArgs( + peripheral, + rssi, + advertisement, + ); + _discoveredController.add(eventArgs); + } + } } @override - void onConnectionStateChanged(int addressArgs, bool stateArgs) { + void onConnectionStateChanged( + MyPeripheralArgs peripheralArgs, + MyConnectionStateArgs stateArgs, + ) { + final addressArgs = peripheralArgs.addressArgs; logger.info('onConnectionStateChanged: $addressArgs - $stateArgs'); - final peripheral = _peripherals[addressArgs]; - if (peripheral == null) { - return; - } - final state = stateArgs; - final eventArgs = ConnectionStateChangedEventArgs(peripheral, state); + final peripheral = peripheralArgs.toPeripheral(); + final state = stateArgs.toState(); + final eventArgs = PeripheralConnectionStateChangedEventArgs( + peripheral, + state, + ); _connectionStateChangedController.add(eventArgs); - if (!state) { - _characteristics.remove(addressArgs); - } + } + + @override + void onMTUChanged(MyPeripheralArgs peripheralArgs, int mtuArgs) { + final addressArgs = peripheralArgs.addressArgs; + logger.info('onMTUChanged: $addressArgs - $mtuArgs'); + final peripheral = peripheralArgs.toPeripheral(); + final mtu = mtuArgs; + final eventArgs = PeripheralMTUChangedEventArgs(peripheral, mtu); + _mtuChangedController.add(eventArgs); } @override void onCharacteristicNotified( - int addressArgs, - int handleArgs, + MyPeripheralArgs peripheralArgs, + MyGATTCharacteristicArgs characteristicArgs, Uint8List valueArgs, ) { + final addressArgs = peripheralArgs.addressArgs; + final handleArgs = characteristicArgs.handleArgs; logger.info( 'onCharacteristicNotified: $addressArgs.$handleArgs - $valueArgs'); - final characteristic = _retrieveCharacteristic(addressArgs, handleArgs); - if (characteristic == null) { - return; - } + final peripheral = peripheralArgs.toPeripheral(); + final characteristic = characteristicArgs.toCharacteristic(); final value = valueArgs; - final eventArgs = GattCharacteristicNotifiedEventArgs( + final eventArgs = GATTCharacteristicNotifiedEventArgs( + peripheral, characteristic, value, ); _characteristicNotifiedController.add(eventArgs); } - GattCharacteristic? _retrieveCharacteristic(int addressArgs, int handleArgs) { - final characteristics = _characteristics[addressArgs]; - if (characteristics == null) { - return null; + Future _initialize() async { + // Here we use `Future()` to make it possible to change the `logLevel` before `initialize()`. + await Future(() async { + try { + logger.info('initialize'); + await _api.initialize(); + _getState(); + } catch (e) { + logger.severe('initialize failed.', e); + } + }); + } + + Future _getState() async { + try { + logger.info('getState'); + final stateArgs = await _api.getState(); + onStateChanged(stateArgs); + } catch (e) { + logger.severe('getState failed.', e); } - return characteristics[handleArgs]; + } + + Future> _getServices( + int addressArgs, + MyCacheModeArgs modeArgs, + ) async { + logger.info('getServices: $addressArgs - $modeArgs'); + final servicesArgs = await _api + .getServices(addressArgs, modeArgs) + .then((args) => args.cast()); + for (var serviceArgs in servicesArgs) { + final handleArgs = serviceArgs.handleArgs; + final includedServicesArgs = await _getIncludedServices( + addressArgs, + handleArgs, + modeArgs, + ); + serviceArgs.includedServicesArgs = includedServicesArgs; + final characteristicsArgs = await _getCharacteristics( + addressArgs, + handleArgs, + modeArgs, + ); + serviceArgs.characteristicsArgs = characteristicsArgs; + } + return servicesArgs; + } + + Future> _getIncludedServices( + int addressArgs, + int handleArgs, + MyCacheModeArgs modeArgs, + ) async { + logger.info('getIncludedServices: $addressArgs.$handleArgs - $modeArgs'); + final servicesArgs = await _api + .getIncludedServices(addressArgs, handleArgs, modeArgs) + .then((args) => args.cast()); + for (var serviceArgs in servicesArgs) { + final handleArgs = serviceArgs.handleArgs; + final includedServicesArgs = await _getIncludedServices( + addressArgs, + handleArgs, + modeArgs, + ); + serviceArgs.includedServicesArgs = includedServicesArgs; + final characteristicsArgs = await _getCharacteristics( + addressArgs, + handleArgs, + modeArgs, + ); + serviceArgs.characteristicsArgs = characteristicsArgs; + } + return servicesArgs; + } + + Future> _getCharacteristics( + int addressArgs, + int handleArgs, + MyCacheModeArgs modeArgs, + ) async { + logger.info('getCharacteristics: $addressArgs.$handleArgs - $modeArgs'); + final characteristicsArgs = await _api + .getCharacteristics(addressArgs, handleArgs, modeArgs) + .then((args) => args.cast()); + for (var characteristicArgs in characteristicsArgs) { + final handleArgs = characteristicArgs.handleArgs; + final descriptorsArgs = await _getDescriptors( + addressArgs, + handleArgs, + modeArgs, + ); + characteristicArgs.descriptorsArgs = descriptorsArgs; + } + return characteristicsArgs; + } + + Future> _getDescriptors( + int addressArgs, + int handleArgs, + MyCacheModeArgs modeArgs, + ) async { + logger.info('getDescriptors: $addressArgs,$handleArgs - $modeArgs'); + final descriptorsArgs = await _api + .getDescriptors(addressArgs, handleArgs, modeArgs) + .then((args) => args.cast()); + return descriptorsArgs; + } + + bool _checkDiscoveryArgs( + MyDiscoveryArgs oldDiscoveryArgs, + MyDiscoveryArgs newDiscoveryArgs, + ) { + final oldAddressArgs = oldDiscoveryArgs.peripheralArgs.addressArgs; + final newAddressArgs = newDiscoveryArgs.peripheralArgs.addressArgs; + if (oldAddressArgs != newAddressArgs) { + logger.fine( + 'ignored by different addressArgs $oldAddressArgs, $newAddressArgs'); + return true; + } + final address = + (newAddressArgs & 0xFFFFFFFFFFFF).toRadixString(16).padLeft(12, '0'); + if (oldDiscoveryArgs.typeArgs == newDiscoveryArgs.typeArgs) { + logger.fine( + 'ignored by same typeArgs $address: ${oldDiscoveryArgs.typeArgs}:${oldDiscoveryArgs.timestampArgs}, ${newDiscoveryArgs.typeArgs}:${newDiscoveryArgs.timestampArgs}'); + return true; + } + if (oldDiscoveryArgs.typeArgs != MyAdvertisementTypeArgs.scanResponse && + newDiscoveryArgs.typeArgs != MyAdvertisementTypeArgs.scanResponse) { + logger.fine( + 'ignored by wrong typeArgs $address: ${oldDiscoveryArgs.typeArgs}:${oldDiscoveryArgs.timestampArgs}, ${newDiscoveryArgs.typeArgs}:${newDiscoveryArgs.timestampArgs}'); + return true; + } + final interval = + newDiscoveryArgs.typeArgs == MyAdvertisementTypeArgs.scanResponse + ? newDiscoveryArgs.timestampArgs - oldDiscoveryArgs.timestampArgs + : oldDiscoveryArgs.timestampArgs - newDiscoveryArgs.timestampArgs; + final ignored = interval < 0 || interval > 1000; + if (ignored) { + logger.fine( + 'ignored by wrong timestampArgs $address: $interval, ${oldDiscoveryArgs.typeArgs}:${oldDiscoveryArgs.timestampArgs}, ${newDiscoveryArgs.typeArgs}:${newDiscoveryArgs.timestampArgs}'); + } + return ignored; } } + +final class MyDiscoveryArgs { + final MyPeripheralArgs peripheralArgs; + final int rssiArgs; + final int timestampArgs; + final MyAdvertisementTypeArgs typeArgs; + final MyAdvertisementArgs advertisementArgs; + + MyDiscoveryArgs( + this.peripheralArgs, + this.rssiArgs, + this.timestampArgs, + this.typeArgs, + this.advertisementArgs, + ); +} diff --git a/bluetooth_low_energy_windows/lib/src/my_gatt.dart b/bluetooth_low_energy_windows/lib/src/my_gatt.dart new file mode 100644 index 0000000..c242d45 --- /dev/null +++ b/bluetooth_low_energy_windows/lib/src/my_gatt.dart @@ -0,0 +1,97 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +import 'my_api.g.dart'; + +final class MyGATTDescriptor extends GATTDescriptor { + final int handleArgs; + + MyGATTDescriptor({ + required this.handleArgs, + required super.uuid, + }); + + @override + int get hashCode => handleArgs; + + @override + bool operator ==(Object other) { + return other is MyGATTDescriptor && other.handleArgs == handleArgs; + } +} + +final class MyGATTCharacteristic extends GATTCharacteristic { + final int handleArgs; + + MyGATTCharacteristic({ + required this.handleArgs, + required super.uuid, + required super.properties, + required List descriptors, + }) : super(descriptors: descriptors); + + @override + List get descriptors => + super.descriptors.cast(); + + @override + int get hashCode => handleArgs; + + @override + bool operator ==(Object other) { + return other is MyGATTCharacteristic && other.handleArgs == handleArgs; + } +} + +final class MyGATTService extends GATTService { + final int handleArgs; + + MyGATTService({ + required this.handleArgs, + required super.uuid, + required super.isPrimary, + required List includedServices, + required List characteristics, + }) : super( + includedServices: includedServices, + characteristics: characteristics, + ); + + @override + List get includedServices => + super.includedServices.cast(); + + @override + List get characteristics => + super.characteristics.cast(); + + @override + int get hashCode => handleArgs; + + @override + bool operator ==(Object other) { + return other is MyGATTService && other.handleArgs == handleArgs; + } +} + +final class MyGATTReadRequest extends GATTReadRequest { + final int idArgs; + final int lengthArgs; + + MyGATTReadRequest({ + required this.idArgs, + required super.offset, + required this.lengthArgs, + }); +} + +final class MyGATTWriteRequest extends GATTWriteRequest { + final int idArgs; + final MyGATTCharacteristicWriteTypeArgs typeArgs; + + MyGATTWriteRequest({ + required this.idArgs, + required super.offset, + required super.value, + required this.typeArgs, + }); +} diff --git a/bluetooth_low_energy_windows/lib/src/my_gatt_characteristic2.dart b/bluetooth_low_energy_windows/lib/src/my_gatt_characteristic2.dart deleted file mode 100644 index 00e3c1a..0000000 --- a/bluetooth_low_energy_windows/lib/src/my_gatt_characteristic2.dart +++ /dev/null @@ -1,30 +0,0 @@ -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 deleted file mode 100644 index 16ec0af..0000000 --- a/bluetooth_low_energy_windows/lib/src/my_gatt_descriptor2.dart +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index 2ac169b..0000000 --- a/bluetooth_low_energy_windows/lib/src/my_gatt_service2.dart +++ /dev/null @@ -1,29 +0,0 @@ -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/lib/src/my_peripheral.dart b/bluetooth_low_energy_windows/lib/src/my_peripheral.dart new file mode 100644 index 0000000..de11a88 --- /dev/null +++ b/bluetooth_low_energy_windows/lib/src/my_peripheral.dart @@ -0,0 +1,10 @@ +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +final class MyPeripheral extends Peripheral { + final int addressArgs; + + MyPeripheral({ + required this.addressArgs, + required super.uuid, + }); +} diff --git a/bluetooth_low_energy_windows/lib/src/my_peripheral_manager.dart b/bluetooth_low_energy_windows/lib/src/my_peripheral_manager.dart new file mode 100644 index 0000000..9fc612f --- /dev/null +++ b/bluetooth_low_energy_windows/lib/src/my_peripheral_manager.dart @@ -0,0 +1,498 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; + +import 'my_api.dart'; +import 'my_api.g.dart'; +import 'my_central.dart'; +import 'my_gatt.dart'; + +final class MyPeripheralManager extends PlatformPeripheralManager + implements MyPeripheralManagerFlutterAPI { + final MyPeripheralManagerHostAPI _api; + final StreamController + _stateChangedController; + final StreamController _mtuChangedController; + final StreamController + _characteristicReadRequestedController; + final StreamController + _characteristicWriteRequestedController; + final StreamController + _characteristicNotifyStateChangedController; + final StreamController + _descriptorReadRequestedController; + final StreamController + _descriptorWriteRequestedController; + + final Map _services; + final Map _characteristics; + final Map _descriptors; + final Map> _subscribedCentralsArgs; + + BluetoothLowEnergyState _state; + + MyPeripheralManager() + : _api = MyPeripheralManagerHostAPI(), + _stateChangedController = StreamController.broadcast(), + _mtuChangedController = StreamController.broadcast(), + _characteristicReadRequestedController = StreamController.broadcast(), + _characteristicWriteRequestedController = StreamController.broadcast(), + _characteristicNotifyStateChangedController = + StreamController.broadcast(), + _descriptorReadRequestedController = StreamController.broadcast(), + _descriptorWriteRequestedController = StreamController.broadcast(), + _services = {}, + _characteristics = {}, + _descriptors = {}, + _subscribedCentralsArgs = {}, + _state = BluetoothLowEnergyState.unknown; + + UUID get cccUUID => UUID.short(0x2902); + @override + BluetoothLowEnergyState get state => _state; + @override + Stream get stateChanged => + _stateChangedController.stream; + @override + Stream get connectionStateChanged => + throw UnsupportedError( + 'connectionStateChanged is not supported on Windows.'); + @override + Stream get mtuChanged => + _mtuChangedController.stream; + @override + Stream + get characteristicReadRequested => + _characteristicReadRequestedController.stream; + @override + Stream + get characteristicWriteRequested => + _characteristicWriteRequestedController.stream; + @override + Stream + get characteristicNotifyStateChanged => + _characteristicNotifyStateChangedController.stream; + @override + Stream get descriptorReadRequested => + _descriptorReadRequestedController.stream; + @override + Stream get descriptorWriteRequested => + _descriptorWriteRequestedController.stream; + + @override + void initialize() { + MyPeripheralManagerFlutterAPI.setUp(this); + _initialize(); + } + + @override + Future authorize() async { + throw UnsupportedError('authorize is not supported on Windows.'); + } + + @override + Future showAppSettings() async { + throw UnsupportedError('showAppSettings is not supported on Windows.'); + } + + @override + Future addService(GATTService service) async { + final serviceArgs = service.toArgs(); + logger.info('addService: $serviceArgs'); + await _api.addService(serviceArgs); + _addService(service); + } + + @override + Future removeService(GATTService service) async { + final hashCodeArgs = service.hashCode; + logger.info('removeService: $hashCodeArgs'); + await _api.removeService(hashCodeArgs); + _removeService(service); + } + + @override + Future removeAllServices() async { + final services = _services.values; + for (var service in services) { + final hashCodeArgs = service.hashCode; + logger.info('removeService: $hashCodeArgs'); + await _api.removeService(hashCodeArgs); + } + _services.clear(); + _characteristics.clear(); + _descriptors.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 getMaximumNotifyLength(Central central) async { + if (central is! MyCentral) { + throw TypeError(); + } + final addressArgs = central.addressArgs; + final maximumNotifyLength = await _api.getMaxNotificationSize(addressArgs); + return maximumNotifyLength; + } + + @override + Future respondReadRequestWithValue( + GATTReadRequest request, { + required Uint8List value, + }) async { + if (request is! MyGATTReadRequest) { + throw TypeError(); + } + final idArgs = request.idArgs; + final valueArgs = value; + logger.info('respondReadRequestWithValue: $idArgs - $valueArgs'); + await _api.respondReadRequestWithValue(idArgs, valueArgs); + } + + @override + Future respondReadRequestWithError( + GATTReadRequest request, { + required GATTError error, + }) async { + if (request is! MyGATTReadRequest) { + throw TypeError(); + } + final idArgs = request.idArgs; + final errorArgs = error.toArgs(); + logger.info('respondReadRequestWithProtocolError: $idArgs - $errorArgs'); + await _api.respondReadRequestWithProtocolError(idArgs, errorArgs); + } + + @override + Future respondWriteRequest(GATTWriteRequest request) async { + if (request is! MyGATTWriteRequest) { + throw TypeError(); + } + if (request.typeArgs == MyGATTCharacteristicWriteTypeArgs.withoutResponse) { + return; + } + final idArgs = request.idArgs; + logger.info('respondWriteRequest: $idArgs'); + await _api.respondWriteRequest(idArgs); + } + + @override + Future respondWriteRequestWithError( + GATTWriteRequest request, { + required GATTError error, + }) async { + if (request is! MyGATTWriteRequest) { + throw TypeError(); + } + if (request.typeArgs == MyGATTCharacteristicWriteTypeArgs.withoutResponse) { + return; + } + final idArgs = request.idArgs; + final errorArgs = error.toArgs(); + logger.info('respondWriteRequestWithProtocolError: $idArgs - $errorArgs'); + await _api.respondWriteRequestWithProtocolError(idArgs, errorArgs); + } + + @override + Future notifyCharacteristic( + Central central, + GATTCharacteristic characteristic, { + required Uint8List value, + }) async { + if (central is! MyCentral || characteristic is! MutableGATTCharacteristic) { + throw TypeError(); + } + final addressArgs = central.addressArgs; + final hashCodeArgs = characteristic.hashCode; + final valueArgs = value; + logger.info('notifyValue: $addressArgs - $hashCodeArgs, $valueArgs'); + await _api.notifyValue( + addressArgs, + hashCodeArgs, + valueArgs, + ); + } + + @override + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs) async { + logger.info('onStateChanged: $stateArgs'); + final state = stateArgs.toState(); + if (_state == state) { + return; + } + _state = state; + final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state); + _stateChangedController.add(eventArgs); + } + + @override + void onMTUChanged(MyCentralArgs centralArgs, int mtuArgs) { + final addressArgs = centralArgs.addressArgs; + logger.info('onMTUChanged: $addressArgs - $mtuArgs'); + final central = centralArgs.toCentral(); + final mtu = mtuArgs; + final eventArgs = CentralMTUChangedEventArgs(central, mtu); + _mtuChangedController.add(eventArgs); + } + + @override + void onCharacteristicReadRequest( + MyCentralArgs centralArgs, + int hashCodeArgs, + MyGATTReadRequestArgs requestArgs, + ) async { + final addressArgs = centralArgs.addressArgs; + logger.info( + 'onCharacteristicReadRequest: $addressArgs - $hashCodeArgs, $requestArgs'); + final central = centralArgs.toCentral(); + final characteristic = _characteristics[hashCodeArgs]; + if (characteristic == null) { + await _respondReadRequestWithProtocolError( + requestArgs.idArgs, + MyGATTProtocolErrorArgs.attributeNotFound, + ); + } else { + final eventArgs = GATTCharacteristicReadRequestedEventArgs( + central, + characteristic, + MyGATTReadRequest( + idArgs: requestArgs.idArgs, + offset: requestArgs.offsetArgs, + lengthArgs: requestArgs.lengthArgs, + ), + ); + _characteristicReadRequestedController.add(eventArgs); + } + } + + @override + void onCharacteristicWriteRequest( + MyCentralArgs centralArgs, + int hashCodeArgs, + MyGATTWriteRequestArgs requestArgs, + ) async { + final addressArgs = centralArgs.addressArgs; + logger.info( + 'onCharacteristicWriteRequest: $addressArgs - $hashCodeArgs, $requestArgs'); + final central = centralArgs.toCentral(); + final characteristic = _characteristics[hashCodeArgs]; + if (characteristic == null) { + if (requestArgs.typeArgs == + MyGATTCharacteristicWriteTypeArgs.withoutResponse) { + return; + } + await _respondWriteRequestWithProtocolError( + requestArgs.idArgs, + MyGATTProtocolErrorArgs.attributeNotFound, + ); + } else { + final eventArgs = GATTCharacteristicWriteRequestedEventArgs( + central, + characteristic, + MyGATTWriteRequest( + idArgs: requestArgs.idArgs, + offset: requestArgs.offsetArgs, + value: requestArgs.valueArgs, + typeArgs: requestArgs.typeArgs, + ), + ); + _characteristicWriteRequestedController.add(eventArgs); + } + } + + @override + void onCharacteristicSubscribedClientsChanged( + int hashCodeArgs, + List centralsArgs, + ) { + logger.info( + 'onCharacteristicSubscribedClientsChanged: $hashCodeArgs - $centralsArgs'); + final characteristic = _characteristics[hashCodeArgs]; + if (characteristic == null) { + logger.warning('The characteristic[$hashCodeArgs] is null.'); + return; + } + final oldSubscribedCentralsArgs = + _subscribedCentralsArgs[hashCodeArgs] ?? {}; + final newSubscribedCentralsArgs = + centralsArgs.cast().toSet(); + final removedCentralsArgs = + oldSubscribedCentralsArgs.difference(newSubscribedCentralsArgs); + final addedCentralsArgs = + newSubscribedCentralsArgs.difference(oldSubscribedCentralsArgs); + for (var centralArgs in removedCentralsArgs) { + final central = centralArgs.toCentral(); + final eventArgs = GATTCharacteristicNotifyStateChangedEventArgs( + central, + characteristic, + false, + ); + _characteristicNotifyStateChangedController.add(eventArgs); + } + for (var centralArgs in addedCentralsArgs) { + final central = centralArgs.toCentral(); + final eventArgs = GATTCharacteristicNotifyStateChangedEventArgs( + central, + characteristic, + true, + ); + _characteristicNotifyStateChangedController.add(eventArgs); + } + _subscribedCentralsArgs[hashCodeArgs] = newSubscribedCentralsArgs; + } + + @override + void onDescriptorReadRequest( + MyCentralArgs centralArgs, + int hashCodeArgs, + MyGATTReadRequestArgs requestArgs, + ) async { + final addressArgs = centralArgs.addressArgs; + logger.info( + 'onDescriptorReadRequest: $addressArgs - $hashCodeArgs, $requestArgs'); + final central = centralArgs.toCentral(); + final descriptor = _descriptors[hashCodeArgs]; + if (descriptor == null) { + await _respondReadRequestWithProtocolError( + requestArgs.idArgs, + MyGATTProtocolErrorArgs.attributeNotFound, + ); + } else { + final eventArgs = GATTDescriptorReadRequestedEventArgs( + central, + descriptor, + MyGATTReadRequest( + idArgs: requestArgs.idArgs, + offset: requestArgs.offsetArgs, + lengthArgs: requestArgs.lengthArgs, + ), + ); + _descriptorReadRequestedController.add(eventArgs); + } + } + + @override + void onDescriptorWriteRequest( + MyCentralArgs centralArgs, + int hashCodeArgs, + MyGATTWriteRequestArgs requestArgs, + ) async { + final addressArgs = centralArgs.addressArgs; + logger.info( + 'onDescriptorWriteRequest: $addressArgs - $hashCodeArgs, $requestArgs'); + final central = centralArgs.toCentral(); + final descriptor = _descriptors[hashCodeArgs]; + if (descriptor == null) { + if (requestArgs.typeArgs == + MyGATTCharacteristicWriteTypeArgs.withoutResponse) { + return; + } + await _respondWriteRequestWithProtocolError( + requestArgs.idArgs, + MyGATTProtocolErrorArgs.attributeNotFound, + ); + } else { + final eventArgs = GATTDescriptorWriteRequestedEventArgs( + central, + descriptor, + MyGATTWriteRequest( + idArgs: requestArgs.idArgs, + offset: requestArgs.offsetArgs, + value: requestArgs.valueArgs, + typeArgs: requestArgs.typeArgs, + ), + ); + _descriptorWriteRequestedController.add(eventArgs); + } + } + + void _addService(GATTService service) { + for (var includedService in service.includedServices) { + _addService(includedService); + } + final characteristics = + service.characteristics.cast(); + for (var characteristic in characteristics) { + final descriptors = + characteristic.descriptors.cast(); + for (var descriptor in descriptors) { + _descriptors[descriptor.hashCode] = descriptor; + } + _characteristics[characteristic.hashCode] = characteristic; + } + _services[service.hashCode] = service; + } + + void _removeService(GATTService service) { + for (var includedService in service.includedServices) { + _removeService(includedService); + } + for (var characteristic in service.characteristics) { + for (var descriptor in characteristic.descriptors) { + _descriptors.remove(descriptor.hashCode); + } + _characteristics.remove(characteristic.hashCode); + } + _services.remove(service.hashCode); + } + + Future _initialize() async { + // Here we use `Future()` to make it possible to change the `logLevel` before `initialize()`. + await Future(() async { + try { + logger.info('initialize'); + await _api.initialize(); + _getState(); + } catch (e) { + logger.severe('initialize failed.', e); + } + }); + } + + Future _getState() async { + try { + logger.info('getState'); + final stateArgs = await _api.getState(); + onStateChanged(stateArgs); + } catch (e) { + logger.severe('getState failed.', e); + } + } + + Future _respondReadRequestWithProtocolError( + int idArgs, + MyGATTProtocolErrorArgs errorArgs, + ) async { + try { + logger.info('respondReadRequestWithProtocolError: $idArgs - $errorArgs'); + await _api.respondReadRequestWithProtocolError(idArgs, errorArgs); + } catch (e) { + logger.severe('respondReadRequestWithProtocolError failed.', e); + } + } + + Future _respondWriteRequestWithProtocolError( + int idArgs, + MyGATTProtocolErrorArgs errorArgs, + ) async { + try { + logger.info('respondWriteRequestWithProtocolError: $idArgs - $errorArgs'); + await _api.respondWriteRequestWithProtocolError(idArgs, errorArgs); + } catch (e) { + logger.severe('respondWriteRequestWithProtocolError failed.', e); + } + } +} diff --git a/bluetooth_low_energy_windows/my_api.dart b/bluetooth_low_energy_windows/my_api.dart index a80a0df..dac7d40 100644 --- a/bluetooth_low_energy_windows/my_api.dart +++ b/bluetooth_low_energy_windows/my_api.dart @@ -1,10 +1,12 @@ +// Run with `dart run pigeon --input my_api.dart`. import 'package:pigeon/pigeon.dart'; +// TODO: Use `@ProxyApi` to manage instancs when this feature released: +// https://github.com/flutter/flutter/issues/147486 @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( @@ -14,12 +16,27 @@ import 'package:pigeon/pigeon.dart'; ) enum MyBluetoothLowEnergyStateArgs { unknown, + unsupported, disabled, off, on, } -enum MyGattCharacteristicPropertyArgs { +enum MyAdvertisementTypeArgs { + connectableUndirected, + connectableDirected, + scannableUndirected, + nonConnectableUndirected, + scanResponse, + extended, +} + +enum MyConnectionStateArgs { + disconnected, + connected, +} + +enum MyGATTCharacteristicPropertyArgs { read, write, writeWithoutResponse, @@ -27,17 +44,49 @@ enum MyGattCharacteristicPropertyArgs { indicate, } -enum MyGattCharacteristicWriteTypeArgs { +enum MyGATTCharacteristicWriteTypeArgs { withResponse, withoutResponse, } -enum MyGattCharacteristicNotifyStateArgs { +enum MyGATTCharacteristicNotifyStateArgs { none, notify, indicate, } +enum MyGATTProtectionLevelArgs { + plain, + authenticationRequired, + entryptionRequired, + encryptionAndAuthenticationRequired, +} + +enum MyGATTProtocolErrorArgs { + invalidHandle, + readNotPermitted, + writeNotPermitted, + invalidPDU, + insufficientAuthentication, + requestNotSupported, + invalidOffset, + insufficientAuthorization, + prepareQueueFull, + attributeNotFound, + attributeNotLong, + insufficientEncryptionKeySize, + invalidAttributeValueLength, + unlikelyError, + insufficientEncryption, + unsupportedGroupType, + insufficientResources, +} + +enum MyCacheModeArgs { + cached, + uncached, +} + class MyManufacturerSpecificDataArgs { final int idArgs; final Uint8List dataArgs; @@ -49,7 +98,7 @@ class MyAdvertisementArgs { final String? nameArgs; final List serviceUUIDsArgs; final Map serviceDataArgs; - final MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs; + final List manufacturerSpecificDataArgs; MyAdvertisementArgs( this.nameArgs, @@ -71,25 +120,23 @@ class MyPeripheralArgs { MyPeripheralArgs(this.addressArgs); } -class MyGattDescriptorArgs { +class MyGATTDescriptorArgs { final int handleArgs; final String uuidArgs; - final Uint8List? valueArgs; - MyGattDescriptorArgs( + MyGATTDescriptorArgs( this.handleArgs, this.uuidArgs, - this.valueArgs, ); } -class MyGattCharacteristicArgs { +class MyGATTCharacteristicArgs { final int handleArgs; final String uuidArgs; final List propertyNumbersArgs; - final List descriptorsArgs; + final List descriptorsArgs; - MyGattCharacteristicArgs( + MyGATTCharacteristicArgs( this.handleArgs, this.uuidArgs, this.propertyNumbersArgs, @@ -97,124 +144,240 @@ class MyGattCharacteristicArgs { ); } -class MyGattServiceArgs { +class MyGATTServiceArgs { final int handleArgs; final String uuidArgs; - final List characteristicsArgs; + final bool isPrimaryArgs; + final List includedServicesArgs; + final List characteristicsArgs; - MyGattServiceArgs( + MyGATTServiceArgs( this.handleArgs, this.uuidArgs, + this.isPrimaryArgs, + this.includedServicesArgs, this.characteristicsArgs, ); } +class MyMutableGATTDescriptorArgs { + final int hashCodeArgs; + final String uuidArgs; + final Uint8List? valueArgs; + final MyGATTProtectionLevelArgs? readProtectionLevelArgs; + final MyGATTProtectionLevelArgs? writeProtectionLevelArgs; + + MyMutableGATTDescriptorArgs( + this.hashCodeArgs, + this.uuidArgs, + this.valueArgs, + this.readProtectionLevelArgs, + this.writeProtectionLevelArgs, + ); +} + +class MyMutableGATTCharacteristicArgs { + final int hashCodeArgs; + final String uuidArgs; + final Uint8List? valueArgs; + final List propertyNumbersArgs; + final MyGATTProtectionLevelArgs? readProtectionLevelArgs; + final MyGATTProtectionLevelArgs? writeProtectionLevelArgs; + final List descriptorsArgs; + + MyMutableGATTCharacteristicArgs( + this.hashCodeArgs, + this.uuidArgs, + this.valueArgs, + this.propertyNumbersArgs, + this.readProtectionLevelArgs, + this.writeProtectionLevelArgs, + this.descriptorsArgs, + ); +} + +class MyMutableGATTServiceArgs { + final int hashCodeArgs; + final String uuidArgs; + final bool isPrimaryArgs; + final List includedServicesArgs; + final List characteristicsArgs; + + MyMutableGATTServiceArgs( + this.hashCodeArgs, + this.uuidArgs, + this.isPrimaryArgs, + this.includedServicesArgs, + this.characteristicsArgs, + ); +} + +class MyGATTReadRequestArgs { + final int idArgs; + final int offsetArgs; + final int lengthArgs; + + MyGATTReadRequestArgs( + this.idArgs, + this.offsetArgs, + this.lengthArgs, + ); +} + +class MyGATTWriteRequestArgs { + final int idArgs; + final int offsetArgs; + final Uint8List valueArgs; + final MyGATTCharacteristicWriteTypeArgs typeArgs; + + MyGATTWriteRequestArgs( + this.idArgs, + this.offsetArgs, + this.valueArgs, + this.typeArgs, + ); +} + @HostApi() -abstract class MyCentralManagerHostApi { +abstract class MyCentralManagerHostAPI { @async - void setUp(); - void startDiscovery(); + void initialize(); + MyBluetoothLowEnergyStateArgs getState(); + void startDiscovery(List serviceUUIDsArgs); void stopDiscovery(); @async void connect(int addressArgs); void disconnect(int addressArgs); + int getMTU(int addressArgs); @async - List discoverServices(int addressArgs); - @async - List discoverCharacteristics( + List getServices( int addressArgs, - int handleArgs, + MyCacheModeArgs modeArgs, ); @async - List discoverDescriptors( + List getIncludedServices( int addressArgs, int handleArgs, + MyCacheModeArgs modeArgs, ); @async - Uint8List readCharacteristic(int addressArgs, int handleArgs); + List getCharacteristics( + int addressArgs, + int handleArgs, + MyCacheModeArgs modeArgs, + ); + @async + List getDescriptors( + int addressArgs, + int handleArgs, + MyCacheModeArgs modeArgs, + ); + @async + Uint8List readCharacteristic( + int addressArgs, + int handleArgs, + MyCacheModeArgs modeArgs, + ); @async void writeCharacteristic( int addressArgs, int handleArgs, Uint8List valueArgs, - int typeNumberArgs, + MyGATTCharacteristicWriteTypeArgs typeArgs, ); @async void setCharacteristicNotifyState( int addressArgs, int handleArgs, - int stateNumberArgs, + MyGATTCharacteristicNotifyStateArgs stateArgs, ); @async - Uint8List readDescriptor(int addressArgs, int handleArgs); + Uint8List readDescriptor( + int addressArgs, + int handleArgs, + MyCacheModeArgs modeArgs, + ); @async void writeDescriptor(int addressArgs, int handleArgs, Uint8List valueArgs); } @FlutterApi() -abstract class MyCentralManagerFlutterApi { - void onStateChanged(int stateNumberArgs); +abstract class MyCentralManagerFlutterAPI { + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); void onDiscovered( MyPeripheralArgs peripheralArgs, int rssiArgs, + int timestampArgs, + MyAdvertisementTypeArgs typeArgs, MyAdvertisementArgs advertisementArgs, ); void onConnectionStateChanged( - int addressArgs, - bool stateArgs, + MyPeripheralArgs peripheralArgs, + MyConnectionStateArgs stateArgs, ); + void onMTUChanged(MyPeripheralArgs peripheralArgs, int mtuArgs); void onCharacteristicNotified( - int addressArgs, - int handleArgs, + MyPeripheralArgs peripheralArgs, + MyGATTCharacteristicArgs characteristicArgs, Uint8List valueArgs, ); } @HostApi() -abstract class MyPeripheralManagerHostApi { +abstract class MyPeripheralManagerHostAPI { @async - void setUp(); - @async - void addService(MyGattServiceArgs serviceArgs); - void removeService(int handleArgs); - void clearServices(); + void initialize(); + MyBluetoothLowEnergyStateArgs getState(); @async + void addService(MyMutableGATTServiceArgs serviceArgs); + void removeService(int hashCodeArgs); void startAdvertising(MyAdvertisementArgs advertisementArgs); void stopAdvertising(); - void sendReadCharacteristicReply( - int addressArgs, - int handleArgs, - bool statusArgs, - Uint8List valueArgs, + int getMaxNotificationSize(int addressArgs); + void respondReadRequestWithValue(int idArgs, Uint8List valueArgs); + void respondReadRequestWithProtocolError( + int idArgs, + MyGATTProtocolErrorArgs errorArgs, ); - void sendWriteCharacteristicReply( - int addressArgs, - int handleArgs, - bool statusArgs, + void respondWriteRequest(int idArgs); + void respondWriteRequestWithProtocolError( + int idArgs, + MyGATTProtocolErrorArgs errorArgs, ); @async - void notifyCharacteristic( + void notifyValue( int addressArgs, - int handleArgs, + int hashCodeArgs, Uint8List valueArgs, ); } @FlutterApi() -abstract class MyPeripheralManagerFlutterApi { - void onStateChanged(int stateNumberArgs); - void onReadCharacteristicCommandReceived( +abstract class MyPeripheralManagerFlutterAPI { + void onStateChanged(MyBluetoothLowEnergyStateArgs stateArgs); + void onMTUChanged(MyCentralArgs centralArgs, int mtuArgs); + void onCharacteristicReadRequest( MyCentralArgs centralArgs, - int handleArgs, + int hashCodeArgs, + MyGATTReadRequestArgs requestArgs, ); - void onWriteCharacteristicCommandReceived( + void onCharacteristicWriteRequest( MyCentralArgs centralArgs, - int handleArgs, - Uint8List valueArgs, + int hashCodeArgs, + MyGATTWriteRequestArgs requestArgs, ); - void onCharacteristicNotifyStateChanged( + void onCharacteristicSubscribedClientsChanged( + int hashCodeArgs, + List centralsArgs, + ); + void onDescriptorReadRequest( MyCentralArgs centralArgs, - int handleArgs, - bool stateArgs, + int hashCodeArgs, + MyGATTReadRequestArgs requestArgs, + ); + void onDescriptorWriteRequest( + MyCentralArgs centralArgs, + int hashCodeArgs, + MyGATTWriteRequestArgs requestArgs, ); } diff --git a/bluetooth_low_energy_windows/pubspec.yaml b/bluetooth_low_energy_windows/pubspec.yaml index f1cf8cc..77ce180 100644 --- a/bluetooth_low_energy_windows/pubspec.yaml +++ b/bluetooth_low_energy_windows/pubspec.yaml @@ -1,27 +1,36 @@ name: bluetooth_low_energy_windows -description: Windows implementation of the bluetooth_low_energy plugin. -version: 5.0.3 +description: "Windows implementation of the bluetooth_low_energy plugin." +version: 6.0.0 homepage: https://github.com/yanshouwang/bluetooth_low_energy +repository: https://github.com/yanshouwang/bluetooth_low_energy +issue_tracker: https://github.com/yanshouwang/bluetooth_low_energy/issues +topics: + - bluetooth + - bluetooth-low-energy + - ble +funding: + - https://paypal.me/yanshouwang5112 + - https://afdian.net/a/yanshouwang environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.0.0" + sdk: '>=3.0.0 <4.0.0' + flutter: '>=3.0.0' dependencies: flutter: sdk: flutter - bluetooth_low_energy_platform_interface: ^5.0.2 + bluetooth_low_energy_platform_interface: ^6.0.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.0 - pigeon: ^15.0.2 + flutter_lints: ^4.0.0 + pigeon: ^19.0.0 flutter: plugin: implements: bluetooth_low_energy platforms: windows: - pluginClass: BluetoothLowEnergyWindowsCApi - dartPluginClass: BluetoothLowEnergyWindows + pluginClass: BluetoothLowEnergyWindowsPluginCApi + dartPluginClass: BluetoothLowEnergyWindowsPlugin diff --git a/bluetooth_low_energy_windows/test/bluetooth_low_energy_windows_test.dart b/bluetooth_low_energy_windows/test/bluetooth_low_energy_windows_test.dart new file mode 100644 index 0000000..ab73b3a --- /dev/null +++ b/bluetooth_low_energy_windows/test/bluetooth_low_energy_windows_test.dart @@ -0,0 +1 @@ +void main() {} diff --git a/bluetooth_low_energy_windows/windows/CMakeLists.txt b/bluetooth_low_energy_windows/windows/CMakeLists.txt index 3589bcd..c38317e 100644 --- a/bluetooth_low_energy_windows/windows/CMakeLists.txt +++ b/bluetooth_low_energy_windows/windows/CMakeLists.txt @@ -19,11 +19,22 @@ set(PLUGIN_NAME "bluetooth_low_energy_windows_plugin") # NUGET: Microsoft.Windows.CppWinRT set(CPPWINRT_VERSION "2.0.230706.1") +include(FetchContent) +FetchContent_Declare(nuget + URL "https://dist.nuget.org/win-x86-commandline/v6.10.0/nuget.exe" + URL_HASH SHA256=bad75b985cef3b2e52fa6141b207db25bafa8724189a420400fcf2787248bf4e + DOWNLOAD_NO_EXTRACT true +) + find_program(NUGET nuget) -if(NOT NUGET) - message(FATAL_ERROR "Nuget.exe not found") +if (NOT NUGET) + message("Nuget.exe not found, trying to download or use cached version.") + FetchContent_MakeAvailable(nuget) + set(NUGET ${nuget_SOURCE_DIR}/nuget.exe) endif() +message(STATUS "NuGet found: ${NUGET}") + execute_process(COMMAND ${NUGET} install Microsoft.Windows.CppWinRT -Version ${CPPWINRT_VERSION} -OutputDirectory packages WORKING_DIRECTORY ${CMAKE_BINARY_DIR} @@ -45,21 +56,25 @@ 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" + "bluetooth_low_energy_windows_plugin.h" + "bluetooth_low_energy_windows_plugin.cpp" "my_api.g.h" - "my_exception.cpp" - "my_exception.h" - "my_central_manager.cpp" + "my_api.g.cpp" "my_central_manager.h" + "my_central_manager.cpp" + "my_peripheral_manager.h" + "my_peripheral_manager.cpp" + "my_exception.h" + "my_exception.cpp" + "my_format.h" + "my_format.cpp" ) # 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" + "include/bluetooth_low_energy_windows/bluetooth_low_energy_windows_plugin_c_api.h" + "bluetooth_low_energy_windows_plugin_c_api.cpp" ${PLUGIN_SOURCES} ) @@ -71,7 +86,8 @@ 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) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) # Enable compiler support for coroutines @@ -80,7 +96,8 @@ 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_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. @@ -102,10 +119,10 @@ 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 + URL https://github.com/google/googletest/archive/v1.14.0.zip + URL_HASH SHA256=1f357c27ca988c3f7c6b4bf68a9395005ac6761f034046e9dde0896e3aba00e4 ) # Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) @@ -116,10 +133,15 @@ 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 + test/bluetooth_low_energy_windows_plugin_test.cpp ${PLUGIN_SOURCES} ) apply_standard_settings(${TEST_RUNNER}) + +# Enable compiler support for coroutines +target_compile_features(${TEST_RUNNER} PRIVATE cxx_std_20) +target_compile_options(${TEST_RUNNER} PRIVATE /await) + 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) diff --git a/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.cpp b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.cpp deleted file mode 100644 index a294aa4..0000000 --- a/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// 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 deleted file mode 100644 index 42b9e1c..0000000 --- a/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows.h +++ /dev/null @@ -1,26 +0,0 @@ -#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 deleted file mode 100644 index 7b0a9cf..0000000 --- a/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_c_api.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#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/bluetooth_low_energy_windows_plugin.cpp b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_plugin.cpp new file mode 100644 index 0000000..357e207 --- /dev/null +++ b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_plugin.cpp @@ -0,0 +1,27 @@ +#include "bluetooth_low_energy_windows_plugin.h" + +namespace bluetooth_low_energy_windows +{ + // static + void BluetoothLowEnergyWindowsPlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar) + { + auto messenger = registrar->messenger(); + + auto central_manager = std::make_unique(messenger); + MyCentralManagerHostAPI::SetUp(messenger, central_manager.get()); + + auto peripheral_manager = std::make_unique(messenger); + MyPeripheralManagerHostAPI::SetUp(messenger, peripheral_manager.get()); + + auto plugin = std::make_unique(std::move(central_manager), std::move(peripheral_manager)); + registrar->AddPlugin(std::move(plugin)); + } + + BluetoothLowEnergyWindowsPlugin::BluetoothLowEnergyWindowsPlugin(std::unique_ptr central_manager, std::unique_ptr peripheral_manager) + { + m_central_manager = std::move(central_manager); + m_peripheral_manager = std::move(peripheral_manager); + } + + BluetoothLowEnergyWindowsPlugin::~BluetoothLowEnergyWindowsPlugin() {} +} // namespace bluetooth_low_energy_windows diff --git a/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_plugin.h b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_plugin.h new file mode 100644 index 0000000..490c63c --- /dev/null +++ b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_plugin.h @@ -0,0 +1,30 @@ +#ifndef FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_PLUGIN_H_ +#define FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_PLUGIN_H_ + +#include + +#include "my_central_manager.h" +#include "my_peripheral_manager.h" + +namespace bluetooth_low_energy_windows +{ + class BluetoothLowEnergyWindowsPlugin : public flutter::Plugin + { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); + + BluetoothLowEnergyWindowsPlugin(std::unique_ptr central_manager, std::unique_ptr peripheral_manager); + + virtual ~BluetoothLowEnergyWindowsPlugin(); + + // Disallow copy and assign. + BluetoothLowEnergyWindowsPlugin(const BluetoothLowEnergyWindowsPlugin &) = delete; + BluetoothLowEnergyWindowsPlugin &operator=(const BluetoothLowEnergyWindowsPlugin &) = delete; + + private: + std::unique_ptr m_central_manager; + std::unique_ptr m_peripheral_manager; + }; +} // namespace bluetooth_low_energy_windows + +#endif // FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_PLUGIN_H_ diff --git a/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_plugin_c_api.cpp b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_plugin_c_api.cpp new file mode 100644 index 0000000..f0e4ed9 --- /dev/null +++ b/bluetooth_low_energy_windows/windows/bluetooth_low_energy_windows_plugin_c_api.cpp @@ -0,0 +1,12 @@ +#include "include/bluetooth_low_energy_windows/bluetooth_low_energy_windows_plugin_c_api.h" + +#include + +#include "bluetooth_low_energy_windows_plugin.h" + +void BluetoothLowEnergyWindowsPluginCApiRegisterWithRegistrar(FlutterDesktopPluginRegistrarRef registrar) +{ + bluetooth_low_energy_windows::BluetoothLowEnergyWindowsPlugin::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_plugin_c_api.h similarity index 53% rename from bluetooth_low_energy_windows/windows/include/bluetooth_low_energy_windows/bluetooth_low_energy_windows_c_api.h rename to bluetooth_low_energy_windows/windows/include/bluetooth_low_energy_windows/bluetooth_low_energy_windows_plugin_c_api.h index fd67e78..4c108ea 100644 --- 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_plugin_c_api.h @@ -1,5 +1,5 @@ -#ifndef FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_C_API_H_ -#define FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_C_API_H_ +#ifndef FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_PLUGIN_C_API_H_ +#define FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_PLUGIN_C_API_H_ #include @@ -13,11 +13,11 @@ extern "C" { #endif -FLUTTER_PLUGIN_EXPORT void BluetoothLowEnergyWindowsCApiRegisterWithRegistrar( +FLUTTER_PLUGIN_EXPORT void BluetoothLowEnergyWindowsPluginCApiRegisterWithRegistrar( FlutterDesktopPluginRegistrarRef registrar); #if defined(__cplusplus) } // extern "C" #endif -#endif // FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_C_API_H_ +#endif // FLUTTER_PLUGIN_BLUETOOTH_LOW_ENERGY_WINDOWS_PLUGIN_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 index 81f8ac5..ed1f099 100644 --- a/bluetooth_low_energy_windows/windows/my_api.g.cpp +++ b/bluetooth_low_energy_windows/windows/my_api.g.cpp @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v15.0.2), do not edit directly. +// Autogenerated from Pigeon (v19.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -73,19 +73,21 @@ MyManufacturerSpecificDataArgs MyManufacturerSpecificDataArgs::FromEncodableList MyAdvertisementArgs::MyAdvertisementArgs( const EncodableList& service_u_u_i_ds_args, - const EncodableMap& service_data_args) + const EncodableMap& service_data_args, + const EncodableList& manufacturer_specific_data_args) : service_u_u_i_ds_args_(service_u_u_i_ds_args), - service_data_args_(service_data_args) {} + service_data_args_(service_data_args), + manufacturer_specific_data_args_(manufacturer_specific_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) + const EncodableList& 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) {} + manufacturer_specific_data_args_(manufacturer_specific_data_args) {} const std::string* MyAdvertisementArgs::name_args() const { return name_args_ ? &(*name_args_) : nullptr; @@ -118,15 +120,11 @@ void MyAdvertisementArgs::set_service_data_args(const EncodableMap& value_arg) { } -const MyManufacturerSpecificDataArgs* MyAdvertisementArgs::manufacturer_specific_data_args() const { - return manufacturer_specific_data_args_ ? &(*manufacturer_specific_data_args_) : nullptr; +const EncodableList& MyAdvertisementArgs::manufacturer_specific_data_args() const { + return manufacturer_specific_data_args_; } -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) { +void MyAdvertisementArgs::set_manufacturer_specific_data_args(const EncodableList& value_arg) { manufacturer_specific_data_args_ = value_arg; } @@ -137,22 +135,19 @@ EncodableList MyAdvertisementArgs::ToEncodableList() const { 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()); + list.push_back(EncodableValue(manufacturer_specific_data_args_)); return list; } MyAdvertisementArgs MyAdvertisementArgs::FromEncodableList(const EncodableList& list) { MyAdvertisementArgs decoded( std::get(list[1]), - std::get(list[2])); + std::get(list[2]), + std::get(list[3])); 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; } @@ -210,76 +205,50 @@ MyPeripheralArgs MyPeripheralArgs::FromEncodableList(const EncodableList& list) return decoded; } -// MyGattDescriptorArgs +// MyGATTDescriptorArgs -MyGattDescriptorArgs::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 { +int64_t MyGATTDescriptorArgs::handle_args() const { return handle_args_; } -void MyGattDescriptorArgs::set_handle_args(int64_t value_arg) { +void MyGATTDescriptorArgs::set_handle_args(int64_t value_arg) { handle_args_ = value_arg; } -const std::string& MyGattDescriptorArgs::uuid_args() const { +const std::string& MyGATTDescriptorArgs::uuid_args() const { return uuid_args_; } -void MyGattDescriptorArgs::set_uuid_args(std::string_view value_arg) { +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 MyGATTDescriptorArgs::ToEncodableList() const { EncodableList list; - list.reserve(3); + list.reserve(2); 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( +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::MyGattCharacteristicArgs( +MyGATTCharacteristicArgs::MyGATTCharacteristicArgs( int64_t handle_args, const std::string& uuid_args, const EncodableList& property_numbers_args, @@ -289,43 +258,43 @@ MyGattCharacteristicArgs::MyGattCharacteristicArgs( property_numbers_args_(property_numbers_args), descriptors_args_(descriptors_args) {} -int64_t MyGattCharacteristicArgs::handle_args() const { +int64_t MyGATTCharacteristicArgs::handle_args() const { return handle_args_; } -void MyGattCharacteristicArgs::set_handle_args(int64_t value_arg) { +void MyGATTCharacteristicArgs::set_handle_args(int64_t value_arg) { handle_args_ = value_arg; } -const std::string& MyGattCharacteristicArgs::uuid_args() const { +const std::string& MyGATTCharacteristicArgs::uuid_args() const { return uuid_args_; } -void MyGattCharacteristicArgs::set_uuid_args(std::string_view value_arg) { +void MyGATTCharacteristicArgs::set_uuid_args(std::string_view value_arg) { uuid_args_ = value_arg; } -const EncodableList& MyGattCharacteristicArgs::property_numbers_args() const { +const EncodableList& MyGATTCharacteristicArgs::property_numbers_args() const { return property_numbers_args_; } -void MyGattCharacteristicArgs::set_property_numbers_args(const EncodableList& value_arg) { +void MyGATTCharacteristicArgs::set_property_numbers_args(const EncodableList& value_arg) { property_numbers_args_ = value_arg; } -const EncodableList& MyGattCharacteristicArgs::descriptors_args() const { +const EncodableList& MyGATTCharacteristicArgs::descriptors_args() const { return descriptors_args_; } -void MyGattCharacteristicArgs::set_descriptors_args(const EncodableList& value_arg) { +void MyGATTCharacteristicArgs::set_descriptors_args(const EncodableList& value_arg) { descriptors_args_ = value_arg; } -EncodableList MyGattCharacteristicArgs::ToEncodableList() const { +EncodableList MyGATTCharacteristicArgs::ToEncodableList() const { EncodableList list; list.reserve(4); list.push_back(EncodableValue(handle_args_)); @@ -335,8 +304,8 @@ EncodableList MyGattCharacteristicArgs::ToEncodableList() const { return list; } -MyGattCharacteristicArgs MyGattCharacteristicArgs::FromEncodableList(const EncodableList& list) { - MyGattCharacteristicArgs decoded( +MyGATTCharacteristicArgs MyGATTCharacteristicArgs::FromEncodableList(const EncodableList& list) { + MyGATTCharacteristicArgs decoded( list[0].LongValue(), std::get(list[1]), std::get(list[2]), @@ -344,116 +313,595 @@ MyGattCharacteristicArgs MyGattCharacteristicArgs::FromEncodableList(const Encod return decoded; } -// MyGattServiceArgs +// MyGATTServiceArgs -MyGattServiceArgs::MyGattServiceArgs( +MyGATTServiceArgs::MyGATTServiceArgs( int64_t handle_args, const std::string& uuid_args, + bool is_primary_args, + const EncodableList& included_services_args, const EncodableList& characteristics_args) : handle_args_(handle_args), uuid_args_(uuid_args), + is_primary_args_(is_primary_args), + included_services_args_(included_services_args), characteristics_args_(characteristics_args) {} -int64_t MyGattServiceArgs::handle_args() const { +int64_t MyGATTServiceArgs::handle_args() const { return handle_args_; } -void MyGattServiceArgs::set_handle_args(int64_t value_arg) { +void MyGATTServiceArgs::set_handle_args(int64_t value_arg) { handle_args_ = value_arg; } -const std::string& MyGattServiceArgs::uuid_args() const { +const std::string& MyGATTServiceArgs::uuid_args() const { return uuid_args_; } -void MyGattServiceArgs::set_uuid_args(std::string_view value_arg) { +void MyGATTServiceArgs::set_uuid_args(std::string_view value_arg) { uuid_args_ = value_arg; } -const EncodableList& MyGattServiceArgs::characteristics_args() const { +bool MyGATTServiceArgs::is_primary_args() const { + return is_primary_args_; +} + +void MyGATTServiceArgs::set_is_primary_args(bool value_arg) { + is_primary_args_ = value_arg; +} + + +const EncodableList& MyGATTServiceArgs::included_services_args() const { + return included_services_args_; +} + +void MyGATTServiceArgs::set_included_services_args(const EncodableList& value_arg) { + included_services_args_ = value_arg; +} + + +const EncodableList& MyGATTServiceArgs::characteristics_args() const { return characteristics_args_; } -void MyGattServiceArgs::set_characteristics_args(const EncodableList& value_arg) { +void MyGATTServiceArgs::set_characteristics_args(const EncodableList& value_arg) { characteristics_args_ = value_arg; } -EncodableList MyGattServiceArgs::ToEncodableList() const { +EncodableList MyGATTServiceArgs::ToEncodableList() const { EncodableList list; - list.reserve(3); + list.reserve(5); list.push_back(EncodableValue(handle_args_)); list.push_back(EncodableValue(uuid_args_)); + list.push_back(EncodableValue(is_primary_args_)); + list.push_back(EncodableValue(included_services_args_)); list.push_back(EncodableValue(characteristics_args_)); return list; } -MyGattServiceArgs MyGattServiceArgs::FromEncodableList(const EncodableList& list) { - MyGattServiceArgs decoded( +MyGATTServiceArgs MyGATTServiceArgs::FromEncodableList(const EncodableList& list) { + MyGATTServiceArgs decoded( list[0].LongValue(), std::get(list[1]), - std::get(list[2])); + std::get(list[2]), + std::get(list[3]), + std::get(list[4])); + return decoded; +} + +// MyMutableGATTDescriptorArgs + +MyMutableGATTDescriptorArgs::MyMutableGATTDescriptorArgs( + int64_t hash_code_args, + const std::string& uuid_args) + : hash_code_args_(hash_code_args), + uuid_args_(uuid_args) {} + +MyMutableGATTDescriptorArgs::MyMutableGATTDescriptorArgs( + int64_t hash_code_args, + const std::string& uuid_args, + const std::vector* value_args, + const MyGATTProtectionLevelArgs* read_protection_level_args, + const MyGATTProtectionLevelArgs* write_protection_level_args) + : hash_code_args_(hash_code_args), + uuid_args_(uuid_args), + value_args_(value_args ? std::optional>(*value_args) : std::nullopt), + read_protection_level_args_(read_protection_level_args ? std::optional(*read_protection_level_args) : std::nullopt), + write_protection_level_args_(write_protection_level_args ? std::optional(*write_protection_level_args) : std::nullopt) {} + +int64_t MyMutableGATTDescriptorArgs::hash_code_args() const { + return hash_code_args_; +} + +void MyMutableGATTDescriptorArgs::set_hash_code_args(int64_t value_arg) { + hash_code_args_ = value_arg; +} + + +const std::string& MyMutableGATTDescriptorArgs::uuid_args() const { + return uuid_args_; +} + +void MyMutableGATTDescriptorArgs::set_uuid_args(std::string_view value_arg) { + uuid_args_ = value_arg; +} + + +const std::vector* MyMutableGATTDescriptorArgs::value_args() const { + return value_args_ ? &(*value_args_) : nullptr; +} + +void MyMutableGATTDescriptorArgs::set_value_args(const std::vector* value_arg) { + value_args_ = value_arg ? std::optional>(*value_arg) : std::nullopt; +} + +void MyMutableGATTDescriptorArgs::set_value_args(const std::vector& value_arg) { + value_args_ = value_arg; +} + + +const MyGATTProtectionLevelArgs* MyMutableGATTDescriptorArgs::read_protection_level_args() const { + return read_protection_level_args_ ? &(*read_protection_level_args_) : nullptr; +} + +void MyMutableGATTDescriptorArgs::set_read_protection_level_args(const MyGATTProtectionLevelArgs* value_arg) { + read_protection_level_args_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void MyMutableGATTDescriptorArgs::set_read_protection_level_args(const MyGATTProtectionLevelArgs& value_arg) { + read_protection_level_args_ = value_arg; +} + + +const MyGATTProtectionLevelArgs* MyMutableGATTDescriptorArgs::write_protection_level_args() const { + return write_protection_level_args_ ? &(*write_protection_level_args_) : nullptr; +} + +void MyMutableGATTDescriptorArgs::set_write_protection_level_args(const MyGATTProtectionLevelArgs* value_arg) { + write_protection_level_args_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void MyMutableGATTDescriptorArgs::set_write_protection_level_args(const MyGATTProtectionLevelArgs& value_arg) { + write_protection_level_args_ = value_arg; +} + + +EncodableList MyMutableGATTDescriptorArgs::ToEncodableList() const { + EncodableList list; + list.reserve(5); + list.push_back(EncodableValue(hash_code_args_)); + list.push_back(EncodableValue(uuid_args_)); + list.push_back(value_args_ ? EncodableValue(*value_args_) : EncodableValue()); + list.push_back(read_protection_level_args_ ? EncodableValue((int)(*read_protection_level_args_)) : EncodableValue()); + list.push_back(write_protection_level_args_ ? EncodableValue((int)(*write_protection_level_args_)) : EncodableValue()); + return list; +} + +MyMutableGATTDescriptorArgs MyMutableGATTDescriptorArgs::FromEncodableList(const EncodableList& list) { + MyMutableGATTDescriptorArgs 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)); + } + auto& encodable_read_protection_level_args = list[3]; + if (!encodable_read_protection_level_args.IsNull()) { + decoded.set_read_protection_level_args((MyGATTProtectionLevelArgs)(std::get(encodable_read_protection_level_args))); + } + auto& encodable_write_protection_level_args = list[4]; + if (!encodable_write_protection_level_args.IsNull()) { + decoded.set_write_protection_level_args((MyGATTProtectionLevelArgs)(std::get(encodable_write_protection_level_args))); + } + return decoded; +} + +// MyMutableGATTCharacteristicArgs + +MyMutableGATTCharacteristicArgs::MyMutableGATTCharacteristicArgs( + int64_t hash_code_args, + const std::string& uuid_args, + const EncodableList& property_numbers_args, + const EncodableList& descriptors_args) + : hash_code_args_(hash_code_args), + uuid_args_(uuid_args), + property_numbers_args_(property_numbers_args), + descriptors_args_(descriptors_args) {} + +MyMutableGATTCharacteristicArgs::MyMutableGATTCharacteristicArgs( + int64_t hash_code_args, + const std::string& uuid_args, + const std::vector* value_args, + const EncodableList& property_numbers_args, + const MyGATTProtectionLevelArgs* read_protection_level_args, + const MyGATTProtectionLevelArgs* write_protection_level_args, + const EncodableList& descriptors_args) + : hash_code_args_(hash_code_args), + uuid_args_(uuid_args), + value_args_(value_args ? std::optional>(*value_args) : std::nullopt), + property_numbers_args_(property_numbers_args), + read_protection_level_args_(read_protection_level_args ? std::optional(*read_protection_level_args) : std::nullopt), + write_protection_level_args_(write_protection_level_args ? std::optional(*write_protection_level_args) : std::nullopt), + descriptors_args_(descriptors_args) {} + +int64_t MyMutableGATTCharacteristicArgs::hash_code_args() const { + return hash_code_args_; +} + +void MyMutableGATTCharacteristicArgs::set_hash_code_args(int64_t value_arg) { + hash_code_args_ = value_arg; +} + + +const std::string& MyMutableGATTCharacteristicArgs::uuid_args() const { + return uuid_args_; +} + +void MyMutableGATTCharacteristicArgs::set_uuid_args(std::string_view value_arg) { + uuid_args_ = value_arg; +} + + +const std::vector* MyMutableGATTCharacteristicArgs::value_args() const { + return value_args_ ? &(*value_args_) : nullptr; +} + +void MyMutableGATTCharacteristicArgs::set_value_args(const std::vector* value_arg) { + value_args_ = value_arg ? std::optional>(*value_arg) : std::nullopt; +} + +void MyMutableGATTCharacteristicArgs::set_value_args(const std::vector& value_arg) { + value_args_ = value_arg; +} + + +const EncodableList& MyMutableGATTCharacteristicArgs::property_numbers_args() const { + return property_numbers_args_; +} + +void MyMutableGATTCharacteristicArgs::set_property_numbers_args(const EncodableList& value_arg) { + property_numbers_args_ = value_arg; +} + + +const MyGATTProtectionLevelArgs* MyMutableGATTCharacteristicArgs::read_protection_level_args() const { + return read_protection_level_args_ ? &(*read_protection_level_args_) : nullptr; +} + +void MyMutableGATTCharacteristicArgs::set_read_protection_level_args(const MyGATTProtectionLevelArgs* value_arg) { + read_protection_level_args_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void MyMutableGATTCharacteristicArgs::set_read_protection_level_args(const MyGATTProtectionLevelArgs& value_arg) { + read_protection_level_args_ = value_arg; +} + + +const MyGATTProtectionLevelArgs* MyMutableGATTCharacteristicArgs::write_protection_level_args() const { + return write_protection_level_args_ ? &(*write_protection_level_args_) : nullptr; +} + +void MyMutableGATTCharacteristicArgs::set_write_protection_level_args(const MyGATTProtectionLevelArgs* value_arg) { + write_protection_level_args_ = value_arg ? std::optional(*value_arg) : std::nullopt; +} + +void MyMutableGATTCharacteristicArgs::set_write_protection_level_args(const MyGATTProtectionLevelArgs& value_arg) { + write_protection_level_args_ = value_arg; +} + + +const EncodableList& MyMutableGATTCharacteristicArgs::descriptors_args() const { + return descriptors_args_; +} + +void MyMutableGATTCharacteristicArgs::set_descriptors_args(const EncodableList& value_arg) { + descriptors_args_ = value_arg; +} + + +EncodableList MyMutableGATTCharacteristicArgs::ToEncodableList() const { + EncodableList list; + list.reserve(7); + list.push_back(EncodableValue(hash_code_args_)); + list.push_back(EncodableValue(uuid_args_)); + list.push_back(value_args_ ? EncodableValue(*value_args_) : EncodableValue()); + list.push_back(EncodableValue(property_numbers_args_)); + list.push_back(read_protection_level_args_ ? EncodableValue((int)(*read_protection_level_args_)) : EncodableValue()); + list.push_back(write_protection_level_args_ ? EncodableValue((int)(*write_protection_level_args_)) : EncodableValue()); + list.push_back(EncodableValue(descriptors_args_)); + return list; +} + +MyMutableGATTCharacteristicArgs MyMutableGATTCharacteristicArgs::FromEncodableList(const EncodableList& list) { + MyMutableGATTCharacteristicArgs decoded( + list[0].LongValue(), + std::get(list[1]), + std::get(list[3]), + std::get(list[6])); + auto& encodable_value_args = list[2]; + if (!encodable_value_args.IsNull()) { + decoded.set_value_args(std::get>(encodable_value_args)); + } + auto& encodable_read_protection_level_args = list[4]; + if (!encodable_read_protection_level_args.IsNull()) { + decoded.set_read_protection_level_args((MyGATTProtectionLevelArgs)(std::get(encodable_read_protection_level_args))); + } + auto& encodable_write_protection_level_args = list[5]; + if (!encodable_write_protection_level_args.IsNull()) { + decoded.set_write_protection_level_args((MyGATTProtectionLevelArgs)(std::get(encodable_write_protection_level_args))); + } + return decoded; +} + +// MyMutableGATTServiceArgs + +MyMutableGATTServiceArgs::MyMutableGATTServiceArgs( + int64_t hash_code_args, + const std::string& uuid_args, + bool is_primary_args, + const EncodableList& included_services_args, + const EncodableList& characteristics_args) + : hash_code_args_(hash_code_args), + uuid_args_(uuid_args), + is_primary_args_(is_primary_args), + included_services_args_(included_services_args), + characteristics_args_(characteristics_args) {} + +int64_t MyMutableGATTServiceArgs::hash_code_args() const { + return hash_code_args_; +} + +void MyMutableGATTServiceArgs::set_hash_code_args(int64_t value_arg) { + hash_code_args_ = value_arg; +} + + +const std::string& MyMutableGATTServiceArgs::uuid_args() const { + return uuid_args_; +} + +void MyMutableGATTServiceArgs::set_uuid_args(std::string_view value_arg) { + uuid_args_ = value_arg; +} + + +bool MyMutableGATTServiceArgs::is_primary_args() const { + return is_primary_args_; +} + +void MyMutableGATTServiceArgs::set_is_primary_args(bool value_arg) { + is_primary_args_ = value_arg; +} + + +const EncodableList& MyMutableGATTServiceArgs::included_services_args() const { + return included_services_args_; +} + +void MyMutableGATTServiceArgs::set_included_services_args(const EncodableList& value_arg) { + included_services_args_ = value_arg; +} + + +const EncodableList& MyMutableGATTServiceArgs::characteristics_args() const { + return characteristics_args_; +} + +void MyMutableGATTServiceArgs::set_characteristics_args(const EncodableList& value_arg) { + characteristics_args_ = value_arg; +} + + +EncodableList MyMutableGATTServiceArgs::ToEncodableList() const { + EncodableList list; + list.reserve(5); + list.push_back(EncodableValue(hash_code_args_)); + list.push_back(EncodableValue(uuid_args_)); + list.push_back(EncodableValue(is_primary_args_)); + list.push_back(EncodableValue(included_services_args_)); + list.push_back(EncodableValue(characteristics_args_)); + return list; +} + +MyMutableGATTServiceArgs MyMutableGATTServiceArgs::FromEncodableList(const EncodableList& list) { + MyMutableGATTServiceArgs decoded( + list[0].LongValue(), + std::get(list[1]), + std::get(list[2]), + std::get(list[3]), + std::get(list[4])); + return decoded; +} + +// MyGATTReadRequestArgs + +MyGATTReadRequestArgs::MyGATTReadRequestArgs( + int64_t id_args, + int64_t offset_args, + int64_t length_args) + : id_args_(id_args), + offset_args_(offset_args), + length_args_(length_args) {} + +int64_t MyGATTReadRequestArgs::id_args() const { + return id_args_; +} + +void MyGATTReadRequestArgs::set_id_args(int64_t value_arg) { + id_args_ = value_arg; +} + + +int64_t MyGATTReadRequestArgs::offset_args() const { + return offset_args_; +} + +void MyGATTReadRequestArgs::set_offset_args(int64_t value_arg) { + offset_args_ = value_arg; +} + + +int64_t MyGATTReadRequestArgs::length_args() const { + return length_args_; +} + +void MyGATTReadRequestArgs::set_length_args(int64_t value_arg) { + length_args_ = value_arg; +} + + +EncodableList MyGATTReadRequestArgs::ToEncodableList() const { + EncodableList list; + list.reserve(3); + list.push_back(EncodableValue(id_args_)); + list.push_back(EncodableValue(offset_args_)); + list.push_back(EncodableValue(length_args_)); + return list; +} + +MyGATTReadRequestArgs MyGATTReadRequestArgs::FromEncodableList(const EncodableList& list) { + MyGATTReadRequestArgs decoded( + list[0].LongValue(), + list[1].LongValue(), + list[2].LongValue()); + return decoded; +} + +// MyGATTWriteRequestArgs + +MyGATTWriteRequestArgs::MyGATTWriteRequestArgs( + int64_t id_args, + int64_t offset_args, + const std::vector& value_args, + const MyGATTCharacteristicWriteTypeArgs& type_args) + : id_args_(id_args), + offset_args_(offset_args), + value_args_(value_args), + type_args_(type_args) {} + +int64_t MyGATTWriteRequestArgs::id_args() const { + return id_args_; +} + +void MyGATTWriteRequestArgs::set_id_args(int64_t value_arg) { + id_args_ = value_arg; +} + + +int64_t MyGATTWriteRequestArgs::offset_args() const { + return offset_args_; +} + +void MyGATTWriteRequestArgs::set_offset_args(int64_t value_arg) { + offset_args_ = value_arg; +} + + +const std::vector& MyGATTWriteRequestArgs::value_args() const { + return value_args_; +} + +void MyGATTWriteRequestArgs::set_value_args(const std::vector& value_arg) { + value_args_ = value_arg; +} + + +const MyGATTCharacteristicWriteTypeArgs& MyGATTWriteRequestArgs::type_args() const { + return type_args_; +} + +void MyGATTWriteRequestArgs::set_type_args(const MyGATTCharacteristicWriteTypeArgs& value_arg) { + type_args_ = value_arg; +} + + +EncodableList MyGATTWriteRequestArgs::ToEncodableList() const { + EncodableList list; + list.reserve(4); + list.push_back(EncodableValue(id_args_)); + list.push_back(EncodableValue(offset_args_)); + list.push_back(EncodableValue(value_args_)); + list.push_back(EncodableValue((int)type_args_)); + return list; +} + +MyGATTWriteRequestArgs MyGATTWriteRequestArgs::FromEncodableList(const EncodableList& list) { + MyGATTWriteRequestArgs decoded( + list[0].LongValue(), + list[1].LongValue(), + std::get>(list[2]), + (MyGATTCharacteristicWriteTypeArgs)(std::get(list[3]))); return decoded; } -MyCentralManagerHostApiCodecSerializer::MyCentralManagerHostApiCodecSerializer() {} +MyCentralManagerHostAPICodecSerializer::MyCentralManagerHostAPICodecSerializer() {} -EncodableValue MyCentralManagerHostApiCodecSerializer::ReadValueOfType( +EncodableValue MyCentralManagerHostAPICodecSerializer::ReadValueOfType( uint8_t type, flutter::ByteStreamReader* stream) const { switch (type) { case 128: - return CustomEncodableValue(MyGattCharacteristicArgs::FromEncodableList(std::get(ReadValue(stream)))); + return CustomEncodableValue(MyGATTCharacteristicArgs::FromEncodableList(std::get(ReadValue(stream)))); case 129: - return CustomEncodableValue(MyGattDescriptorArgs::FromEncodableList(std::get(ReadValue(stream)))); + return CustomEncodableValue(MyGATTDescriptorArgs::FromEncodableList(std::get(ReadValue(stream)))); case 130: - return CustomEncodableValue(MyGattServiceArgs::FromEncodableList(std::get(ReadValue(stream)))); + return CustomEncodableValue(MyGATTServiceArgs::FromEncodableList(std::get(ReadValue(stream)))); default: return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } } -void MyCentralManagerHostApiCodecSerializer::WriteValue( +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)) { + if (custom_value->type() == typeid(MyGATTCharacteristicArgs)) { stream->WriteByte(128); - WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(MyGattDescriptorArgs)) { + if (custom_value->type() == typeid(MyGATTDescriptorArgs)) { stream->WriteByte(129); - WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(MyGattServiceArgs)) { + if (custom_value->type() == typeid(MyGATTServiceArgs)) { stream->WriteByte(130); - WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + 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()); +/// 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( +// Sets up an instance of `MyCentralManagerHostAPI` to handle messages through the `binary_messenger`. +void MyCentralManagerHostAPI::SetUp( flutter::BinaryMessenger* binary_messenger, - MyCentralManagerHostApi* api) { + MyCentralManagerHostAPI* api) { + MyCentralManagerHostAPI::SetUp(binary_messenger, api, ""); +} + +void MyCentralManagerHostAPI::SetUp( + flutter::BinaryMessenger* binary_messenger, + MyCentralManagerHostAPI* api, + const std::string& message_channel_suffix) { + const std::string prepended_suffix = message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : ""; { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.setUp", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.initialize" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { try { - api->SetUp([reply](std::optional&& output) { + api->Initialize([reply](std::optional&& output) { if (output.has_value()) { reply(WrapError(output.value())); return; @@ -467,15 +915,43 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.startDiscovery", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getState" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { try { - std::optional output = api->StartDiscovery(); + ErrorOr output = api->GetState(); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue((int) std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.startDiscovery" + prepended_suffix, &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_u_u_i_ds_args_arg = args.at(0); + if (encodable_service_u_u_i_ds_args_arg.IsNull()) { + reply(WrapError("service_u_u_i_ds_args_arg unexpectedly null.")); + return; + } + const auto& service_u_u_i_ds_args_arg = std::get(encodable_service_u_u_i_ds_args_arg); + std::optional output = api->StartDiscovery(service_u_u_i_ds_args_arg); if (output.has_value()) { reply(WrapError(output.value())); return; @@ -488,13 +964,13 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.stopDiscovery", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.stopDiscovery" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { try { std::optional output = api->StopDiscovery(); if (output.has_value()) { @@ -509,13 +985,13 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.connect", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.connect" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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); @@ -538,13 +1014,13 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.disconnect", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.disconnect" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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); @@ -566,13 +1042,13 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverServices", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getMTU" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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); @@ -581,7 +1057,41 @@ void MyCentralManagerHostApi::SetUp( return; } const int64_t address_args_arg = encodable_address_args_arg.LongValue(); - api->DiscoverServices(address_args_arg, [reply](ErrorOr&& output) { + ErrorOr output = api->GetMTU(address_args_arg); + 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); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getServices" + prepended_suffix, &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_mode_args_arg = args.at(1); + if (encodable_mode_args_arg.IsNull()) { + reply(WrapError("mode_args_arg unexpectedly null.")); + return; + } + const MyCacheModeArgs& mode_args_arg = (MyCacheModeArgs)encodable_mode_args_arg.LongValue(); + api->GetServices(address_args_arg, mode_args_arg, [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -595,13 +1105,13 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverCharacteristics", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getIncludedServices" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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); @@ -616,7 +1126,13 @@ void MyCentralManagerHostApi::SetUp( return; } const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); - api->DiscoverCharacteristics(address_args_arg, handle_args_arg, [reply](ErrorOr&& output) { + const auto& encodable_mode_args_arg = args.at(2); + if (encodable_mode_args_arg.IsNull()) { + reply(WrapError("mode_args_arg unexpectedly null.")); + return; + } + const MyCacheModeArgs& mode_args_arg = (MyCacheModeArgs)encodable_mode_args_arg.LongValue(); + api->GetIncludedServices(address_args_arg, handle_args_arg, mode_args_arg, [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -630,13 +1146,13 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.discoverDescriptors", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getCharacteristics" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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); @@ -651,7 +1167,13 @@ void MyCentralManagerHostApi::SetUp( return; } const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); - api->DiscoverDescriptors(address_args_arg, handle_args_arg, [reply](ErrorOr&& output) { + const auto& encodable_mode_args_arg = args.at(2); + if (encodable_mode_args_arg.IsNull()) { + reply(WrapError("mode_args_arg unexpectedly null.")); + return; + } + const MyCacheModeArgs& mode_args_arg = (MyCacheModeArgs)encodable_mode_args_arg.LongValue(); + api->GetCharacteristics(address_args_arg, handle_args_arg, mode_args_arg, [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -665,13 +1187,13 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.readCharacteristic", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.getDescriptors" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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); @@ -686,7 +1208,13 @@ void MyCentralManagerHostApi::SetUp( return; } const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); - api->ReadCharacteristic(address_args_arg, handle_args_arg, [reply](ErrorOr>&& output) { + const auto& encodable_mode_args_arg = args.at(2); + if (encodable_mode_args_arg.IsNull()) { + reply(WrapError("mode_args_arg unexpectedly null.")); + return; + } + const MyCacheModeArgs& mode_args_arg = (MyCacheModeArgs)encodable_mode_args_arg.LongValue(); + api->GetDescriptors(address_args_arg, handle_args_arg, mode_args_arg, [reply](ErrorOr&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -700,13 +1228,54 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.writeCharacteristic", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.readCharacteristic" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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_mode_args_arg = args.at(2); + if (encodable_mode_args_arg.IsNull()) { + reply(WrapError("mode_args_arg unexpectedly null.")); + return; + } + const MyCacheModeArgs& mode_args_arg = (MyCacheModeArgs)encodable_mode_args_arg.LongValue(); + api->ReadCharacteristic(address_args_arg, handle_args_arg, mode_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); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.writeCharacteristic" + prepended_suffix, &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); @@ -727,13 +1296,13 @@ void MyCentralManagerHostApi::SetUp( 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.")); + const auto& encodable_type_args_arg = args.at(3); + if (encodable_type_args_arg.IsNull()) { + reply(WrapError("type_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) { + const MyGATTCharacteristicWriteTypeArgs& type_args_arg = (MyGATTCharacteristicWriteTypeArgs)encodable_type_args_arg.LongValue(); + api->WriteCharacteristic(address_args_arg, handle_args_arg, value_args_arg, type_args_arg, [reply](std::optional&& output) { if (output.has_value()) { reply(WrapError(output.value())); return; @@ -747,13 +1316,13 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.setCharacteristicNotifyState", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.setCharacteristicNotifyState" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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); @@ -768,13 +1337,13 @@ void MyCentralManagerHostApi::SetUp( 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.")); + const auto& encodable_state_args_arg = args.at(2); + if (encodable_state_args_arg.IsNull()) { + reply(WrapError("state_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) { + const MyGATTCharacteristicNotifyStateArgs& state_args_arg = (MyGATTCharacteristicNotifyStateArgs)encodable_state_args_arg.LongValue(); + api->SetCharacteristicNotifyState(address_args_arg, handle_args_arg, state_args_arg, [reply](std::optional&& output) { if (output.has_value()) { reply(WrapError(output.value())); return; @@ -788,13 +1357,13 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.readDescriptor", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.readDescriptor" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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); @@ -809,7 +1378,13 @@ void MyCentralManagerHostApi::SetUp( return; } const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); - api->ReadDescriptor(address_args_arg, handle_args_arg, [reply](ErrorOr>&& output) { + const auto& encodable_mode_args_arg = args.at(2); + if (encodable_mode_args_arg.IsNull()) { + reply(WrapError("mode_args_arg unexpectedly null.")); + return; + } + const MyCacheModeArgs& mode_args_arg = (MyCacheModeArgs)encodable_mode_args_arg.LongValue(); + api->ReadDescriptor(address_args_arg, handle_args_arg, mode_args_arg, [reply](ErrorOr>&& output) { if (output.has_error()) { reply(WrapError(output.error())); return; @@ -823,13 +1398,13 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostApi.writeDescriptor", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerHostAPI.writeDescriptor" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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); @@ -864,12 +1439,12 @@ void MyCentralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } } -EncodableValue MyCentralManagerHostApi::WrapError(std::string_view error_message) { +EncodableValue MyCentralManagerHostAPI::WrapError(std::string_view error_message) { return EncodableValue(EncodableList{ EncodableValue(std::string(error_message)), EncodableValue("Error"), @@ -877,7 +1452,7 @@ EncodableValue MyCentralManagerHostApi::WrapError(std::string_view error_message }); } -EncodableValue MyCentralManagerHostApi::WrapError(const FlutterError& error) { +EncodableValue MyCentralManagerHostAPI::WrapError(const FlutterError& error) { return EncodableValue(EncodableList{ EncodableValue(error.code()), EncodableValue(error.message()), @@ -886,24 +1461,28 @@ EncodableValue MyCentralManagerHostApi::WrapError(const FlutterError& error) { } -MyCentralManagerFlutterApiCodecSerializer::MyCentralManagerFlutterApiCodecSerializer() {} +MyCentralManagerFlutterAPICodecSerializer::MyCentralManagerFlutterAPICodecSerializer() {} -EncodableValue MyCentralManagerFlutterApiCodecSerializer::ReadValueOfType( +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)))); + return CustomEncodableValue(MyGATTCharacteristicArgs::FromEncodableList(std::get(ReadValue(stream)))); case 130: + return CustomEncodableValue(MyGATTDescriptorArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 131: + return CustomEncodableValue(MyManufacturerSpecificDataArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 132: return CustomEncodableValue(MyPeripheralArgs::FromEncodableList(std::get(ReadValue(stream)))); default: return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } } -void MyCentralManagerFlutterApiCodecSerializer::WriteValue( +void MyCentralManagerFlutterAPICodecSerializer::WriteValue( const EncodableValue& value, flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { @@ -912,13 +1491,23 @@ void MyCentralManagerFlutterApiCodecSerializer::WriteValue( WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } - if (custom_value->type() == typeid(MyManufacturerSpecificDataArgs)) { + 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(MyManufacturerSpecificDataArgs)) { + stream->WriteByte(131); WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } if (custom_value->type() == typeid(MyPeripheralArgs)) { - stream->WriteByte(130); + stream->WriteByte(132); WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } @@ -927,23 +1516,30 @@ void MyCentralManagerFlutterApiCodecSerializer::WriteValue( } // Generated class from Pigeon that represents Flutter messages that can be called from C++. -MyCentralManagerFlutterApi::MyCentralManagerFlutterApi(flutter::BinaryMessenger* binary_messenger) - : binary_messenger_(binary_messenger) {} +MyCentralManagerFlutterAPI::MyCentralManagerFlutterAPI(flutter::BinaryMessenger* binary_messenger) + : binary_messenger_(binary_messenger), + message_channel_suffix_("") {} -const flutter::StandardMessageCodec& MyCentralManagerFlutterApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance(&MyCentralManagerFlutterApiCodecSerializer::GetInstance()); +MyCentralManagerFlutterAPI::MyCentralManagerFlutterAPI( + flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix) + : binary_messenger_(binary_messenger), + message_channel_suffix_(message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : "") {} + +const flutter::StandardMessageCodec& MyCentralManagerFlutterAPI::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&MyCentralManagerFlutterAPICodecSerializer::GetInstance()); } -void MyCentralManagerFlutterApi::OnStateChanged( - int64_t state_number_args_arg, +void MyCentralManagerFlutterAPI::OnStateChanged( + const MyBluetoothLowEnergyStateArgs& state_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()); + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onStateChanged" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ - EncodableValue(state_number_args_arg), + EncodableValue((int)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) { + 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); @@ -959,20 +1555,24 @@ void MyCentralManagerFlutterApi::OnStateChanged( }); } -void MyCentralManagerFlutterApi::OnDiscovered( +void MyCentralManagerFlutterAPI::OnDiscovered( const MyPeripheralArgs& peripheral_args_arg, int64_t rssi_args_arg, + int64_t timestamp_args_arg, + const MyAdvertisementTypeArgs& type_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()); + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onDiscovered" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ CustomEncodableValue(peripheral_args_arg), EncodableValue(rssi_args_arg), + EncodableValue(timestamp_args_arg), + EncodableValue((int)type_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) { + 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); @@ -988,18 +1588,18 @@ void MyCentralManagerFlutterApi::OnDiscovered( }); } -void MyCentralManagerFlutterApi::OnConnectionStateChanged( - int64_t address_args_arg, - bool state_args_arg, +void MyCentralManagerFlutterAPI::OnConnectionStateChanged( + const MyPeripheralArgs& peripheral_args_arg, + const MyConnectionStateArgs& 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()); + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onConnectionStateChanged" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ - EncodableValue(address_args_arg), - EncodableValue(state_args_arg), + CustomEncodableValue(peripheral_args_arg), + EncodableValue((int)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) { + 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); @@ -1015,20 +1615,47 @@ void MyCentralManagerFlutterApi::OnConnectionStateChanged( }); } -void MyCentralManagerFlutterApi::OnCharacteristicNotified( - int64_t address_args_arg, - int64_t handle_args_arg, +void MyCentralManagerFlutterAPI::OnMTUChanged( + const MyPeripheralArgs& peripheral_args_arg, + int64_t mtu_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onMTUChanged" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + CustomEncodableValue(peripheral_args_arg), + EncodableValue(mtu_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( + const MyPeripheralArgs& peripheral_args_arg, + const MyGATTCharacteristicArgs& characteristic_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()); + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyCentralManagerFlutterAPI.onCharacteristicNotified" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ - EncodableValue(address_args_arg), - EncodableValue(handle_args_arg), + CustomEncodableValue(peripheral_args_arg), + CustomEncodableValue(characteristic_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) { + 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); @@ -1045,28 +1672,28 @@ void MyCentralManagerFlutterApi::OnCharacteristicNotified( } -MyPeripheralManagerHostApiCodecSerializer::MyPeripheralManagerHostApiCodecSerializer() {} +MyPeripheralManagerHostAPICodecSerializer::MyPeripheralManagerHostAPICodecSerializer() {} -EncodableValue MyPeripheralManagerHostApiCodecSerializer::ReadValueOfType( +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)))); + case 130: + return CustomEncodableValue(MyMutableGATTCharacteristicArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 131: + return CustomEncodableValue(MyMutableGATTDescriptorArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 132: + return CustomEncodableValue(MyMutableGATTServiceArgs::FromEncodableList(std::get(ReadValue(stream)))); default: return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } } -void MyPeripheralManagerHostApiCodecSerializer::WriteValue( +void MyPeripheralManagerHostAPICodecSerializer::WriteValue( const EncodableValue& value, flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { @@ -1075,45 +1702,53 @@ void MyPeripheralManagerHostApiCodecSerializer::WriteValue( 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); + stream->WriteByte(129); WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } + if (custom_value->type() == typeid(MyMutableGATTCharacteristicArgs)) { + stream->WriteByte(130); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyMutableGATTDescriptorArgs)) { + stream->WriteByte(131); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyMutableGATTServiceArgs)) { + 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()); +/// 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( +// Sets up an instance of `MyPeripheralManagerHostAPI` to handle messages through the `binary_messenger`. +void MyPeripheralManagerHostAPI::SetUp( flutter::BinaryMessenger* binary_messenger, - MyPeripheralManagerHostApi* api) { + MyPeripheralManagerHostAPI* api) { + MyPeripheralManagerHostAPI::SetUp(binary_messenger, api, ""); +} + +void MyPeripheralManagerHostAPI::SetUp( + flutter::BinaryMessenger* binary_messenger, + MyPeripheralManagerHostAPI* api, + const std::string& message_channel_suffix) { + const std::string prepended_suffix = message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : ""; { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.setUp", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.initialize" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { try { - api->SetUp([reply](std::optional&& output) { + api->Initialize([reply](std::optional&& output) { if (output.has_value()) { reply(WrapError(output.value())); return; @@ -1127,13 +1762,34 @@ void MyPeripheralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.addService", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.getState" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + ErrorOr output = api->GetState(); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue((int) std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.addService" + prepended_suffix, &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); @@ -1141,7 +1797,7 @@ void MyPeripheralManagerHostApi::SetUp( reply(WrapError("service_args_arg unexpectedly null.")); return; } - const auto& service_args_arg = std::any_cast(std::get(encodable_service_args_arg)); + 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())); @@ -1156,22 +1812,22 @@ void MyPeripheralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.removeService", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.removeService" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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.")); + const auto& encodable_hash_code_args_arg = args.at(0); + if (encodable_hash_code_args_arg.IsNull()) { + reply(WrapError("hash_code_args_arg unexpectedly null.")); return; } - const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); - std::optional output = api->RemoveService(handle_args_arg); + const int64_t hash_code_args_arg = encodable_hash_code_args_arg.LongValue(); + std::optional output = api->RemoveService(hash_code_args_arg); if (output.has_value()) { reply(WrapError(output.value())); return; @@ -1184,34 +1840,13 @@ void MyPeripheralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.clearServices", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.startAdvertising" + prepended_suffix, &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) { + 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); @@ -1220,27 +1855,26 @@ void MyPeripheralManagerHostApi::SetUp( 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))); - }); + std::optional output = api->StartAdvertising(advertisement_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); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.stopAdvertising", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.stopAdvertising" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { try { std::optional output = api->StopAdvertising(); if (output.has_value()) { @@ -1255,13 +1889,13 @@ void MyPeripheralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.sendReadCharacteristicReply", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.getMaxNotificationSize" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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); @@ -1270,25 +1904,41 @@ void MyPeripheralManagerHostApi::SetUp( 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.")); + ErrorOr output = api->GetMaxNotificationSize(address_args_arg); + if (output.has_error()) { + reply(WrapError(output.error())); 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.")); + 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); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.respondReadRequestWithValue" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_id_args_arg = args.at(0); + if (encodable_id_args_arg.IsNull()) { + reply(WrapError("id_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); + const int64_t id_args_arg = encodable_id_args_arg.LongValue(); + const auto& encodable_value_args_arg = args.at(1); 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); + std::optional output = api->RespondReadRequestWithValue(id_args_arg, value_args_arg); if (output.has_value()) { reply(WrapError(output.value())); return; @@ -1301,34 +1951,28 @@ void MyPeripheralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.sendWriteCharacteristicReply", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.respondReadRequestWithProtocolError" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + 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.")); + const auto& encodable_id_args_arg = args.at(0); + if (encodable_id_args_arg.IsNull()) { + reply(WrapError("id_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.")); + const int64_t id_args_arg = encodable_id_args_arg.LongValue(); + const auto& encodable_error_args_arg = args.at(1); + if (encodable_error_args_arg.IsNull()) { + reply(WrapError("error_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); + const MyGATTProtocolErrorArgs& error_args_arg = (MyGATTProtocolErrorArgs)encodable_error_args_arg.LongValue(); + std::optional output = api->RespondReadRequestWithProtocolError(id_args_arg, error_args_arg); if (output.has_value()) { reply(WrapError(output.value())); return; @@ -1341,13 +1985,75 @@ void MyPeripheralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } { - auto channel = std::make_unique>(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostApi.notifyCharacteristic", &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.respondWriteRequest" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel->SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_id_args_arg = args.at(0); + if (encodable_id_args_arg.IsNull()) { + reply(WrapError("id_args_arg unexpectedly null.")); + return; + } + const int64_t id_args_arg = encodable_id_args_arg.LongValue(); + std::optional output = api->RespondWriteRequest(id_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); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.respondWriteRequestWithProtocolError" + prepended_suffix, &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_id_args_arg = args.at(0); + if (encodable_id_args_arg.IsNull()) { + reply(WrapError("id_args_arg unexpectedly null.")); + return; + } + const int64_t id_args_arg = encodable_id_args_arg.LongValue(); + const auto& encodable_error_args_arg = args.at(1); + if (encodable_error_args_arg.IsNull()) { + reply(WrapError("error_args_arg unexpectedly null.")); + return; + } + const MyGATTProtocolErrorArgs& error_args_arg = (MyGATTProtocolErrorArgs)encodable_error_args_arg.LongValue(); + std::optional output = api->RespondWriteRequestWithProtocolError(id_args_arg, error_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); + } + } + { + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerHostAPI.notifyValue" + prepended_suffix, &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); @@ -1356,19 +2062,19 @@ void MyPeripheralManagerHostApi::SetUp( 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.")); + const auto& encodable_hash_code_args_arg = args.at(1); + if (encodable_hash_code_args_arg.IsNull()) { + reply(WrapError("hash_code_args_arg unexpectedly null.")); return; } - const int64_t handle_args_arg = encodable_handle_args_arg.LongValue(); + const int64_t hash_code_args_arg = encodable_hash_code_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) { + api->NotifyValue(address_args_arg, hash_code_args_arg, value_args_arg, [reply](std::optional&& output) { if (output.has_value()) { reply(WrapError(output.value())); return; @@ -1382,12 +2088,12 @@ void MyPeripheralManagerHostApi::SetUp( } }); } else { - channel->SetMessageHandler(nullptr); + channel.SetMessageHandler(nullptr); } } } -EncodableValue MyPeripheralManagerHostApi::WrapError(std::string_view error_message) { +EncodableValue MyPeripheralManagerHostAPI::WrapError(std::string_view error_message) { return EncodableValue(EncodableList{ EncodableValue(std::string(error_message)), EncodableValue("Error"), @@ -1395,7 +2101,7 @@ EncodableValue MyPeripheralManagerHostApi::WrapError(std::string_view error_mess }); } -EncodableValue MyPeripheralManagerHostApi::WrapError(const FlutterError& error) { +EncodableValue MyPeripheralManagerHostAPI::WrapError(const FlutterError& error) { return EncodableValue(EncodableList{ EncodableValue(error.code()), EncodableValue(error.message()), @@ -1404,20 +2110,26 @@ EncodableValue MyPeripheralManagerHostApi::WrapError(const FlutterError& error) } -MyPeripheralManagerFlutterApiCodecSerializer::MyPeripheralManagerFlutterApiCodecSerializer() {} +MyPeripheralManagerFlutterAPICodecSerializer::MyPeripheralManagerFlutterAPICodecSerializer() {} -EncodableValue MyPeripheralManagerFlutterApiCodecSerializer::ReadValueOfType( +EncodableValue MyPeripheralManagerFlutterAPICodecSerializer::ReadValueOfType( uint8_t type, flutter::ByteStreamReader* stream) const { switch (type) { case 128: return CustomEncodableValue(MyCentralArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 129: + return CustomEncodableValue(MyCentralArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 130: + return CustomEncodableValue(MyGATTReadRequestArgs::FromEncodableList(std::get(ReadValue(stream)))); + case 131: + return CustomEncodableValue(MyGATTWriteRequestArgs::FromEncodableList(std::get(ReadValue(stream)))); default: return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } } -void MyPeripheralManagerFlutterApiCodecSerializer::WriteValue( +void MyPeripheralManagerFlutterAPICodecSerializer::WriteValue( const EncodableValue& value, flutter::ByteStreamWriter* stream) const { if (const CustomEncodableValue* custom_value = std::get_if(&value)) { @@ -1426,28 +2138,50 @@ void MyPeripheralManagerFlutterApiCodecSerializer::WriteValue( WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); return; } + if (custom_value->type() == typeid(MyCentralArgs)) { + stream->WriteByte(129); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyGATTReadRequestArgs)) { + stream->WriteByte(130); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + if (custom_value->type() == typeid(MyGATTWriteRequestArgs)) { + stream->WriteByte(131); + 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) {} +MyPeripheralManagerFlutterAPI::MyPeripheralManagerFlutterAPI(flutter::BinaryMessenger* binary_messenger) + : binary_messenger_(binary_messenger), + message_channel_suffix_("") {} -const flutter::StandardMessageCodec& MyPeripheralManagerFlutterApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance(&MyPeripheralManagerFlutterApiCodecSerializer::GetInstance()); +MyPeripheralManagerFlutterAPI::MyPeripheralManagerFlutterAPI( + flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix) + : binary_messenger_(binary_messenger), + message_channel_suffix_(message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : "") {} + +const flutter::StandardMessageCodec& MyPeripheralManagerFlutterAPI::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&MyPeripheralManagerFlutterAPICodecSerializer::GetInstance()); } -void MyPeripheralManagerFlutterApi::OnStateChanged( - int64_t state_number_args_arg, +void MyPeripheralManagerFlutterAPI::OnStateChanged( + const MyBluetoothLowEnergyStateArgs& state_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()); + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onStateChanged" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ - EncodableValue(state_number_args_arg), + EncodableValue((int)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) { + 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); @@ -1463,18 +2197,18 @@ void MyPeripheralManagerFlutterApi::OnStateChanged( }); } -void MyPeripheralManagerFlutterApi::OnReadCharacteristicCommandReceived( +void MyPeripheralManagerFlutterAPI::OnMTUChanged( const MyCentralArgs& central_args_arg, - int64_t handle_args_arg, + int64_t mtu_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()); + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onMTUChanged" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ CustomEncodableValue(central_args_arg), - EncodableValue(handle_args_arg), + EncodableValue(mtu_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) { + 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); @@ -1490,20 +2224,20 @@ void MyPeripheralManagerFlutterApi::OnReadCharacteristicCommandReceived( }); } -void MyPeripheralManagerFlutterApi::OnWriteCharacteristicCommandReceived( +void MyPeripheralManagerFlutterAPI::OnCharacteristicReadRequest( const MyCentralArgs& central_args_arg, - int64_t handle_args_arg, - const std::vector& value_args_arg, + int64_t hash_code_args_arg, + const MyGATTReadRequestArgs& request_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()); + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onCharacteristicReadRequest" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ CustomEncodableValue(central_args_arg), - EncodableValue(handle_args_arg), - EncodableValue(value_args_arg), + EncodableValue(hash_code_args_arg), + CustomEncodableValue(request_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) { + 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); @@ -1519,20 +2253,105 @@ void MyPeripheralManagerFlutterApi::OnWriteCharacteristicCommandReceived( }); } -void MyPeripheralManagerFlutterApi::OnCharacteristicNotifyStateChanged( +void MyPeripheralManagerFlutterAPI::OnCharacteristicWriteRequest( const MyCentralArgs& central_args_arg, - int64_t handle_args_arg, - bool state_args_arg, + int64_t hash_code_args_arg, + const MyGATTWriteRequestArgs& request_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()); + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onCharacteristicWriteRequest" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ CustomEncodableValue(central_args_arg), - EncodableValue(handle_args_arg), - EncodableValue(state_args_arg), + EncodableValue(hash_code_args_arg), + CustomEncodableValue(request_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) { + 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::OnCharacteristicSubscribedClientsChanged( + int64_t hash_code_args_arg, + const EncodableList& centrals_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onCharacteristicSubscribedClientsChanged" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + EncodableValue(hash_code_args_arg), + EncodableValue(centrals_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::OnDescriptorReadRequest( + const MyCentralArgs& central_args_arg, + int64_t hash_code_args_arg, + const MyGATTReadRequestArgs& request_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onDescriptorReadRequest" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + CustomEncodableValue(central_args_arg), + EncodableValue(hash_code_args_arg), + CustomEncodableValue(request_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::OnDescriptorWriteRequest( + const MyCentralArgs& central_args_arg, + int64_t hash_code_args_arg, + const MyGATTWriteRequestArgs& request_args_arg, + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.bluetooth_low_energy_windows.MyPeripheralManagerFlutterAPI.onDescriptorWriteRequest" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(EncodableList{ + CustomEncodableValue(central_args_arg), + EncodableValue(hash_code_args_arg), + CustomEncodableValue(request_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); diff --git a/bluetooth_low_energy_windows/windows/my_api.g.h b/bluetooth_low_energy_windows/windows/my_api.g.h index b2a7d5f..2596c9b 100644 --- a/bluetooth_low_energy_windows/windows/my_api.g.h +++ b/bluetooth_low_energy_windows/windows/my_api.g.h @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v15.0.2), do not edit directly. +// Autogenerated from Pigeon (v19.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_MY_API_G_H_ @@ -48,10 +48,10 @@ template class ErrorOr { const FlutterError& error() const { return std::get(v_); }; private: - friend class MyCentralManagerHostApi; - friend class MyCentralManagerFlutterApi; - friend class MyPeripheralManagerHostApi; - friend class MyPeripheralManagerFlutterApi; + friend class MyCentralManagerHostAPI; + friend class MyCentralManagerFlutterAPI; + friend class MyPeripheralManagerHostAPI; + friend class MyPeripheralManagerFlutterAPI; ErrorOr() = default; T TakeValue() && { return std::get(std::move(v_)); } @@ -61,12 +61,27 @@ template class ErrorOr { enum class MyBluetoothLowEnergyStateArgs { unknown = 0, - disabled = 1, - off = 2, - on = 3 + unsupported = 1, + disabled = 2, + off = 3, + on = 4 }; -enum class MyGattCharacteristicPropertyArgs { +enum class MyAdvertisementTypeArgs { + connectableUndirected = 0, + connectableDirected = 1, + scannableUndirected = 2, + nonConnectableUndirected = 3, + scanResponse = 4, + extended = 5 +}; + +enum class MyConnectionStateArgs { + disconnected = 0, + connected = 1 +}; + +enum class MyGATTCharacteristicPropertyArgs { read = 0, write = 1, writeWithoutResponse = 2, @@ -74,17 +89,49 @@ enum class MyGattCharacteristicPropertyArgs { indicate = 4 }; -enum class MyGattCharacteristicWriteTypeArgs { +enum class MyGATTCharacteristicWriteTypeArgs { withResponse = 0, withoutResponse = 1 }; -enum class MyGattCharacteristicNotifyStateArgs { +enum class MyGATTCharacteristicNotifyStateArgs { none = 0, notify = 1, indicate = 2 }; +enum class MyGATTProtectionLevelArgs { + plain = 0, + authenticationRequired = 1, + entryptionRequired = 2, + encryptionAndAuthenticationRequired = 3 +}; + +enum class MyGATTProtocolErrorArgs { + invalidHandle = 0, + readNotPermitted = 1, + writeNotPermitted = 2, + invalidPDU = 3, + insufficientAuthentication = 4, + requestNotSupported = 5, + invalidOffset = 6, + insufficientAuthorization = 7, + prepareQueueFull = 8, + attributeNotFound = 9, + attributeNotLong = 10, + insufficientEncryptionKeySize = 11, + invalidAttributeValueLength = 12, + unlikelyError = 13, + insufficientEncryption = 14, + unsupportedGroupType = 15, + insufficientResources = 16 +}; + +enum class MyCacheModeArgs { + cached = 0, + uncached = 1 +}; + // Generated class from Pigeon that represents data sent in messages. class MyManufacturerSpecificDataArgs { public: @@ -103,15 +150,14 @@ class MyManufacturerSpecificDataArgs { 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; + 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_; @@ -124,14 +170,15 @@ class MyAdvertisementArgs { // 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); + const flutter::EncodableMap& service_data_args, + const flutter::EncodableList& manufacturer_specific_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 flutter::EncodableList& manufacturer_specific_data_args); const std::string* name_args() const; void set_name_args(const std::string_view* value_arg); @@ -143,26 +190,25 @@ class MyAdvertisementArgs { 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); + const flutter::EncodableList& manufacturer_specific_data_args() const; + void set_manufacturer_specific_data_args(const flutter::EncodableList& 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; + 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_; + flutter::EncodableList manufacturer_specific_data_args_; }; @@ -180,14 +226,14 @@ class MyCentralArgs { 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; + 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_; }; @@ -206,67 +252,56 @@ class MyPeripheralArgs { 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; + 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 { +class MyGATTDescriptorArgs { public: - // Constructs an object setting all non-nullable fields. - explicit MyGattDescriptorArgs( + // Constructs an object setting all 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); + 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; + 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 { +class MyGATTCharacteristicArgs { public: // Constructs an object setting all fields. - explicit MyGattCharacteristicArgs( + explicit MyGATTCharacteristicArgs( int64_t handle_args, const std::string& uuid_args, const flutter::EncodableList& property_numbers_args, @@ -286,16 +321,16 @@ class MyGattCharacteristicArgs { private: - static MyGattCharacteristicArgs FromEncodableList(const flutter::EncodableList& list); + 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; + 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_; @@ -305,12 +340,14 @@ class MyGattCharacteristicArgs { // Generated class from Pigeon that represents data sent in messages. -class MyGattServiceArgs { +class MyGATTServiceArgs { public: // Constructs an object setting all fields. - explicit MyGattServiceArgs( + explicit MyGATTServiceArgs( int64_t handle_args, const std::string& uuid_args, + bool is_primary_args, + const flutter::EncodableList& included_services_args, const flutter::EncodableList& characteristics_args); int64_t handle_args() const; @@ -319,32 +356,288 @@ class MyGattServiceArgs { const std::string& uuid_args() const; void set_uuid_args(std::string_view value_arg); + bool is_primary_args() const; + void set_is_primary_args(bool value_arg); + + const flutter::EncodableList& included_services_args() const; + void set_included_services_args(const flutter::EncodableList& 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); + 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; + 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_; + bool is_primary_args_; + flutter::EncodableList included_services_args_; flutter::EncodableList characteristics_args_; }; -class MyCentralManagerHostApiCodecSerializer : public flutter::StandardCodecSerializer { + +// Generated class from Pigeon that represents data sent in messages. +class MyMutableGATTDescriptorArgs { public: - MyCentralManagerHostApiCodecSerializer(); - inline static MyCentralManagerHostApiCodecSerializer& GetInstance() { - static MyCentralManagerHostApiCodecSerializer sInstance; + // Constructs an object setting all non-nullable fields. + explicit MyMutableGATTDescriptorArgs( + int64_t hash_code_args, + const std::string& uuid_args); + + // Constructs an object setting all fields. + explicit MyMutableGATTDescriptorArgs( + int64_t hash_code_args, + const std::string& uuid_args, + const std::vector* value_args, + const MyGATTProtectionLevelArgs* read_protection_level_args, + const MyGATTProtectionLevelArgs* write_protection_level_args); + + int64_t hash_code_args() const; + void set_hash_code_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); + + const MyGATTProtectionLevelArgs* read_protection_level_args() const; + void set_read_protection_level_args(const MyGATTProtectionLevelArgs* value_arg); + void set_read_protection_level_args(const MyGATTProtectionLevelArgs& value_arg); + + const MyGATTProtectionLevelArgs* write_protection_level_args() const; + void set_write_protection_level_args(const MyGATTProtectionLevelArgs* value_arg); + void set_write_protection_level_args(const MyGATTProtectionLevelArgs& value_arg); + + + private: + static MyMutableGATTDescriptorArgs 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 hash_code_args_; + std::string uuid_args_; + std::optional> value_args_; + std::optional read_protection_level_args_; + std::optional write_protection_level_args_; + +}; + + +// Generated class from Pigeon that represents data sent in messages. +class MyMutableGATTCharacteristicArgs { + public: + // Constructs an object setting all non-nullable fields. + explicit MyMutableGATTCharacteristicArgs( + int64_t hash_code_args, + const std::string& uuid_args, + const flutter::EncodableList& property_numbers_args, + const flutter::EncodableList& descriptors_args); + + // Constructs an object setting all fields. + explicit MyMutableGATTCharacteristicArgs( + int64_t hash_code_args, + const std::string& uuid_args, + const std::vector* value_args, + const flutter::EncodableList& property_numbers_args, + const MyGATTProtectionLevelArgs* read_protection_level_args, + const MyGATTProtectionLevelArgs* write_protection_level_args, + const flutter::EncodableList& descriptors_args); + + int64_t hash_code_args() const; + void set_hash_code_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); + + const flutter::EncodableList& property_numbers_args() const; + void set_property_numbers_args(const flutter::EncodableList& value_arg); + + const MyGATTProtectionLevelArgs* read_protection_level_args() const; + void set_read_protection_level_args(const MyGATTProtectionLevelArgs* value_arg); + void set_read_protection_level_args(const MyGATTProtectionLevelArgs& value_arg); + + const MyGATTProtectionLevelArgs* write_protection_level_args() const; + void set_write_protection_level_args(const MyGATTProtectionLevelArgs* value_arg); + void set_write_protection_level_args(const MyGATTProtectionLevelArgs& value_arg); + + const flutter::EncodableList& descriptors_args() const; + void set_descriptors_args(const flutter::EncodableList& value_arg); + + + private: + static MyMutableGATTCharacteristicArgs 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 hash_code_args_; + std::string uuid_args_; + std::optional> value_args_; + flutter::EncodableList property_numbers_args_; + std::optional read_protection_level_args_; + std::optional write_protection_level_args_; + flutter::EncodableList descriptors_args_; + +}; + + +// Generated class from Pigeon that represents data sent in messages. +class MyMutableGATTServiceArgs { + public: + // Constructs an object setting all fields. + explicit MyMutableGATTServiceArgs( + int64_t hash_code_args, + const std::string& uuid_args, + bool is_primary_args, + const flutter::EncodableList& included_services_args, + const flutter::EncodableList& characteristics_args); + + int64_t hash_code_args() const; + void set_hash_code_args(int64_t value_arg); + + const std::string& uuid_args() const; + void set_uuid_args(std::string_view value_arg); + + bool is_primary_args() const; + void set_is_primary_args(bool value_arg); + + const flutter::EncodableList& included_services_args() const; + void set_included_services_args(const flutter::EncodableList& value_arg); + + const flutter::EncodableList& characteristics_args() const; + void set_characteristics_args(const flutter::EncodableList& value_arg); + + + private: + static MyMutableGATTServiceArgs 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 hash_code_args_; + std::string uuid_args_; + bool is_primary_args_; + flutter::EncodableList included_services_args_; + flutter::EncodableList characteristics_args_; + +}; + + +// Generated class from Pigeon that represents data sent in messages. +class MyGATTReadRequestArgs { + public: + // Constructs an object setting all fields. + explicit MyGATTReadRequestArgs( + int64_t id_args, + int64_t offset_args, + int64_t length_args); + + int64_t id_args() const; + void set_id_args(int64_t value_arg); + + int64_t offset_args() const; + void set_offset_args(int64_t value_arg); + + int64_t length_args() const; + void set_length_args(int64_t value_arg); + + + private: + static MyGATTReadRequestArgs 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 id_args_; + int64_t offset_args_; + int64_t length_args_; + +}; + + +// Generated class from Pigeon that represents data sent in messages. +class MyGATTWriteRequestArgs { + public: + // Constructs an object setting all fields. + explicit MyGATTWriteRequestArgs( + int64_t id_args, + int64_t offset_args, + const std::vector& value_args, + const MyGATTCharacteristicWriteTypeArgs& type_args); + + int64_t id_args() const; + void set_id_args(int64_t value_arg); + + int64_t offset_args() const; + void set_offset_args(int64_t value_arg); + + const std::vector& value_args() const; + void set_value_args(const std::vector& value_arg); + + const MyGATTCharacteristicWriteTypeArgs& type_args() const; + void set_type_args(const MyGATTCharacteristicWriteTypeArgs& value_arg); + + + private: + static MyGATTWriteRequestArgs 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 id_args_; + int64_t offset_args_; + std::vector value_args_; + MyGATTCharacteristicWriteTypeArgs type_args_; + +}; + +class MyCentralManagerHostAPICodecSerializer : public flutter::StandardCodecSerializer { + public: + MyCentralManagerHostAPICodecSerializer(); + inline static MyCentralManagerHostAPICodecSerializer& GetInstance() { + static MyCentralManagerHostAPICodecSerializer sInstance; return sInstance; } @@ -360,47 +653,59 @@ class MyCentralManagerHostApiCodecSerializer : public flutter::StandardCodecSeri }; // Generated interface from Pigeon that represents a handler of messages from Flutter. -class MyCentralManagerHostApi { +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; + MyCentralManagerHostAPI(const MyCentralManagerHostAPI&) = delete; + MyCentralManagerHostAPI& operator=(const MyCentralManagerHostAPI&) = delete; + virtual ~MyCentralManagerHostAPI() {} + virtual void Initialize(std::function reply)> result) = 0; + virtual ErrorOr GetState() = 0; + virtual std::optional StartDiscovery(const flutter::EncodableList& service_u_u_i_ds_args) = 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( + virtual ErrorOr GetMTU(int64_t address_args) = 0; + virtual void GetServices( int64_t address_args, + const MyCacheModeArgs& mode_args, std::function reply)> result) = 0; - virtual void DiscoverCharacteristics( + virtual void GetIncludedServices( int64_t address_args, int64_t handle_args, + const MyCacheModeArgs& mode_args, std::function reply)> result) = 0; - virtual void DiscoverDescriptors( + virtual void GetCharacteristics( int64_t address_args, int64_t handle_args, + const MyCacheModeArgs& mode_args, + std::function reply)> result) = 0; + virtual void GetDescriptors( + int64_t address_args, + int64_t handle_args, + const MyCacheModeArgs& mode_args, std::function reply)> result) = 0; virtual void ReadCharacteristic( int64_t address_args, int64_t handle_args, + const MyCacheModeArgs& mode_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, + const MyGATTCharacteristicWriteTypeArgs& type_args, std::function reply)> result) = 0; virtual void SetCharacteristicNotifyState( int64_t address_args, int64_t handle_args, - int64_t state_number_args, + const MyGATTCharacteristicNotifyStateArgs& state_args, std::function reply)> result) = 0; virtual void ReadDescriptor( int64_t address_args, int64_t handle_args, + const MyCacheModeArgs& mode_args, std::function> reply)> result) = 0; virtual void WriteDescriptor( int64_t address_args, @@ -408,24 +713,28 @@ class MyCentralManagerHostApi { const std::vector& value_args, std::function reply)> result) = 0; - // The codec used by MyCentralManagerHostApi. + // The codec used by MyCentralManagerHostAPI. static const flutter::StandardMessageCodec& GetCodec(); - // Sets up an instance of `MyCentralManagerHostApi` to handle messages through the `binary_messenger`. + // Sets up an instance of `MyCentralManagerHostAPI` to handle messages through the `binary_messenger`. static void SetUp( flutter::BinaryMessenger* binary_messenger, - MyCentralManagerHostApi* api); + MyCentralManagerHostAPI* api); + static void SetUp( + flutter::BinaryMessenger* binary_messenger, + MyCentralManagerHostAPI* api, + const std::string& message_channel_suffix); static flutter::EncodableValue WrapError(std::string_view error_message); static flutter::EncodableValue WrapError(const FlutterError& error); protected: - MyCentralManagerHostApi() = default; + MyCentralManagerHostAPI() = default; }; -class MyCentralManagerFlutterApiCodecSerializer : public flutter::StandardCodecSerializer { +class MyCentralManagerFlutterAPICodecSerializer : public flutter::StandardCodecSerializer { public: - MyCentralManagerFlutterApiCodecSerializer(); - inline static MyCentralManagerFlutterApiCodecSerializer& GetInstance() { - static MyCentralManagerFlutterApiCodecSerializer sInstance; + MyCentralManagerFlutterAPICodecSerializer(); + inline static MyCentralManagerFlutterAPICodecSerializer& GetInstance() { + static MyCentralManagerFlutterAPICodecSerializer sInstance; return sInstance; } @@ -441,41 +750,52 @@ class MyCentralManagerFlutterApiCodecSerializer : public flutter::StandardCodecS }; // Generated class from Pigeon that represents Flutter messages that can be called from C++. -class MyCentralManagerFlutterApi { +class MyCentralManagerFlutterAPI { public: - MyCentralManagerFlutterApi(flutter::BinaryMessenger* binary_messenger); + MyCentralManagerFlutterAPI(flutter::BinaryMessenger* binary_messenger); + MyCentralManagerFlutterAPI( + flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix); static const flutter::StandardMessageCodec& GetCodec(); void OnStateChanged( - int64_t state_number_args, + const MyBluetoothLowEnergyStateArgs& state_args, std::function&& on_success, std::function&& on_error); void OnDiscovered( const MyPeripheralArgs& peripheral_args, int64_t rssi_args, + int64_t timestamp_args, + const MyAdvertisementTypeArgs& type_args, const MyAdvertisementArgs& advertisement_args, std::function&& on_success, std::function&& on_error); void OnConnectionStateChanged( - int64_t address_args, - bool state_args, + const MyPeripheralArgs& peripheral_args, + const MyConnectionStateArgs& state_args, + std::function&& on_success, + std::function&& on_error); + void OnMTUChanged( + const MyPeripheralArgs& peripheral_args, + int64_t mtu_args, std::function&& on_success, std::function&& on_error); void OnCharacteristicNotified( - int64_t address_args, - int64_t handle_args, + const MyPeripheralArgs& peripheral_args, + const MyGATTCharacteristicArgs& characteristic_args, const std::vector& value_args, std::function&& on_success, std::function&& on_error); private: flutter::BinaryMessenger* binary_messenger_; + std::string message_channel_suffix_; }; -class MyPeripheralManagerHostApiCodecSerializer : public flutter::StandardCodecSerializer { +class MyPeripheralManagerHostAPICodecSerializer : public flutter::StandardCodecSerializer { public: - MyPeripheralManagerHostApiCodecSerializer(); - inline static MyPeripheralManagerHostApiCodecSerializer& GetInstance() { - static MyPeripheralManagerHostApiCodecSerializer sInstance; + MyPeripheralManagerHostAPICodecSerializer(); + inline static MyPeripheralManagerHostAPICodecSerializer& GetInstance() { + static MyPeripheralManagerHostAPICodecSerializer sInstance; return sInstance; } @@ -491,54 +811,58 @@ class MyPeripheralManagerHostApiCodecSerializer : public flutter::StandardCodecS }; // Generated interface from Pigeon that represents a handler of messages from Flutter. -class MyPeripheralManagerHostApi { +class MyPeripheralManagerHostAPI { public: - MyPeripheralManagerHostApi(const MyPeripheralManagerHostApi&) = delete; - MyPeripheralManagerHostApi& operator=(const MyPeripheralManagerHostApi&) = delete; - virtual ~MyPeripheralManagerHostApi() {} - virtual void SetUp(std::function reply)> result) = 0; + MyPeripheralManagerHostAPI(const MyPeripheralManagerHostAPI&) = delete; + MyPeripheralManagerHostAPI& operator=(const MyPeripheralManagerHostAPI&) = delete; + virtual ~MyPeripheralManagerHostAPI() {} + virtual void Initialize(std::function reply)> result) = 0; + virtual ErrorOr GetState() = 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, + const MyMutableGATTServiceArgs& service_args, std::function reply)> result) = 0; + virtual std::optional RemoveService(int64_t hash_code_args) = 0; + virtual std::optional StartAdvertising(const MyAdvertisementArgs& advertisement_args) = 0; virtual std::optional StopAdvertising() = 0; - virtual std::optional SendReadCharacteristicReply( - int64_t address_args, - int64_t handle_args, - bool status_args, + virtual ErrorOr GetMaxNotificationSize(int64_t address_args) = 0; + virtual std::optional RespondReadRequestWithValue( + int64_t id_args, const std::vector& value_args) = 0; - virtual std::optional SendWriteCharacteristicReply( + virtual std::optional RespondReadRequestWithProtocolError( + int64_t id_args, + const MyGATTProtocolErrorArgs& error_args) = 0; + virtual std::optional RespondWriteRequest(int64_t id_args) = 0; + virtual std::optional RespondWriteRequestWithProtocolError( + int64_t id_args, + const MyGATTProtocolErrorArgs& error_args) = 0; + virtual void NotifyValue( int64_t address_args, - int64_t handle_args, - bool status_args) = 0; - virtual void NotifyCharacteristic( - int64_t address_args, - int64_t handle_args, + int64_t hash_code_args, const std::vector& value_args, std::function reply)> result) = 0; - // The codec used by MyPeripheralManagerHostApi. + // The codec used by MyPeripheralManagerHostAPI. static const flutter::StandardMessageCodec& GetCodec(); - // Sets up an instance of `MyPeripheralManagerHostApi` to handle messages through the `binary_messenger`. + // Sets up an instance of `MyPeripheralManagerHostAPI` to handle messages through the `binary_messenger`. static void SetUp( flutter::BinaryMessenger* binary_messenger, - MyPeripheralManagerHostApi* api); + MyPeripheralManagerHostAPI* api); + static void SetUp( + flutter::BinaryMessenger* binary_messenger, + MyPeripheralManagerHostAPI* api, + const std::string& message_channel_suffix); static flutter::EncodableValue WrapError(std::string_view error_message); static flutter::EncodableValue WrapError(const FlutterError& error); protected: - MyPeripheralManagerHostApi() = default; + MyPeripheralManagerHostAPI() = default; }; -class MyPeripheralManagerFlutterApiCodecSerializer : public flutter::StandardCodecSerializer { +class MyPeripheralManagerFlutterAPICodecSerializer : public flutter::StandardCodecSerializer { public: - MyPeripheralManagerFlutterApiCodecSerializer(); - inline static MyPeripheralManagerFlutterApiCodecSerializer& GetInstance() { - static MyPeripheralManagerFlutterApiCodecSerializer sInstance; + MyPeripheralManagerFlutterAPICodecSerializer(); + inline static MyPeripheralManagerFlutterAPICodecSerializer& GetInstance() { + static MyPeripheralManagerFlutterAPICodecSerializer sInstance; return sInstance; } @@ -554,34 +878,55 @@ class MyPeripheralManagerFlutterApiCodecSerializer : public flutter::StandardCod }; // Generated class from Pigeon that represents Flutter messages that can be called from C++. -class MyPeripheralManagerFlutterApi { +class MyPeripheralManagerFlutterAPI { public: - MyPeripheralManagerFlutterApi(flutter::BinaryMessenger* binary_messenger); + MyPeripheralManagerFlutterAPI(flutter::BinaryMessenger* binary_messenger); + MyPeripheralManagerFlutterAPI( + flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix); static const flutter::StandardMessageCodec& GetCodec(); void OnStateChanged( - int64_t state_number_args, + const MyBluetoothLowEnergyStateArgs& state_args, std::function&& on_success, std::function&& on_error); - void OnReadCharacteristicCommandReceived( + void OnMTUChanged( const MyCentralArgs& central_args, - int64_t handle_args, + int64_t mtu_args, std::function&& on_success, std::function&& on_error); - void OnWriteCharacteristicCommandReceived( + void OnCharacteristicReadRequest( const MyCentralArgs& central_args, - int64_t handle_args, - const std::vector& value_args, + int64_t hash_code_args, + const MyGATTReadRequestArgs& request_args, std::function&& on_success, std::function&& on_error); - void OnCharacteristicNotifyStateChanged( + void OnCharacteristicWriteRequest( const MyCentralArgs& central_args, - int64_t handle_args, - bool state_args, + int64_t hash_code_args, + const MyGATTWriteRequestArgs& request_args, + std::function&& on_success, + std::function&& on_error); + void OnCharacteristicSubscribedClientsChanged( + int64_t hash_code_args, + const flutter::EncodableList& centrals_args, + std::function&& on_success, + std::function&& on_error); + void OnDescriptorReadRequest( + const MyCentralArgs& central_args, + int64_t hash_code_args, + const MyGATTReadRequestArgs& request_args, + std::function&& on_success, + std::function&& on_error); + void OnDescriptorWriteRequest( + const MyCentralArgs& central_args, + int64_t hash_code_args, + const MyGATTWriteRequestArgs& request_args, std::function&& on_success, std::function&& on_error); private: flutter::BinaryMessenger* binary_messenger_; + std::string message_channel_suffix_; }; } // namespace bluetooth_low_energy_windows diff --git a/bluetooth_low_energy_windows/windows/my_central_manager.cpp b/bluetooth_low_energy_windows/windows/my_central_manager.cpp index 5c5bdb9..739f03a 100644 --- a/bluetooth_low_energy_windows/windows/my_central_manager.cpp +++ b/bluetooth_low_energy_windows/windows/my_central_manager.cpp @@ -1,639 +1,746 @@ -// 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" +#undef _HAS_EXCEPTIONS #include "my_central_manager.h" -#include "my_exception.h" namespace bluetooth_low_energy_windows { - MyCentralManager::MyCentralManager(flutter::BinaryMessenger* messenger) + MyCentralManager::MyCentralManager(flutter::BinaryMessenger *messenger) { - m_api = MyCentralManagerFlutterApi(messenger); - m_watcher = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher(); + const auto api = MyCentralManagerFlutterAPI(messenger); + const auto watcher = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher(); + watcher.ScanningMode(winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEScanningMode::Active); + m_api = api; + m_watcher = watcher; } MyCentralManager::~MyCentralManager() { } - void MyCentralManager::SetUp(std::functionreply)> result) + void MyCentralManager::Initialize(std::function reply)> result) { - m_set_up(std::move(result)); + InitializeAsync(std::move(result)); } - std::optional MyCentralManager::StartDiscovery() + ErrorOr MyCentralManager::GetState() { - m_watcher->Start(); - return std::nullopt; + try + { + const auto has_adapter = m_adapter.has_value(); + if (has_adapter) + { + const auto &adapter = m_adapter.value(); + const auto supported = adapter.IsCentralRoleSupported(); + if (supported) + { + const auto &radio = m_radio.value(); + const auto state = radio.State(); + const auto state_args = RadioStateToArgs(state); + return state_args; + } + } + return MyBluetoothLowEnergyStateArgs::unsupported; + } + 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); + return FlutterError(code, message); + } + } + + std::optional MyCentralManager::StartDiscovery(const flutter::EncodableList &service_uuids_args) + { + try + { + const auto &watcher = m_watcher.value(); + const auto filter = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementFilter::BluetoothLEAdvertisementFilter(); + const auto advertisement = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisement(); + const auto service_uuids = advertisement.ServiceUuids(); + for (const auto &service_uuid_args_value : service_uuids_args) + { + const auto &service_uuid_args = std::get(service_uuid_args_value); + const auto service_uuid = winrt::guid(service_uuid_args); + service_uuids.Append(service_uuid); + } + filter.Advertisement(advertisement); + watcher.AdvertisementFilter(filter); + watcher.Start(); + return 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); + return FlutterError(code, message); + } } std::optional MyCentralManager::StopDiscovery() { - m_watcher->Stop(); - return std::nullopt; + try + { + const auto &watcher = m_watcher.value(); + watcher.Stop(); + return 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); + return FlutterError(code, message); + } } - void MyCentralManager::Connect(int64_t address_args, std::functionreply)> result) + void MyCentralManager::Connect(int64_t address_args, std::function reply)> result) { - m_connect(address_args, std::move(result)); + ConnectAsync(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) {}); + OnDisconnected(address_args); + auto &api = m_api.value(); + const auto peripheral_args = MyPeripheralArgs(address_args); + const auto state_args = MyConnectionStateArgs::disconnected; + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnConnectionStateChanged(peripheral_args, state_args, [] {}, [](auto error) {}); return std::nullopt; } - catch (const std::exception& ex) + catch (const winrt::hresult_error &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."; + const auto code = "winrt::hresult_error"; + const auto winrt_message = ex.message(); + const auto message = winrt::to_string(winrt_message); 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) + ErrorOr MyCentralManager::GetMTU(int64_t address_args) { 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) + const auto &session = m_sessions[address_args].value(); + const auto mtu = session.MaxPduSize(); + const auto mtu_args = static_cast(mtu); + return mtu_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); + return FlutterError(code, message); + } + } + + void MyCentralManager::GetServices(int64_t address_args, const MyCacheModeArgs &mode_args, std::function reply)> result) + { + GetServicesAsync(address_args, mode_args, std::move(result)); + } + + void MyCentralManager::GetIncludedServices(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result) + { + GetIncludedServicesAsync(address_args, handle_args, mode_args, std::move(result)); + } + + void MyCentralManager::GetCharacteristics(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result) + { + GetCharacteristicsAsync(address_args, handle_args, mode_args, std::move(result)); + } + + void MyCentralManager::GetDescriptors(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result) + { + GetDescriptorsAsync(address_args, handle_args, mode_args, std::move(result)); + } + + void MyCentralManager::ReadCharacteristic(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function> reply)> result) + { + ReadCharacteristicAsync(address_args, handle_args, mode_args, std::move(result)); + } + + void MyCentralManager::WriteCharacteristic(int64_t address_args, int64_t handle_args, const std::vector &value_args, const MyGATTCharacteristicWriteTypeArgs &type_args, std::function reply)> result) + { + WriteCharacteristicAsync(address_args, handle_args, value_args, type_args, std::move(result)); + } + + void MyCentralManager::SetCharacteristicNotifyState(int64_t address_args, int64_t handle_args, const MyGATTCharacteristicNotifyStateArgs &state_args, std::function reply)> result) + { + SetCharacteristicNotifyStateAsync(address_args, handle_args, state_args, std::move(result)); + } + + void MyCentralManager::ReadDescriptor(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function> reply)> result) + { + ReadDescriptorAsync(address_args, handle_args, mode_args, std::move(result)); + } + + void MyCentralManager::WriteDescriptor(int64_t address_args, int64_t handle_args, const std::vector &value_args, std::function reply)> result) + { + WriteDescriptorAsync(address_args, handle_args, value_args, std::move(result)); + } + + winrt::fire_and_forget MyCentralManager::InitializeAsync(std::function reply)> result) + { + try + { + const auto &watcher = m_watcher.value(); + const auto status = watcher.Status(); + if (status == winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcherStatus::Started) { - 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) {}); - }); + watcher.Stop(); } + + for (const auto &item : m_devices) + { + const auto address_args = item.first; + OnDisconnected(address_args); + } + + const auto &adapter = co_await winrt::Windows::Devices::Bluetooth::BluetoothAdapter::GetDefaultAsync(); + if (adapter != NULL) + { + const auto supported = adapter.IsCentralRoleSupported(); + if (supported) + { + const auto &radio = co_await adapter.GetRadioAsync(); + m_radio_state_changed_revoker = radio.StateChanged( + winrt::auto_revoke, + [this](winrt::Windows::Devices::Radios::Radio radio, auto obj) + { + auto &api = m_api.value(); + const auto state = radio.State(); + const auto state_args = RadioStateToArgs(state); + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnStateChanged(state_args, [] {}, [](auto error) {}); + }); + m_radio = radio; + } + else + { + m_radio.reset(); + } + m_adapter = adapter; + } + else + { + m_adapter.reset(); + } + + m_watcher_received_revoker = watcher.Received( + winrt::auto_revoke, + [this](winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher watcher, winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementReceivedEventArgs event_args) + { + auto &api = m_api.value(); + const auto address = event_args.BluetoothAddress(); + const auto address_args = static_cast(address); + const auto peripheral_args = MyPeripheralArgs(address_args); + const auto rssi = event_args.RawSignalStrengthInDBm(); + const auto rssi_args = static_cast(rssi); + const auto &file_timestamp = event_args.Timestamp(); + const auto timestamp = std::chrono::clock_cast(file_timestamp).time_since_epoch(); + const auto timestamp_args = std::chrono::duration_cast(timestamp).count(); + const auto type = event_args.AdvertisementType(); + const auto type_args = AdvertisementTypeToArgs(type); + const auto advertisement = event_args.Advertisement(); + const auto advertisement_args = AdvertisementToArgs(advertisement); + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnDiscovered(peripheral_args, rssi_args, timestamp_args, type_args, advertisement_args, [] {}, [](auto error) {}); + }); result(std::nullopt); } - catch (const winrt::hresult_error& ex) { + 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) + 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) + winrt::fire_and_forget MyCentralManager::ConnectAsync(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 &device = co_await winrt::Windows::Devices::Bluetooth::BluetoothLEDevice::FromBluetoothAddressAsync(address); + const auto id = device.BluetoothDeviceId(); + const auto &session = co_await winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattSession::FromDeviceIdAsync(id); + // Creating a BluetoothLEDevice object by calling this method alone doesn't (necessarily) initiate a + // connection. To initiate a connection, set GattSession.MaintainConnection to true, or call an + // uncached service discovery method on BluetoothLEDevice, or perform a read/write operation against + // the device. + // See:https://learn.microsoft.com/en-us/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 status = r.Status(); + if (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); + const auto status_code = static_cast(status); + const auto message = "Connect failed with status: " + std::to_string(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) + auto &api = m_api.value(); + const auto peripheral_args = MyPeripheralArgs(address_args); + const auto state_args = MyConnectionStateArgs::connected; + const auto mtu = session.MaxPduSize(); + const auto mtu_args = static_cast(mtu); + api.OnConnectionStateChanged(peripheral_args, state_args, [] {}, [](auto error) {}); + api.OnMTUChanged(peripheral_args, mtu_args, [] {}, [](auto error) {}); + m_device_connection_status_changed_revokers[address_args] = device.ConnectionStatusChanged( + winrt::auto_revoke, + [this, address_args](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); + OnDisconnected(address_args); } + auto &api = m_api.value(); + const auto peripheral_args = MyPeripheralArgs(address_args); + const auto state_args = ConnectionStatusToArgs(status); + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnConnectionStateChanged(peripheral_args, state_args, [] {}, [](auto error) {}); }); + m_session_max_pdu_size_changed_revokers[address_args] = session.MaxPduSizeChanged( + winrt::auto_revoke, + [this, peripheral_args](winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattSession session, auto obj) + { + auto &api = m_api.value(); + const auto mtu = session.MaxPduSize(); + const auto mtu_args = static_cast(mtu); + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnMTUChanged(peripheral_args, mtu_args, [] {}, [](auto error) {}); + }); + m_devices[address_args] = device; + m_sessions[address_args] = session; result(std::nullopt); } - catch (const winrt::hresult_error& ex) { + 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) + 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) + winrt::fire_and_forget MyCentralManager::GetServicesAsync(int64_t address_args, const MyCacheModeArgs &mode_args, std::function reply)> result) { try { - const auto& device = m_devices[address_args]; - const auto& r = co_await device->GetGattServicesAsync(winrt::Windows::Devices::Bluetooth::BluetoothCacheMode::Uncached); + const auto &device = RetrieveDevice(address_args); + const auto mode = ArgsToCacheMode(mode_args); + const auto &r = co_await device.GetGattServicesAsync(mode); 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); + const auto status_code = static_cast(status); + const auto message = "Get 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]; + const auto services = r.Services(); 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); + for (const auto service : services) + { + const auto service_args = ServiceToArgs(service); + const auto service_args_value = flutter::CustomEncodableValue(service_args); + const auto service_handle_args = service_args.handle_args(); + m_services[address_args][service_handle_args] = service; + services_args.emplace_back(service_args_value); } result(services_args); } - catch (const winrt::hresult_error& ex) { + 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) + 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."; + } + + winrt::fire_and_forget MyCentralManager::GetIncludedServicesAsync(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result) + { + try + { + const auto &service = RetrieveService(address_args, handle_args); + const auto mode = ArgsToCacheMode(mode_args); + const auto &r = co_await service.GetIncludedServicesAsync(mode); + const auto status = r.Status(); + if (status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) + { + const auto status_code = static_cast(status); + const auto message = "Get included services failed with status: " + std::to_string(status_code); + throw MyException(message); + } + const auto included_services = r.Services(); + auto included_services_args = flutter::EncodableList(); + for (const auto included_service : included_services) + { + const auto service_args = ServiceToArgs(included_service); + const auto service_args_value = flutter::CustomEncodableValue(service_args); + const auto service_handle_args = service_args.handle_args(); + m_services[address_args][service_handle_args] = included_service; + included_services_args.emplace_back(service_args_value); + } + result(included_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); } } - winrt::fire_and_forget MyCentralManager::m_discover_characteristics(int64_t address_args, int64_t handle_args, std::functionreply)> result) + winrt::fire_and_forget MyCentralManager::GetCharacteristicsAsync(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result) { try { - const auto& service = m_retrieve_service(address_args, handle_args); - const auto& r = co_await service->GetCharacteristicsAsync(winrt::Windows::Devices::Bluetooth::BluetoothCacheMode::Uncached); + const auto &service = RetrieveService(address_args, handle_args); + const auto mode = ArgsToCacheMode(mode_args); + const auto &r = co_await service.GetCharacteristicsAsync(mode); 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); + const auto status_code = static_cast(status); + const auto message = "Get 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]; + const auto characteristics = r.Characteristics(); 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) + for (const auto characteristic : characteristics) + { + const auto characteristic_args = CharacteristicToArgs(characteristic); + const auto characteristic_args_value = flutter::CustomEncodableValue(characteristic_args); + const auto characteristic_handle_args = characteristic_args.handle_args(); + m_characteristic_value_changed_revokers[address_args][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) {}); + auto &api = m_api.value(); + const auto peripheral_args = MyPeripheralArgs(address_args); + const auto characteristic_args = CharacteristicToArgs(characteristic); + const auto value = event_args.CharacteristicValue(); + const auto value_length = value.Length(); + auto value_args = std::vector(value_length); + const auto value_reader = winrt::Windows::Storage::Streams::DataReader::FromBuffer(value); + value_reader.ReadBytes(value_args); + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnCharacteristicNotified(peripheral_args, characteristic_args, value_args, []() {}, [](const auto &error) {}); }); - characteristics_args.emplace_back(characteristic_args); + m_characteristics[address_args][characteristic_handle_args] = characteristic; + characteristics_args.emplace_back(characteristic_args_value); } result(characteristics_args); } - catch (const winrt::hresult_error& ex) { + 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) + 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) + winrt::fire_and_forget MyCentralManager::GetDescriptorsAsync(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result) { try { - const auto& characteristic = m_retrieve_characteristic(address_args, handle_args); - const auto& r = co_await characteristic->GetDescriptorsAsync(winrt::Windows::Devices::Bluetooth::BluetoothCacheMode::Uncached); + const auto &characteristic = RetrieveCharacteristic(address_args, handle_args); + const auto mode = ArgsToCacheMode(mode_args); + const auto &r = co_await characteristic.GetDescriptorsAsync(mode); 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); + const auto status_code = static_cast(status); + const auto message = "Get 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]; + const auto descriptors = r.Descriptors(); 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); + for (const auto descriptor : descriptors) + { + const auto descriptor_args = DescriptorToArgs(descriptor); + const auto descriptor_args_value = flutter::CustomEncodableValue(descriptor_args); + const auto descriptor_handle_args = descriptor_args.handle_args(); + m_descriptors[address_args][descriptor_handle_args] = descriptor; + descriptors_args.emplace_back(descriptor_args_value); } result(descriptors_args); } - catch (const winrt::hresult_error& ex) { + 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) + 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) + winrt::fire_and_forget MyCentralManager::ReadCharacteristicAsync(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function> reply)> result) { try { - const auto& characteristic = m_retrieve_characteristic(address_args, handle_args); - const auto& r = co_await characteristic->ReadValueAsync(winrt::Windows::Devices::Bluetooth::BluetoothCacheMode::Uncached); + const auto &characteristic = RetrieveCharacteristic(address_args, handle_args); + const auto mode = ArgsToCacheMode(mode_args); + const auto &r = co_await characteristic.ReadValueAsync(mode); const auto status = r.Status(); if (status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) { - const auto status_code = static_cast(status); + 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); + const auto value = r.Value(); + const auto value_length = value.Length(); + auto value_args = std::vector(value_length); + const auto value_reader = winrt::Windows::Storage::Streams::DataReader::FromBuffer(value); + value_reader.ReadBytes(value_args); result(value_args); } - catch (const winrt::hresult_error& ex) { + 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) + 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) + winrt::fire_and_forget MyCentralManager::WriteCharacteristicAsync(int64_t address_args, int64_t handle_args, const std::vector &value_args, const MyGATTCharacteristicWriteTypeArgs &type_args, std::function reply)> 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); + const auto &characteristic = RetrieveCharacteristic(address_args, handle_args); + const auto value_writer = winrt::Windows::Storage::Streams::DataWriter(); + value_writer.WriteBytes(value_args); + const auto value = value_writer.DetachBuffer(); + const auto option = ArgsToWriteOption(type_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 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) { + 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) + 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) + winrt::fire_and_forget MyCentralManager::SetCharacteristicNotifyStateAsync(int64_t address_args, int64_t handle_args, const MyGATTCharacteristicNotifyStateArgs &state_args, std::function reply)> 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); + const auto &characteristic = RetrieveCharacteristic(address_args, handle_args); + const auto value = ArgsToCCCDescriptorValue(state_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 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) { + 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) + 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) + winrt::fire_and_forget MyCentralManager::ReadDescriptorAsync(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function> reply)> result) { try { - const auto& descriptor = m_retrieve_descriptor(address_args, handle_args); - const auto& r = co_await descriptor->ReadValueAsync(winrt::Windows::Devices::Bluetooth::BluetoothCacheMode::Uncached); + const auto &descriptor = RetrieveDescriptor(address_args, handle_args); + const auto mode = ArgsToCacheMode(mode_args); + const auto &r = co_await descriptor.ReadValueAsync(mode); const auto status = r.Status(); if (status != winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCommunicationStatus::Success) { - const auto status_code = static_cast(status); + 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); + const auto value = r.Value(); + const auto value_length = value.Length(); + auto value_args = std::vector(value_length); + const auto value_reader = winrt::Windows::Storage::Streams::DataReader::FromBuffer(value); + value_reader.ReadBytes(value_args); result(value_args); } - catch (const winrt::hresult_error& ex) { + 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) + 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) + winrt::fire_and_forget MyCentralManager::WriteDescriptorAsync(int64_t address_args, int64_t handle_args, const std::vector &value_args, std::function reply)> 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); + const auto &descriptor = RetrieveDescriptor(address_args, handle_args); + const auto value_writer = winrt::Windows::Storage::Streams::DataWriter(); + value_writer.WriteBytes(value_args); + const auto value = value_writer.DetachBuffer(); + 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 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) { + 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) + 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() + void MyCentralManager::OnDisconnected(int64_t address_args) { - // 停止扫描 - 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_session_max_pdu_size_changed_revokers.erase(address_args); m_characteristic_value_changed_revokers.erase(address_args); m_devices.erase(address_args); + m_sessions.erase(address_args); m_services.erase(address_args); m_characteristics.erase(address_args); m_descriptors.erase(address_args); } - void MyCentralManager::m_on_state_changed() + winrt::Windows::Devices::Bluetooth::BluetoothLEDevice &MyCentralManager::RetrieveDevice(int64_t address_args) { - 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) {}); + return m_devices[address_args].value(); } - MyBluetoothLowEnergyStateArgs MyCentralManager::m_radio_state_to_args(winrt::Windows::Devices::Radios::RadioState state) + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDeviceService &MyCentralManager::RetrieveService(int64_t address_args, int64_t handle_args) + { + return m_services[address_args][handle_args].value(); + } + + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristic &MyCentralManager::RetrieveCharacteristic(int64_t address_args, int64_t handle_args) + { + return m_characteristics[address_args][handle_args].value(); + } + + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDescriptor &MyCentralManager::RetrieveDescriptor(int64_t address_args, int64_t handle_args) + { + return m_descriptors[address_args][handle_args].value(); + } + + MyBluetoothLowEnergyStateArgs MyCentralManager::RadioStateToArgs(const winrt::Windows::Devices::Radios::RadioState &state) { switch (state) { @@ -650,29 +757,76 @@ namespace bluetooth_low_energy_windows } } - MyAdvertisementArgs MyCentralManager::m_advertisement_to_args(const winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisement& advertisement) + MyAdvertisementTypeArgs MyCentralManager::AdvertisementTypeToArgs(const winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementType &type) + { + switch (type) + { + case winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementType::ConnectableUndirected: + return MyAdvertisementTypeArgs::connectableUndirected; + case winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementType::ConnectableDirected: + return MyAdvertisementTypeArgs::connectableDirected; + case winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementType::ScannableUndirected: + return MyAdvertisementTypeArgs::scannableUndirected; + case winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementType::NonConnectableUndirected: + return MyAdvertisementTypeArgs::nonConnectableUndirected; + case winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementType::ScanResponse: + return MyAdvertisementTypeArgs::scanResponse; + case winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementType::Extended: + return MyAdvertisementTypeArgs::extended; + default: + throw std::bad_cast(); + } + } + + MyConnectionStateArgs MyCentralManager::ConnectionStatusToArgs(const winrt::Windows::Devices::Bluetooth::BluetoothConnectionStatus &status) + { + switch (status) + { + case winrt::Windows::Devices::Bluetooth::BluetoothConnectionStatus::Disconnected: + return MyConnectionStateArgs::disconnected; + case winrt::Windows::Devices::Bluetooth::BluetoothConnectionStatus::Connected: + return MyConnectionStateArgs::connected; + default: + throw std::bad_cast(); + } + } + + MyManufacturerSpecificDataArgs MyCentralManager::ManufacturerDataToArgs(const winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEManufacturerData &manufacturer_data) + { + const auto id = manufacturer_data.CompanyId(); + const auto id_args = static_cast(id); + const auto data = manufacturer_data.Data(); + const auto data_length = data.Length(); + auto data_args = std::vector(data_length); + const auto data_reader = winrt::Windows::Storage::Streams::DataReader::FromBuffer(data); + data_reader.ReadBytes(data_args); + return MyManufacturerSpecificDataArgs(id_args, data_args); + } + + MyAdvertisementArgs MyCentralManager::AdvertisementToArgs(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(); + const auto name_args = winrt::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); + for (const auto &service_uuid : service_uuids) + { + const auto service_uuid_args = GUIDToArgs(service_uuid); + service_uuids_args.emplace_back(service_uuid_args); } - const auto& data_sections = advertisement.DataSections(); + const auto data_sections = advertisement.DataSections(); auto service_data_args = flutter::EncodableMap(); - for (const auto& data_section : data_sections) + for (const auto data_section : data_sections) { const auto section_type = data_section.DataType(); - const auto& section_buffer = data_section.Data(); + 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(); + 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); @@ -681,7 +835,7 @@ namespace bluetooth_low_energy_windows } else if (section_type == type32 && section_data_length > 4) { - const auto& section_data = section_buffer.data(); + 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); @@ -690,7 +844,7 @@ namespace bluetooth_low_energy_windows } else if (section_type == type128 && section_data_length > 16) { - const auto& section_data = section_buffer.data(); + const auto section_data = section_buffer.data(); auto data1 = uint32_t(); std::memcpy(&data1, section_data, 4Ui64); auto data2 = uint16_t(); @@ -700,60 +854,47 @@ namespace bluetooth_low_energy_windows 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 uuid_args = GUIDToArgs(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 manufacturer_data = advertisement.ManufacturerData(); + auto manufacturer_specific_data_args = flutter::EncodableList(); + for (const auto &data : manufacturer_data) { - 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); + const auto data_args = ManufacturerDataToArgs(data); + const auto data_args_value = flutter::CustomEncodableValue(data_args); + manufacturer_specific_data_args.emplace_back(data_args_value); } + return MyAdvertisementArgs(&name_args, service_uuids_args, service_data_args, manufacturer_specific_data_args); } - 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) + MyGATTServiceArgs MyCentralManager::ServiceToArgs(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 uuid_args = GUIDToArgs(uuid); + const auto is_primary_args = true; + const auto included_services_args = flutter::EncodableList(); const auto characteristics_args = flutter::EncodableList(); - return MyGattServiceArgs(handle_args, uuid_args, characteristics_args); + return MyGATTServiceArgs(handle_args, uuid_args, is_primary_args, included_services_args, characteristics_args); } - MyGattCharacteristicArgs MyCentralManager::m_characteristic_to_args(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristic& characteristic) + MyGATTCharacteristicArgs MyCentralManager::CharacteristicToArgs(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 uuid = characteristic.Uuid(); + const auto uuid_args = GUIDToArgs(uuid); const auto properties = characteristic.CharacteristicProperties(); - const auto property_numbers_args = m_characteristic_properties_to_args(properties); + const auto property_numbers_args = CharacteristicPropertiesToArgs(properties); const auto descriptors_args = flutter::EncodableList(); - return MyGattCharacteristicArgs(handle_args, uuid_args, property_numbers_args, descriptors_args); + 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) + flutter::EncodableList MyCentralManager::CharacteristicPropertiesToArgs(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); @@ -763,109 +904,86 @@ namespace bluetooth_low_energy_windows auto property_numbers_args = flutter::EncodableList(); if (readable) { - const auto property_number_args = static_cast(MyGattCharacteristicPropertyArgs::read); + 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); + 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); + 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); + 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); + 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) + MyGATTDescriptorArgs MyCentralManager::DescriptorToArgs(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); + const auto uuid = descriptor.Uuid(); + const auto uuid_args = GUIDToArgs(uuid); + return MyGATTDescriptorArgs(handle_args, uuid_args); } - std::string MyCentralManager::m_uuid_to_args(const winrt::guid& uuid) + std::string MyCentralManager::GUIDToArgs(const winrt::guid &guid) { - //const auto uuid_value = winrt::to_hstring(uuid); - //return winrt::to_string(uuid_value); - return std::format("{}", uuid); + // const auto uuid_value = winrt::to_hstring(uuid); + // return winrt::to_string(uuid_value); + return std::format("{}", guid); } - winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption MyCentralManager::m_write_type_number_args_to_write_option(int64_t type_number_args) + winrt::Windows::Devices::Bluetooth::BluetoothCacheMode MyCentralManager::ArgsToCacheMode(const MyCacheModeArgs &mode_args) { - switch (type_number_args) + switch (mode_args) { - case static_cast(MyGattCharacteristicWriteTypeArgs::withoutResponse): + case MyCacheModeArgs::cached: + return winrt::Windows::Devices::Bluetooth::BluetoothCacheMode::Cached; + case MyCacheModeArgs::uncached: + return winrt::Windows::Devices::Bluetooth::BluetoothCacheMode::Uncached; + default: + throw std::bad_cast(); + } + } + + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption MyCentralManager::ArgsToWriteOption(const MyGATTCharacteristicWriteTypeArgs &type_args) + { + switch (type_args) + { + case MyGATTCharacteristicWriteTypeArgs::withoutResponse: return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption::WriteWithoutResponse; - case static_cast(MyGattCharacteristicWriteTypeArgs::withResponse): + case 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) + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue MyCentralManager::ArgsToCCCDescriptorValue(const MyGATTCharacteristicNotifyStateArgs &state_args) { - switch (state_number_args) + switch (state_args) { - case static_cast(MyGattCharacteristicNotifyStateArgs::none): + case MyGATTCharacteristicNotifyStateArgs::none: return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue::None; - case static_cast(MyGattCharacteristicNotifyStateArgs::notify): + case MyGATTCharacteristicNotifyStateArgs::notify: return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue::Notify; - case static_cast(MyGattCharacteristicNotifyStateArgs::indicate): + case 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 index 52e8d7f..8fe9741 100644 --- a/bluetooth_low_energy_windows/windows/my_central_manager.h +++ b/bluetooth_low_energy_windows/windows/my_central_manager.h @@ -1,82 +1,100 @@ #ifndef BLEW_MY_CENTRAL_MANAGER_H_ #define BLEW_MY_CENTRAL_MANAGER_H_ +#include +#include + #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 "winrt/Windows.Foundation.h" +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/Windows.Storage.Streams.h" #include "my_api.g.h" +#include "my_exception.h" +#include "my_format.h" namespace bluetooth_low_energy_windows { - class MyCentralManager : public MyCentralManagerHostApi + class MyCentralManager : public MyCentralManagerHostAPI { public: - MyCentralManager(flutter::BinaryMessenger* messenger); + MyCentralManager(flutter::BinaryMessenger *messenger); virtual ~MyCentralManager(); // Disallow copy and assign. - MyCentralManager(const MyCentralManager&) = delete; - MyCentralManager& operator=(const MyCentralManager&) = delete; + MyCentralManager(const MyCentralManager &) = delete; + MyCentralManager &operator=(const MyCentralManager &) = delete; - // 通过 MyCentralManagerHostApi 继承 - void SetUp(std::functionreply)> result) override; - std::optional StartDiscovery() override; + void Initialize(std::function reply)> result) override; + ErrorOr GetState() override; + std::optional StartDiscovery(const flutter::EncodableList &service_uuids_args) override; std::optional StopDiscovery() override; - void Connect(int64_t address_args, std::functionreply)> result) override; + void Connect(int64_t address_args, std::function reply)> 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; + ErrorOr GetMTU(int64_t address_args) override; + void GetServices(int64_t address_args, const MyCacheModeArgs &mode_args, std::function reply)> result) override; + void GetIncludedServices(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result) override; + void GetCharacteristics(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result) override; + void GetDescriptors(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result) override; + void ReadCharacteristic(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function> reply)> result) override; + void WriteCharacteristic(int64_t address_args, int64_t handle_args, const std::vector &value_args, const MyGATTCharacteristicWriteTypeArgs &type_args, std::function reply)> result) override; + void SetCharacteristicNotifyState(int64_t address_args, int64_t handle_args, const MyGATTCharacteristicNotifyStateArgs &state_args, std::function reply)> result) override; + void ReadDescriptor(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function> reply)> result) override; + void WriteDescriptor(int64_t address_args, int64_t handle_args, const std::vector &value_args, std::function reply)> result) override; + private: - std::optional m_api; + std::optional m_api; std::optional m_watcher; std::optional m_adapter; std::optional m_radio; std::map> m_devices; + std::map> m_sessions; 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_session_max_pdu_size_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); + winrt::fire_and_forget InitializeAsync(std::function reply)> result); + winrt::fire_and_forget ConnectAsync(int64_t address_args, std::function reply)> result); + winrt::fire_and_forget GetServicesAsync(int64_t address_args, const MyCacheModeArgs &mode_args, std::function reply)> result); + winrt::fire_and_forget GetIncludedServicesAsync(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result); + winrt::fire_and_forget GetCharacteristicsAsync(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result); + winrt::fire_and_forget GetDescriptorsAsync(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function reply)> result); + winrt::fire_and_forget ReadCharacteristicAsync(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function> reply)> result); + winrt::fire_and_forget WriteCharacteristicAsync(int64_t address_args, int64_t handle_args, const std::vector &value_args, const MyGATTCharacteristicWriteTypeArgs &type_args, std::function reply)> result); + winrt::fire_and_forget SetCharacteristicNotifyStateAsync(int64_t address_args, int64_t handle_args, const MyGATTCharacteristicNotifyStateArgs &state_args, std::function reply)> result); + winrt::fire_and_forget ReadDescriptorAsync(int64_t address_args, int64_t handle_args, const MyCacheModeArgs &mode_args, std::function> reply)> result); + winrt::fire_and_forget WriteDescriptorAsync(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); + void OnDisconnected(int64_t address_args); + + winrt::Windows::Devices::Bluetooth::BluetoothLEDevice &RetrieveDevice(int64_t address_args); + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDeviceService &RetrieveService(int64_t address_args, int64_t handle_args); + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristic &RetrieveCharacteristic(int64_t address, int64_t handle_args); + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDescriptor &RetrieveDescriptor(int64_t address_args, int64_t handle_args); + + MyBluetoothLowEnergyStateArgs RadioStateToArgs(const winrt::Windows::Devices::Radios::RadioState &state); + MyAdvertisementTypeArgs AdvertisementTypeToArgs(const winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementType &type); + MyConnectionStateArgs ConnectionStatusToArgs(const winrt::Windows::Devices::Bluetooth::BluetoothConnectionStatus &status); + MyManufacturerSpecificDataArgs ManufacturerDataToArgs(const winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEManufacturerData &manufacturer_data); + MyAdvertisementArgs AdvertisementToArgs(const winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisement &advertisement); + MyGATTServiceArgs ServiceToArgs(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDeviceService &service); + MyGATTCharacteristicArgs CharacteristicToArgs(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristic &characteristic); + flutter::EncodableList CharacteristicPropertiesToArgs(winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties properties); + MyGATTDescriptorArgs DescriptorToArgs(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDescriptor &descriptor); + std::string GUIDToArgs(const winrt::guid &guid); + + winrt::Windows::Devices::Bluetooth::BluetoothCacheMode ArgsToCacheMode(const MyCacheModeArgs &mode_args); + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption ArgsToWriteOption(const MyGATTCharacteristicWriteTypeArgs &type_args); + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattClientCharacteristicConfigurationDescriptorValue ArgsToCCCDescriptorValue(const MyGATTCharacteristicNotifyStateArgs &state_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 index 39882aa..9e5da8e 100644 --- a/bluetooth_low_energy_windows/windows/my_exception.cpp +++ b/bluetooth_low_energy_windows/windows/my_exception.cpp @@ -1,9 +1,3 @@ -#include "my_exception.h" +#undef _HAS_EXCEPTIONS -namespace bluetooth_low_energy_windows -{ - const char* MyException::what() const noexcept - { - return message.c_str(); - } -} \ No newline at end of file +#include "my_exception.h" diff --git a/bluetooth_low_energy_windows/windows/my_exception.h b/bluetooth_low_energy_windows/windows/my_exception.h index d94043b..870724f 100644 --- a/bluetooth_low_energy_windows/windows/my_exception.h +++ b/bluetooth_low_energy_windows/windows/my_exception.h @@ -1,22 +1,24 @@ #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) - { - } +namespace bluetooth_low_energy_windows +{ + class MyException : public std::exception + { + public: + MyException(const std::string &message) : message(message) {} + ~MyException() {} - const char* what() const noexcept override; - private: - std::string message; - }; + const char *what() const noexcept override + { + return message.c_str(); + } + private: + std::string message; + }; } -#endif \ No newline at end of file +#endif // !BLEW_MY_EXCEPTION_H_ diff --git a/bluetooth_low_energy_windows/windows/my_format.cpp b/bluetooth_low_energy_windows/windows/my_format.cpp new file mode 100644 index 0000000..3b6b93d --- /dev/null +++ b/bluetooth_low_energy_windows/windows/my_format.cpp @@ -0,0 +1 @@ +#include "my_format.h" diff --git a/bluetooth_low_energy_windows/windows/my_format.h b/bluetooth_low_energy_windows/windows/my_format.h new file mode 100644 index 0000000..c266522 --- /dev/null +++ b/bluetooth_low_energy_windows/windows/my_format.h @@ -0,0 +1,24 @@ +#ifndef BLEW_MY_FORMAT_H_ +#define BLEW_MY_FORMAT_H_ + +#include "winrt/base.h" + +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; + } +}; + +#endif // !BLEW_MY_FORMAT_H_ diff --git a/bluetooth_low_energy_windows/windows/my_peripheral_manager.cpp b/bluetooth_low_energy_windows/windows/my_peripheral_manager.cpp new file mode 100644 index 0000000..609217a --- /dev/null +++ b/bluetooth_low_energy_windows/windows/my_peripheral_manager.cpp @@ -0,0 +1,887 @@ +#undef _HAS_EXCEPTIONS + +#include "my_peripheral_manager.h" + +namespace bluetooth_low_energy_windows +{ + MyPeripheralManager::MyPeripheralManager(flutter::BinaryMessenger *messenger) + { + const auto api = MyPeripheralManagerFlutterAPI(messenger); + const auto publisher = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementPublisher(); + m_api = api; + m_publisher = publisher; + } + + MyPeripheralManager::~MyPeripheralManager() + { + } + + void MyPeripheralManager::Initialize(std::function reply)> result) + { + InitializeAsync(std::move(result)); + } + + ErrorOr MyPeripheralManager::GetState() + { + try + { + const auto has_adapter = m_adapter.has_value(); + if (has_adapter) + { + const auto &adapter = m_adapter.value(); + const auto supported = adapter.IsPeripheralRoleSupported(); + if (supported) + { + const auto &radio = m_radio.value(); + const auto state = radio.State(); + const auto state_args = RadioStateToArgs(state); + return state_args; + } + } + return MyBluetoothLowEnergyStateArgs::unsupported; + } + 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); + return FlutterError(code, message); + } + } + + void MyPeripheralManager::AddService(const MyMutableGATTServiceArgs &service_args, std::function reply)> result) + { + AddServiceAsync(service_args, std::move(result)); + } + + std::optional MyPeripheralManager::RemoveService(int64_t hash_code_args) + { + try + { + const auto &service_provider = m_service_providers[hash_code_args].value(); + const auto &service_args = m_services_args[hash_code_args].value(); + service_provider.StopAdvertising(); + RemoveServiceArgs(service_args); + m_service_providers.erase(hash_code_args); + m_services_args.erase(hash_code_args); + return 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); + return FlutterError(code, message); + } + } + + std::optional MyPeripheralManager::StartAdvertising(const MyAdvertisementArgs &advertisement_args) + { + try + { + const auto &publisher = m_publisher.value(); + const auto advertisement = publisher.Advertisement(); + // When configuring the publisher object, you can't add restricted section types + // (BluetoothLEAdvertisementPublisher.Advertisement.Flags and + // BluetoothLEAdvertisementPublisher.Advertisement.LocalName). Trying to set those property values results in a + // runtime exception. You can still set the manufacturer data section, or any other sections not defined by the list of + // restrictions. + // See: https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementpublisher.advertisement?view=winrt-22621 + // const auto name_args = advertisement_args.name_args(); + // if (name_args) + // { + // const auto name = winrt::to_hstring(*name_args); + // advertisement.LocalName(name); + // } + const auto service_uuids = advertisement.ServiceUuids(); + const auto &service_uuids_args = advertisement_args.service_u_u_i_ds_args(); + for (const auto &service_uuid_args_value : service_uuids_args) + { + const auto &service_uuid_args = std::get(service_uuid_args_value); + const auto service_uuid = winrt::guid(service_uuid_args); + service_uuids.Append(service_uuid); + } + const auto data_sections = advertisement.DataSections(); + const auto &service_data_args = advertisement_args.service_data_args(); + for (const auto &data_args_value : service_data_args) + { + const auto service_data = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementDataSection(); + const auto data_type = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementDataTypes::ServiceData128BitUuids(); + service_data.DataType(data_type); + const auto data_writer = winrt::Windows::Storage::Streams::DataWriter(); + const auto &uuid_args = std::get(data_args_value.first); + const auto uuid = winrt::guid(uuid_args); + data_writer.WriteGuid(uuid); + const auto &data_args = std::get>(data_args_value.second); + data_writer.WriteBytes(data_args); + const auto data = data_writer.DetachBuffer(); + service_data.Data(data); + data_sections.Append(service_data); + } + const auto manufacturer_data = advertisement.ManufacturerData(); + const auto &manufacturer_specific_data_args = advertisement_args.manufacturer_specific_data_args(); + for (const auto &data_args_value : manufacturer_specific_data_args) + { + const auto &custom_data_args_value = std::get(data_args_value); + const auto data_args = std::any_cast(custom_data_args_value); + const auto data = ArgsToManufacturerData(data_args); + manufacturer_data.Append(data); + } + publisher.Start(); + return 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); + return FlutterError(code, message); + } + } + + std::optional MyPeripheralManager::StopAdvertising() + { + try + { + const auto &publisher = m_publisher.value(); + publisher.Stop(); + return 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); + return FlutterError(code, message); + } + } + + ErrorOr MyPeripheralManager::GetMaxNotificationSize(int64_t address_args) + { + try + { + const auto &client = m_clients[address_args].value(); + const auto max_notification_size = client.MaxNotificationSize(); + const auto max_notification_size_args = static_cast(max_notification_size); + return max_notification_size_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); + return FlutterError(code, message); + } + } + + std::optional MyPeripheralManager::RespondReadRequestWithValue(int64_t id_args, const std::vector &value_args) + { + try + { + const auto &deferal = m_deferrals[id_args].value(); + const auto &request = m_read_requests[id_args].value(); + const auto value_writer = winrt::Windows::Storage::Streams::DataWriter(); + value_writer.WriteBytes(value_args); + const auto value = value_writer.DetachBuffer(); + request.RespondWithValue(value); + deferal.Complete(); + m_deferrals.erase(id_args); + m_read_requests.erase(id_args); + return 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); + return FlutterError(code, message); + } + } + + std::optional MyPeripheralManager::RespondReadRequestWithProtocolError(int64_t id_args, const MyGATTProtocolErrorArgs &error_args) + { + try + { + const auto &deferal = m_deferrals[id_args].value(); + const auto &request = m_read_requests[id_args].value(); + const auto error = ArgsToProtocolError(error_args); + request.RespondWithProtocolError(error); + deferal.Complete(); + m_deferrals.erase(id_args); + m_read_requests.erase(id_args); + return 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); + return FlutterError(code, message); + } + } + + std::optional MyPeripheralManager::RespondWriteRequest(int64_t id_args) + { + try + { + const auto &deferal = m_deferrals[id_args].value(); + const auto &request = m_write_requests[id_args].value(); + request.Respond(); + deferal.Complete(); + m_deferrals.erase(id_args); + m_write_requests.erase(id_args); + return 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); + return FlutterError(code, message); + } + } + + std::optional MyPeripheralManager::RespondWriteRequestWithProtocolError(int64_t id_args, const MyGATTProtocolErrorArgs &error_args) + { + try + { + const auto &deferal = m_deferrals[id_args].value(); + const auto &request = m_write_requests[id_args].value(); + const auto error = ArgsToProtocolError(error_args); + request.RespondWithProtocolError(error); + deferal.Complete(); + m_deferrals.erase(id_args); + m_write_requests.erase(id_args); + return 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); + return FlutterError(code, message); + } + } + + void MyPeripheralManager::NotifyValue(int64_t address_args, int64_t hash_code_args, const std::vector &value_args, std::function reply)> result) + { + NotifyValueAsync(address_args, hash_code_args, value_args, std::move(result)); + } + + winrt::fire_and_forget MyPeripheralManager::InitializeAsync(std::function reply)> result) + { + try + { + const auto &publisher = m_publisher.value(); + const auto status = publisher.Status(); + if (status == winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementPublisherStatus::Started) + { + publisher.Stop(); + } + auto hash_codes_args = std::list(); + std::transform( + m_services_args.begin(), + m_services_args.end(), + std::back_inserter(hash_codes_args), + [](const auto item) + { + return item.first; + }); + for (const auto hash_code_args : hash_codes_args) + { + const auto &service_provider = m_service_providers[hash_code_args].value(); + const auto &service_args = m_services_args[hash_code_args].value(); + service_provider.StopAdvertising(); + RemoveServiceArgs(service_args); + m_service_providers.erase(hash_code_args); + m_services_args.erase(hash_code_args); + } + + const auto &adapter = co_await winrt::Windows::Devices::Bluetooth::BluetoothAdapter::GetDefaultAsync(); + if (adapter != NULL) + { + const auto supported = adapter.IsPeripheralRoleSupported(); + if (supported) + { + const auto &radio = co_await adapter.GetRadioAsync(); + m_radio_state_changed_revoker = radio.StateChanged( + winrt::auto_revoke, + [this](winrt::Windows::Devices::Radios::Radio radio, auto obj) + { + auto &api = m_api.value(); + const auto state = radio.State(); + const auto state_args = RadioStateToArgs(state); + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnStateChanged(state_args, [] {}, [](auto error) {}); + }); + m_radio = radio; + } + else + { + m_radio.reset(); + } + m_adapter = adapter; + } + else + { + m_adapter.reset(); + } + 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); + } + } + + winrt::fire_and_forget MyPeripheralManager::AddServiceAsync(const MyMutableGATTServiceArgs &service_args, std::function reply)> result) + { + try + { + co_await CreateServiceAsync(service_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); + } + } + + winrt::fire_and_forget MyPeripheralManager::NotifyValueAsync(int64_t address_args, int64_t hash_code_args, const std::vector &value_args, std::function reply)> result) + { + try + { + const auto &client = m_clients[address_args].value(); + const auto &characteristic = m_characteristics[hash_code_args].value(); + const auto value_writer = winrt::Windows::Storage::Streams::DataWriter(); + value_writer.WriteBytes(value_args); + const auto value = value_writer.DetachBuffer(); + co_await characteristic.NotifyValueAsync(value, client); + 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); + } + } + + winrt::Windows::Foundation::IAsyncAction MyPeripheralManager::CreateServiceAsync(const MyMutableGATTServiceArgs service_args) + { + const auto hash_code_args = service_args.hash_code_args(); + const auto &uuid_args = service_args.uuid_args(); + const auto uuid = winrt::guid(uuid_args); + const auto &r = co_await winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattServiceProvider::CreateAsync(uuid); + const auto error = r.Error(); + if (error != winrt::Windows::Devices::Bluetooth::BluetoothError::Success) + { + const auto error_code = static_cast(error); + const auto message = "Create service failed with error: " + std::to_string(error_code); + throw MyException(message); + } + const auto service_provider = r.ServiceProvider(); + const auto service = service_provider.Service(); + const auto &characteristics_args = service_args.characteristics_args(); + for (const auto &characteristic_args_value : characteristics_args) + { + const auto &custom_characteristic_args_value = std::get(characteristic_args_value); + const auto characteristic_args = std::any_cast(custom_characteristic_args_value); + co_await CreateCharacteristicAsync(service, characteristic_args); + } + const auto parameters = winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattServiceProviderAdvertisingParameters(); + parameters.IsDiscoverable(true); + parameters.IsConnectable(true); + service_provider.StartAdvertising(parameters); + m_services_args[hash_code_args] = service_args; + m_service_providers[hash_code_args] = service_provider; + } + + winrt::Windows::Foundation::IAsyncAction MyPeripheralManager::CreateCharacteristicAsync(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalService &service, const MyMutableGATTCharacteristicArgs characteristic_args) + { + const auto hash_code_args = characteristic_args.hash_code_args(); + const auto &uuid_args = characteristic_args.uuid_args(); + const auto uuid = winrt::guid(uuid_args); + const auto parameters = winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalCharacteristicParameters(); + const auto value_args = characteristic_args.value_args(); + if (value_args) + { + const auto value_writer = winrt::Windows::Storage::Streams::DataWriter(); + value_writer.WriteBytes(*value_args); + const auto value = value_writer.DetachBuffer(); + parameters.StaticValue(value); + } + const auto &property_numbers_args = characteristic_args.property_numbers_args(); + const auto properties = ArgsToCharacteristicProperties(property_numbers_args); + parameters.CharacteristicProperties(properties); + const auto read_protection_level_args = characteristic_args.read_protection_level_args(); + if (read_protection_level_args) + { + const auto read_protection_level = ArgsToProtectionLevel(*read_protection_level_args); + parameters.ReadProtectionLevel(read_protection_level); + } + const auto write_protection_level_args = characteristic_args.write_protection_level_args(); + if (write_protection_level_args) + { + const auto write_protection_level = ArgsToProtectionLevel(*write_protection_level_args); + parameters.WriteProtectionLevel(write_protection_level); + } + const auto &r = co_await service.CreateCharacteristicAsync(uuid, parameters); + const auto error = r.Error(); + if (error != winrt::Windows::Devices::Bluetooth::BluetoothError::Success) + { + const auto error_code = static_cast(error); + const auto message = "Create characteristic failed with error: " + std::to_string(error_code); + throw MyException(message); + } + const auto characteristic = r.Characteristic(); + m_characteristic_read_requested_revokers[hash_code_args] = characteristic.ReadRequested( + winrt::auto_revoke, + [this, hash_code_args](winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalCharacteristic characteristic, winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattReadRequestedEventArgs event_args) + { + OnCharacteristicReadRequestedAsync(hash_code_args, event_args); + }); + m_characteristic_write_requested_revokers[hash_code_args] = characteristic.WriteRequested( + winrt::auto_revoke, + [this, hash_code_args](winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalCharacteristic characteristic, winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteRequestedEventArgs event_args) + { + OnCharacteristicWriteRequestedAsync(hash_code_args, event_args); + }); + m_characteristic_subscribed_clients_changed_revokers[hash_code_args] = characteristic.SubscribedClientsChanged( + winrt::auto_revoke, + [this, hash_code_args](winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalCharacteristic characteristic, auto obj) + { + OnCharacteristicSubscribedClientsChangedAsync(hash_code_args, characteristic); + }); + const auto &descriptors_args = characteristic_args.descriptors_args(); + for (const auto &descriptor_args_value : descriptors_args) + { + const auto &custom_descriptor_args_value = std::get(descriptor_args_value); + const auto descriptor_args = std::any_cast(custom_descriptor_args_value); + co_await CreateDescriptorAsync(characteristic, descriptor_args); + } + m_characteristics[hash_code_args] = characteristic; + } + + winrt::Windows::Foundation::IAsyncAction MyPeripheralManager::CreateDescriptorAsync(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalCharacteristic &characteristic, const MyMutableGATTDescriptorArgs descriptor_args) + { + const auto hash_code_args = descriptor_args.hash_code_args(); + const auto &uuid_args = descriptor_args.uuid_args(); + const auto uuid = winrt::guid(uuid_args); + const auto parameters = winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalDescriptorParameters(); + const auto value_args = descriptor_args.value_args(); + if (value_args) + { + const auto value_writer = winrt::Windows::Storage::Streams::DataWriter(); + value_writer.WriteBytes(*value_args); + const auto value = value_writer.DetachBuffer(); + parameters.StaticValue(value); + } + const auto read_protection_level_args = descriptor_args.read_protection_level_args(); + if (read_protection_level_args) + { + const auto read_protection_level = ArgsToProtectionLevel(*read_protection_level_args); + parameters.ReadProtectionLevel(read_protection_level); + } + const auto write_protection_level_args = descriptor_args.write_protection_level_args(); + if (write_protection_level_args) + { + const auto write_protection_level = ArgsToProtectionLevel(*write_protection_level_args); + parameters.WriteProtectionLevel(write_protection_level); + } + const auto &r = co_await characteristic.CreateDescriptorAsync(uuid, parameters); + const auto error = r.Error(); + if (error != winrt::Windows::Devices::Bluetooth::BluetoothError::Success) + { + const auto error_code = static_cast(error); + const auto message = "Create descriptor failed with error: " + std::to_string(error_code); + throw MyException(message); + } + const auto descriptor = r.Descriptor(); + m_descriptor_read_requested_revokers[hash_code_args] = descriptor.ReadRequested( + winrt::auto_revoke, + [this, hash_code_args](winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalDescriptor descriptor, winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattReadRequestedEventArgs event_args) + { + OnDescriptorReadRequestedAsync(hash_code_args, event_args); + }); + m_descriptor_write_requested_revokers[hash_code_args] = descriptor.WriteRequested( + winrt::auto_revoke, + [this, hash_code_args](winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalDescriptor descriptor, winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteRequestedEventArgs event_args) + { + OnDescriptorWriteRequestedAsync(hash_code_args, event_args); + }); + } + + void MyPeripheralManager::RemoveServiceArgs(const MyMutableGATTServiceArgs &service_args) + { + const auto &characteristics_args = service_args.characteristics_args(); + for (const auto &characteristic_args_value : characteristics_args) + { + const auto &custom_characteristic_args_value = std::get(characteristic_args_value); + const auto characteristic_args = std::any_cast(custom_characteristic_args_value); + RemoveCharacteristicArgs(characteristic_args); + } + } + + void MyPeripheralManager::RemoveCharacteristicArgs(const MyMutableGATTCharacteristicArgs &characteristic_args) + { + const auto hash_code_args = characteristic_args.hash_code_args(); + const auto &descriptors_args = characteristic_args.descriptors_args(); + for (const auto &descriptor_args_value : descriptors_args) + { + const auto &custom_descriptor_args_value = std::get(descriptor_args_value); + const auto descriptor_args = std::any_cast(custom_descriptor_args_value); + RemoveDescriptorArgs(descriptor_args); + } + m_characteristic_read_requested_revokers.erase(hash_code_args); + m_characteristic_write_requested_revokers.erase(hash_code_args); + m_characteristic_subscribed_clients_changed_revokers.erase(hash_code_args); + m_characteristics.erase(hash_code_args); + } + + void MyPeripheralManager::RemoveDescriptorArgs(const MyMutableGATTDescriptorArgs &descriptor_args) + { + const auto hash_code_args = descriptor_args.hash_code_args(); + m_descriptor_read_requested_revokers.erase(hash_code_args); + m_descriptor_write_requested_revokers.erase(hash_code_args); + } + + winrt::fire_and_forget MyPeripheralManager::OnCharacteristicReadRequestedAsync(const int64_t hash_code_args, const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattReadRequestedEventArgs event_args) + { + auto &api = m_api.value(); + const auto deferral = event_args.GetDeferral(); + const auto session = event_args.Session(); + const auto device_id = session.DeviceId().Id(); + const auto &device = co_await winrt::Windows::Devices::Bluetooth::BluetoothLEDevice::FromIdAsync(device_id); + const auto address = device.BluetoothAddress(); + const auto address_args = static_cast(address); + const auto central_args = MyCentralArgs(address_args); + m_session_max_pdu_size_changed_revokers[address_args] = session.MaxPduSizeChanged( + winrt::auto_revoke, + [this, central_args](winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattSession session, auto obj) + { + auto &api = m_api.value(); + const auto mtu = session.MaxPduSize(); + const auto mtu_args = static_cast(mtu); + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnMTUChanged(central_args, mtu_args, [] {}, [](auto error) {}); + }); + const auto &request = co_await event_args.GetRequestAsync(); + const auto id = std::addressof(request); + const auto id_args = reinterpret_cast(id); + const auto offset = request.Offset(); + const auto offset_args = static_cast(offset); + const auto length = request.Length(); + const auto length_args = static_cast(length); + m_deferrals[id_args] = deferral; + m_read_requests[id_args] = request; + const auto request_args = MyGATTReadRequestArgs(id_args, offset_args, length_args); + api.OnCharacteristicReadRequest(central_args, hash_code_args, request_args, []() {}, [](const auto &error) {}); + } + + winrt::fire_and_forget MyPeripheralManager::OnCharacteristicWriteRequestedAsync(const int64_t hash_code_args, const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteRequestedEventArgs event_args) + { + auto &api = m_api.value(); + const auto deferral = event_args.GetDeferral(); + const auto session = event_args.Session(); + const auto device_id = session.DeviceId().Id(); + const auto &device = co_await winrt::Windows::Devices::Bluetooth::BluetoothLEDevice::FromIdAsync(device_id); + const auto address = device.BluetoothAddress(); + const auto address_args = static_cast(address); + const auto central_args = MyCentralArgs(address_args); + m_session_max_pdu_size_changed_revokers[address_args] = session.MaxPduSizeChanged( + winrt::auto_revoke, + [this, central_args](winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattSession session, auto obj) + { + auto &api = m_api.value(); + const auto mtu = session.MaxPduSize(); + const auto mtu_args = static_cast(mtu); + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnMTUChanged(central_args, mtu_args, [] {}, [](auto error) {}); + }); + const auto &request = co_await event_args.GetRequestAsync(); + const auto id = std::addressof(request); + const auto id_args = reinterpret_cast(id); + const auto offset = request.Offset(); + const auto offset_args = static_cast(offset); + const auto value = request.Value(); + const auto value_length = value.Length(); + auto value_args = std::vector(value_length); + const auto value_reader = winrt::Windows::Storage::Streams::DataReader::FromBuffer(value); + value_reader.ReadBytes(value_args); + const auto option = request.Option(); + const auto type_args = WriteOptionToArgs(option); + m_deferrals[id_args] = deferral; + m_write_requests[id_args] = request; + const auto request_args = MyGATTWriteRequestArgs(id_args, offset_args, value_args, type_args); + api.OnCharacteristicWriteRequest(central_args, hash_code_args, request_args, []() {}, [](const auto &error) {}); + } + + winrt::fire_and_forget MyPeripheralManager::OnCharacteristicSubscribedClientsChangedAsync(const int64_t hash_code_args, const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalCharacteristic &characteristic) + { + auto &api = m_api.value(); + auto centrals_args = flutter::EncodableList(); + const auto clients = characteristic.SubscribedClients(); + for (const auto client : clients) + { + const auto session = client.Session(); + const auto device_id = session.DeviceId().Id(); + const auto &device = co_await winrt::Windows::Devices::Bluetooth::BluetoothLEDevice::FromIdAsync(device_id); + const auto address = device.BluetoothAddress(); + const auto address_args = static_cast(address); + const auto central_args = MyCentralArgs(address_args); + m_session_max_pdu_size_changed_revokers[address_args] = session.MaxPduSizeChanged( + winrt::auto_revoke, + [this, central_args](winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattSession session, auto obj) + { + auto &api = m_api.value(); + const auto mtu = session.MaxPduSize(); + const auto mtu_args = static_cast(mtu); + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnMTUChanged(central_args, mtu_args, [] {}, [](auto error) {}); + }); + m_clients[address_args] = client; + const auto central_args_value = flutter::CustomEncodableValue(central_args); + centrals_args.emplace_back(central_args_value); + } + api.OnCharacteristicSubscribedClientsChanged(hash_code_args, centrals_args, []() {}, [](const auto &error) {}); + } + + winrt::fire_and_forget MyPeripheralManager::OnDescriptorReadRequestedAsync(const int64_t hash_code_args, const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattReadRequestedEventArgs event_args) + { + auto &api = m_api.value(); + const auto deferral = event_args.GetDeferral(); + const auto session = event_args.Session(); + const auto device_id = session.DeviceId().Id(); + const auto &device = co_await winrt::Windows::Devices::Bluetooth::BluetoothLEDevice::FromIdAsync(device_id); + const auto address = device.BluetoothAddress(); + const auto address_args = static_cast(address); + const auto central_args = MyCentralArgs(address_args); + m_session_max_pdu_size_changed_revokers[address_args] = session.MaxPduSizeChanged( + winrt::auto_revoke, + [this, central_args](winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattSession session, auto obj) + { + auto &api = m_api.value(); + const auto mtu = session.MaxPduSize(); + const auto mtu_args = static_cast(mtu); + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnMTUChanged(central_args, mtu_args, [] {}, [](auto error) {}); + }); + const auto &request = co_await event_args.GetRequestAsync(); + const auto id = std::addressof(request); + const auto id_args = reinterpret_cast(id); + const auto offset = request.Offset(); + const auto offset_args = static_cast(offset); + const auto length = request.Length(); + const auto length_args = static_cast(length); + m_deferrals[id_args] = deferral; + m_read_requests[id_args] = request; + const auto request_args = MyGATTReadRequestArgs(id_args, offset_args, length_args); + api.OnDescriptorReadRequest(central_args, hash_code_args, request_args, []() {}, [](const auto &error) {}); + } + + winrt::fire_and_forget MyPeripheralManager::OnDescriptorWriteRequestedAsync(const int64_t hash_code_args, const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteRequestedEventArgs event_args) + { + auto &api = m_api.value(); + const auto deferral = event_args.GetDeferral(); + const auto session = event_args.Session(); + const auto device_id = session.DeviceId().Id(); + const auto &device = co_await winrt::Windows::Devices::Bluetooth::BluetoothLEDevice::FromIdAsync(device_id); + const auto address = device.BluetoothAddress(); + const auto address_args = static_cast(address); + const auto central_args = MyCentralArgs(address_args); + m_session_max_pdu_size_changed_revokers[address_args] = session.MaxPduSizeChanged( + winrt::auto_revoke, + [this, central_args](winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattSession session, auto obj) + { + auto &api = m_api.value(); + const auto mtu = session.MaxPduSize(); + const auto mtu_args = static_cast(mtu); + // TODO: Make this thread safe when this issue closed: https://github.com/flutter/flutter/issues/134346. + api.OnMTUChanged(central_args, mtu_args, [] {}, [](auto error) {}); + }); + const auto &request = co_await event_args.GetRequestAsync(); + const auto id = std::addressof(request); + const auto id_args = reinterpret_cast(id); + const auto offset = request.Offset(); + const auto offset_args = static_cast(offset); + const auto value = request.Value(); + const auto value_length = value.Length(); + auto value_args = std::vector(value_length); + const auto value_reader = winrt::Windows::Storage::Streams::DataReader::FromBuffer(value); + value_reader.ReadBytes(value_args); + const auto option = request.Option(); + const auto type_args = WriteOptionToArgs(option); + m_deferrals[id_args] = deferral; + m_write_requests[id_args] = request; + const auto request_args = MyGATTWriteRequestArgs(id_args, offset_args, value_args, type_args); + api.OnDescriptorWriteRequest(central_args, hash_code_args, request_args, []() {}, [](const auto &error) {}); + } + + MyBluetoothLowEnergyStateArgs MyPeripheralManager::RadioStateToArgs(const 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; + } + } + + winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEManufacturerData MyPeripheralManager::ArgsToManufacturerData(const MyManufacturerSpecificDataArgs &manufacturer_specific_data_args) + { + const auto manufacturer_data = winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEManufacturerData(); + const auto id_args = manufacturer_specific_data_args.id_args(); + const auto id = static_cast(id_args); + manufacturer_data.CompanyId(id); + const auto &data_args = manufacturer_specific_data_args.data_args(); + const auto data_writer = winrt::Windows::Storage::Streams::DataWriter(); + data_writer.WriteBytes(data_args); + const auto data = data_writer.DetachBuffer(); + manufacturer_data.Data(data); + return manufacturer_data; + } + + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties MyPeripheralManager::ArgsToCharacteristicProperties(const flutter::EncodableList property_numbers_args) + { + auto properties = winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::None; + for (const auto &property_number_args_value : property_numbers_args) + { + const auto property_number_args = std::get(property_number_args_value); + const auto property_args = static_cast(property_number_args); + switch (property_args) + { + case MyGATTCharacteristicPropertyArgs::read: + properties |= winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Read; + break; + case MyGATTCharacteristicPropertyArgs::write: + properties |= winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Write; + break; + case MyGATTCharacteristicPropertyArgs::writeWithoutResponse: + properties |= winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::WriteWithoutResponse; + break; + case MyGATTCharacteristicPropertyArgs::notify: + properties |= winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Notify; + break; + case MyGATTCharacteristicPropertyArgs::indicate: + properties |= winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties::Indicate; + break; + default: + break; + } + } + return properties; + } + + MyGATTCharacteristicWriteTypeArgs MyPeripheralManager::WriteOptionToArgs(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption &option) + { + switch (option) + { + case winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption::WriteWithResponse: + return MyGATTCharacteristicWriteTypeArgs::withResponse; + case winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption::WriteWithoutResponse: + return MyGATTCharacteristicWriteTypeArgs::withoutResponse; + default: + throw std::bad_cast(); + } + } + + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtectionLevel MyPeripheralManager::ArgsToProtectionLevel(const MyGATTProtectionLevelArgs &level_args) + { + switch (level_args) + { + case MyGATTProtectionLevelArgs::plain: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtectionLevel::Plain; + case MyGATTProtectionLevelArgs::authenticationRequired: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtectionLevel::AuthenticationRequired; + case MyGATTProtectionLevelArgs::entryptionRequired: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtectionLevel::EncryptionRequired; + case MyGATTProtectionLevelArgs::encryptionAndAuthenticationRequired: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtectionLevel::EncryptionAndAuthenticationRequired; + default: + throw std::bad_cast(); + } + } + + uint8_t MyPeripheralManager::ArgsToProtocolError(const MyGATTProtocolErrorArgs &error_args) + { + switch (error_args) + { + case MyGATTProtocolErrorArgs::invalidHandle: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::InvalidHandle(); + case MyGATTProtocolErrorArgs::readNotPermitted: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::ReadNotPermitted(); + case MyGATTProtocolErrorArgs::writeNotPermitted: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::WriteNotPermitted(); + case MyGATTProtocolErrorArgs::invalidPDU: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::InvalidPdu(); + case MyGATTProtocolErrorArgs::insufficientAuthentication: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::InsufficientAuthentication(); + case MyGATTProtocolErrorArgs::requestNotSupported: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::RequestNotSupported(); + case MyGATTProtocolErrorArgs::invalidOffset: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::InvalidOffset(); + case MyGATTProtocolErrorArgs::insufficientAuthorization: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::InsufficientAuthorization(); + case MyGATTProtocolErrorArgs::prepareQueueFull: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::PrepareQueueFull(); + case MyGATTProtocolErrorArgs::attributeNotFound: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::AttributeNotFound(); + case MyGATTProtocolErrorArgs::attributeNotLong: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::AttributeNotLong(); + case MyGATTProtocolErrorArgs::insufficientEncryptionKeySize: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::InsufficientEncryptionKeySize(); + case MyGATTProtocolErrorArgs::invalidAttributeValueLength: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::InvalidAttributeValueLength(); + case MyGATTProtocolErrorArgs::unlikelyError: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::UnlikelyError(); + case MyGATTProtocolErrorArgs::insufficientEncryption: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::InsufficientEncryption(); + case MyGATTProtocolErrorArgs::unsupportedGroupType: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::UnsupportedGroupType(); + case MyGATTProtocolErrorArgs::insufficientResources: + return winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtocolError::InsufficientResources(); + default: + throw std::bad_cast(); + } + } + +} // namespace bluetooth_low_energy_windows diff --git a/bluetooth_low_energy_windows/windows/my_peripheral_manager.h b/bluetooth_low_energy_windows/windows/my_peripheral_manager.h new file mode 100644 index 0000000..ccc16cb --- /dev/null +++ b/bluetooth_low_energy_windows/windows/my_peripheral_manager.h @@ -0,0 +1,90 @@ +#ifndef BLEW_MY_PERIPHERAL_MANAGER_H_ +#define BLEW_MY_PERIPHERAL_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 "winrt/Windows.Foundation.h" +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/Windows.Storage.Streams.h" + +#include "my_api.g.h" +#include "my_exception.h" + +namespace bluetooth_low_energy_windows +{ + class MyPeripheralManager : public MyPeripheralManagerHostAPI + { + public: + MyPeripheralManager(flutter::BinaryMessenger *messenger); + virtual ~MyPeripheralManager(); + + // Disallow copy and assign. + MyPeripheralManager(const MyPeripheralManager &) = delete; + MyPeripheralManager &operator=(const MyPeripheralManager &) = delete; + + void Initialize(std::function reply)> result) override; + ErrorOr GetState() override; + void AddService(const MyMutableGATTServiceArgs &service_args, std::function reply)> result) override; + std::optional RemoveService(int64_t hash_code_args) override; + std::optional StartAdvertising(const MyAdvertisementArgs &advertisement_args) override; + std::optional StopAdvertising() override; + ErrorOr GetMaxNotificationSize(int64_t address_args) override; + std::optional RespondReadRequestWithValue(int64_t id_args, const std::vector &value_args) override; + std::optional RespondReadRequestWithProtocolError(int64_t id_args, const MyGATTProtocolErrorArgs &error_args) override; + std::optional RespondWriteRequest(int64_t id_args) override; + std::optional RespondWriteRequestWithProtocolError(int64_t id_args, const MyGATTProtocolErrorArgs &error_args) override; + void NotifyValue(int64_t address_args, int64_t hash_code_args, const std::vector &value_args, std::function reply)> result) override; + + private: + std::optional m_api; + std::optional m_publisher; + std::optional m_adapter; + std::optional m_radio; + std::map> m_clients; + std::map> m_services_args; + std::map> m_service_providers; + std::map> m_characteristics; + std::map> m_deferrals; + std::map> m_read_requests; + std::map> m_write_requests; + + std::optional m_radio_state_changed_revoker; + std::map> m_session_max_pdu_size_changed_revokers; + std::map> m_characteristic_read_requested_revokers; + std::map> m_characteristic_write_requested_revokers; + std::map> m_characteristic_subscribed_clients_changed_revokers; + std::map> m_descriptor_read_requested_revokers; + std::map> m_descriptor_write_requested_revokers; + + winrt::fire_and_forget InitializeAsync(std::function reply)> result); + winrt::fire_and_forget AddServiceAsync(const MyMutableGATTServiceArgs &service_args, std::function reply)> result); + winrt::fire_and_forget NotifyValueAsync(int64_t address_args, int64_t hash_code_args, const std::vector &value_args, std::function reply)> result); + + winrt::Windows::Foundation::IAsyncAction CreateServiceAsync(const MyMutableGATTServiceArgs service_args); + winrt::Windows::Foundation::IAsyncAction CreateCharacteristicAsync(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalService &service, const MyMutableGATTCharacteristicArgs characteristic_args); + winrt::Windows::Foundation::IAsyncAction CreateDescriptorAsync(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalCharacteristic &characteristic, const MyMutableGATTDescriptorArgs descriptor_args); + + void RemoveServiceArgs(const MyMutableGATTServiceArgs &service_args); + void RemoveCharacteristicArgs(const MyMutableGATTCharacteristicArgs &characteristic_args); + void RemoveDescriptorArgs(const MyMutableGATTDescriptorArgs &descriptor_args); + + winrt::fire_and_forget OnCharacteristicReadRequestedAsync(const int64_t hash_code_args, const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattReadRequestedEventArgs event_args); + winrt::fire_and_forget OnCharacteristicWriteRequestedAsync(const int64_t hash_code_args, const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteRequestedEventArgs event_args); + winrt::fire_and_forget OnCharacteristicSubscribedClientsChangedAsync(const int64_t hash_code_args, const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattLocalCharacteristic &characteristic); + winrt::fire_and_forget OnDescriptorReadRequestedAsync(const int64_t hash_code_args, const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattReadRequestedEventArgs event_args); + winrt::fire_and_forget OnDescriptorWriteRequestedAsync(const int64_t hash_code_args, const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteRequestedEventArgs event_args); + + MyBluetoothLowEnergyStateArgs RadioStateToArgs(const winrt::Windows::Devices::Radios::RadioState &state); + MyGATTCharacteristicWriteTypeArgs WriteOptionToArgs(const winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattWriteOption &option); + + winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEManufacturerData ArgsToManufacturerData(const MyManufacturerSpecificDataArgs &manufacturer_specific_data_args); + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattCharacteristicProperties ArgsToCharacteristicProperties(const flutter::EncodableList property_numbers_args); + winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::GattProtectionLevel ArgsToProtectionLevel(const MyGATTProtectionLevelArgs &level_args); + uint8_t ArgsToProtocolError(const MyGATTProtocolErrorArgs &error_args); + }; + +} // namespace bluetooth_low_energy_windows + +#endif // BLEW_MY_PERIPHERAL_MANAGER_H_ diff --git a/bluetooth_low_energy_windows/windows/test/bluetooth_low_energy_windows_plugin_test.cpp b/bluetooth_low_energy_windows/windows/test/bluetooth_low_energy_windows_plugin_test.cpp new file mode 100644 index 0000000..c6567fb --- /dev/null +++ b/bluetooth_low_energy_windows/windows/test/bluetooth_low_energy_windows_plugin_test.cpp @@ -0,0 +1,8 @@ +#include "bluetooth_low_energy_windows_plugin.h" + +namespace bluetooth_low_energy_windows +{ + namespace test + { + } // namespace test +} // namespace bluetooth_low_energy_windows 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 deleted file mode 100644 index 439b6e3..0000000 --- a/bluetooth_low_energy_windows/windows/test/bluetooth_low_energy_windows_test.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#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 diff --git a/scripts/run_pigeon.sh b/scripts/run_pigeon.sh deleted file mode 100644 index a374e85..0000000 --- a/scripts/run_pigeon.sh +++ /dev/null @@ -1 +0,0 @@ -dart run pigeon --input my_api.dart