6.0.0 (#74)
* 调整接口 * 临时提交 * 重构 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 <kevin@dekeyser.ch> * 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 <dekeyser.kevin97@gmail.com> Co-authored-by: Kevin De Keyser <kevin@dekeyser.ch>
This commit is contained in:
1
bluetooth_low_energy_darwin/.gitignore
vendored
1
bluetooth_low_energy_darwin/.gitignore
vendored
@ -26,5 +26,4 @@ migrate_working_dir/
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.packages
|
||||
build/
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
38
bluetooth_low_energy_darwin/darwin/.gitignore
vendored
Normal file
38
bluetooth_low_energy_darwin/darwin/.gitignore
vendored
Normal file
@ -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
|
0
bluetooth_low_energy_darwin/darwin/Assets/.gitkeep
Normal file
0
bluetooth_low_energy_darwin/darwin/Assets/.gitkeep
Normal file
@ -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)
|
||||
}
|
||||
}
|
1352
bluetooth_low_energy_darwin/darwin/Classes/MyAPI.g.swift
Normal file
1352
bluetooth_low_energy_darwin/darwin/Classes/MyAPI.g.swift
Normal file
File diff suppressed because it is too large
Load Diff
@ -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<NSNumber>.size)
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt16 {
|
||||
var data: Data {
|
||||
var source = self
|
||||
return Data(bytes: &source, count: MemoryLayout<UInt16>.size)
|
||||
var bytes = self
|
||||
return Data(bytes: &bytes, count: MemoryLayout<UInt16>.size)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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, Error>) -> Void]
|
||||
private var _disconnectCompletions: [String: (Result<Void, Error>) -> Void]
|
||||
private var _readRssiCompletions: [String: (Result<Int64, Error>) -> 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<FlutterStandardTypedData, Error>) -> Void]]
|
||||
private var _writeCharacteristicCompletions: [String: [Int64: (Result<Void, Error>) -> Void]]
|
||||
private var _setCharacteristicNotifyStateCompletions: [String: [Int64: (Result<Void, Error>) -> Void]]
|
||||
private var _readDescriptorCompletions: [String: [Int64: (Result<FlutterStandardTypedData, Error>) -> Void]]
|
||||
private var _writeDescriptorCompletions: [String: [Int64: (Result<Void, Error>) -> Void]]
|
||||
private var mConnectCompletions: [String: (Result<Void, Error>) -> Void]
|
||||
private var mDisconnectCompletions: [String: (Result<Void, Error>) -> Void]
|
||||
private var mReadRSSICompletions: [String: (Result<Int64, Error>) -> 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<FlutterStandardTypedData, Error>) -> Void]]
|
||||
private var mWriteCharacteristicCompletions: [String: [Int64: (Result<Void, Error>) -> Void]]
|
||||
private var mSetCharacteristicNotifyStateCompletions: [String: [Int64: (Result<Void, Error>) -> Void]]
|
||||
private var mReadDescriptorCompletions: [String: [Int64: (Result<FlutterStandardTypedData, Error>) -> Void]]
|
||||
private var mWriteDescriptorCompletions: [String: [Int64: (Result<Void, Error>) -> 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, any Error>) -> 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, Error>) -> 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, Error>) -> 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<Int64, Error>) -> 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<FlutterStandardTypedData, 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.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, Error>) -> Void) {
|
||||
func writeCharacteristic(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeArgs: MyGATTCharacteristicWriteTypeArgs, completion: @escaping (Result<Void, 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)
|
||||
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, 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)
|
||||
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<FlutterStandardTypedData, Error>) -> 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, Error>) -> 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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,8 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
// TODO: 优化错误内容
|
||||
enum MyError: Error {
|
||||
case illegalArgument
|
||||
case unknown
|
||||
case unsupported
|
||||
case illegalArgument
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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, Error>) -> Void)?
|
||||
private var _startAdvertisingCompletion: ((Result<Void, Error>) -> Void)?
|
||||
private var _isReadyToUpdateSubscribersCallbacks: [() -> Void]
|
||||
private var mAddServiceCompletion: ((Result<Void, Error>) -> Void)?
|
||||
private var mStartAdvertisingCompletion: ((Result<Void, Error>) -> 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, Error>) -> 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, Error>) -> 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, Error>) -> Void) {
|
||||
func showAppSettings(completion: @escaping (Result<Void, any Error>) -> 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, Error>) -> 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, Error>) -> 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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -27,7 +27,6 @@ migrate_working_dir/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
// });
|
||||
}
|
||||
|
@ -21,6 +21,6 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>11.0</string>
|
||||
<string>12.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>";
|
||||
};
|
||||
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -104,17 +95,13 @@
|
||||
path = RunnerTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
@ -135,8 +122,8 @@
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||
3C9A3D0714D61C07F02BFBB4 /* Pods */,
|
||||
0FAACD3CD9E188E482384358 /* Frameworks */,
|
||||
E27789BD215E61248833A6B2 /* Pods */,
|
||||
711971ED4BF1C4587FC0E521 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@ -164,6 +151,19 @@
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
};
|
||||
/* 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;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
LastUpgradeVersion = "1510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
@ -1,5 +1,5 @@
|
||||
import UIKit
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
@UIApplicationMain
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
|
@ -7,7 +7,7 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Bluetooth Low Energy</string>
|
||||
<string>Bluetooth Low Energy Darwin</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
@ -15,7 +15,7 @@
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>bluetooth_low_energy_example</string>
|
||||
<string>bluetooth_low_energy_darwin_example</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
@ -27,7 +27,7 @@
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>Hello Bluetooth LowEnergy!</string>
|
||||
<string>Need to use the bluetooth to communicate with peripherals</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
|
@ -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: [])
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
1
bluetooth_low_energy_darwin/example/lib/models.dart
Normal file
1
bluetooth_low_energy_darwin/example/lib/models.dart
Normal file
@ -0,0 +1 @@
|
||||
export 'models/log.dart';
|
10
bluetooth_low_energy_darwin/example/lib/models/log.dart
Normal file
10
bluetooth_low_energy_darwin/example/lib/models/log.dart
Normal file
@ -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();
|
||||
}
|
85
bluetooth_low_energy_darwin/example/lib/router_config.dart
Normal file
85
bluetooth_low_energy_darwin/example/lib/router_config.dart
Normal file
@ -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<CentralManagerViewModel>(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(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
6
bluetooth_low_energy_darwin/example/lib/view_models.dart
Normal file
6
bluetooth_low_energy_darwin/example/lib/view_models.dart
Normal file
@ -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';
|
@ -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<DiscoveredEventArgs> _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<DiscoveredEventArgs> get discoveries => _discoveries;
|
||||
|
||||
Future<void> showAppSettings() async {
|
||||
await _manager.showAppSettings();
|
||||
}
|
||||
|
||||
Future<void> startDiscovery({
|
||||
List<UUID>? serviceUUIDs,
|
||||
}) async {
|
||||
if (_discovering) {
|
||||
return;
|
||||
}
|
||||
_discoveries.clear();
|
||||
await _manager.startDiscovery(
|
||||
serviceUUIDs: serviceUUIDs,
|
||||
);
|
||||
_discovering = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> stopDiscovery() async {
|
||||
if (!_discovering) {
|
||||
return;
|
||||
}
|
||||
await _manager.stopDiscovery();
|
||||
_discovering = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_stateChangedSubscription.cancel();
|
||||
_discoveredSubscription.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
@ -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<DescriptorViewModel> _descriptorViewModels;
|
||||
final List<Log> _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<DescriptorViewModel> get descriptorViewModels => _descriptorViewModels;
|
||||
List<Log> get logs => _logs;
|
||||
GATTCharacteristicWriteType get writeType => _writeType;
|
||||
bool get notifyState => _notifyState;
|
||||
|
||||
Future<void> 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<void> 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<void> 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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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<Log> _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<Log> get logs => _logs;
|
||||
|
||||
Future<void> showAppSettings() async {
|
||||
await _manager.showAppSettings();
|
||||
}
|
||||
|
||||
Future<void> 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<void> 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();
|
||||
}
|
||||
}
|
@ -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<ServiceViewModel> _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<ServiceViewModel> get serviceViewModels => _serviceViewModels;
|
||||
|
||||
Future<void> connect() async {
|
||||
await _manager.connect(_peripheral);
|
||||
}
|
||||
|
||||
Future<void> disconnect() async {
|
||||
await _manager.disconnect(_peripheral);
|
||||
}
|
||||
|
||||
Future<void> 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();
|
||||
}
|
||||
}
|
@ -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<ServiceViewModel> _includedServiceViewModels;
|
||||
final List<CharacteristicViewModel> _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<ServiceViewModel> get includedServiceViewModels =>
|
||||
_includedServiceViewModels;
|
||||
List<CharacteristicViewModel> get characteristicViewModels =>
|
||||
_characteristicViewModels;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (var characteristicViewModel in characteristicViewModels) {
|
||||
characteristicViewModel.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
4
bluetooth_low_energy_darwin/example/lib/views.dart
Normal file
4
bluetooth_low_energy_darwin/example/lib/views.dart
Normal file
@ -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';
|
@ -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(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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<CentralManagerViewModel>(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<CentralManagerViewModel>(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<CentralManagerViewModel>(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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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<CharacteristicViewModel>(context);
|
||||
return Text(
|
||||
'${viewModel.uuid}',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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<CharacteristicView> createState() => _CharacteristicViewState();
|
||||
}
|
||||
|
||||
class _CharacteristicViewState extends State<CharacteristicView> {
|
||||
late final TextEditingController _textController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_textController = TextEditingController();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final viewModel = ViewModel.of<CharacteristicViewModel>(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();
|
||||
}
|
||||
}
|
@ -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<DescriptorViewModel>(context);
|
||||
return Text(
|
||||
'${viewModel.uuid}',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.tertiary,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
96
bluetooth_low_energy_darwin/example/lib/views/home_view.dart
Normal file
96
bluetooth_low_energy_darwin/example/lib/views/home_view.dart
Normal file
@ -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<Widget> navigators;
|
||||
|
||||
const HomeView({
|
||||
super.key,
|
||||
required this.navigationShell,
|
||||
required this.navigators,
|
||||
});
|
||||
|
||||
@override
|
||||
State<HomeView> createState() => _HomeViewState();
|
||||
}
|
||||
|
||||
class _HomeViewState extends State<HomeView> {
|
||||
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();
|
||||
}
|
||||
}
|
43
bluetooth_low_energy_darwin/example/lib/views/log_view.dart
Normal file
43
bluetooth_low_energy_darwin/example/lib/views/log_view.dart
Normal file
@ -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,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -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<PeripheralManagerViewModel>(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<PeripheralManagerViewModel>(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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -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<PeripheralViewModel>(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<TreeNode> _buildServiceTreeNodes(List<ServiceViewModel> 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<TreeNode> _buildCharacteristicTreeNodes(
|
||||
List<CharacteristicViewModel> 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<TreeNode> _buildDescriptorTreeNodes(
|
||||
List<DescriptorViewModel> viewModels) {
|
||||
return viewModels.map((viewModel) {
|
||||
return TreeNode(
|
||||
content: InheritedViewModel(
|
||||
view: const DescriptorTreeNodeView(),
|
||||
viewModel: viewModel,
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
}
|
@ -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<ServiceViewModel>(context);
|
||||
return Text(
|
||||
'${viewModel.uuid}',
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
1
bluetooth_low_energy_darwin/example/lib/widgets.dart
Normal file
1
bluetooth_low_energy_darwin/example/lib/widgets.dart
Normal file
@ -0,0 +1 @@
|
||||
export '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);
|
||||
}
|
||||
}
|
@ -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"))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
@ -81,13 +78,16 @@
|
||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
|
||||
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
/* 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 = "<group>";
|
||||
};
|
||||
331C80D6294CF71000263BE5 /* RunnerTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -137,14 +151,14 @@
|
||||
331C80D6294CF71000263BE5 /* RunnerTests */,
|
||||
33CC10EE2044A3C60003C045 /* Products */,
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
||||
5413C8972DC878C53291A3D1 /* Pods */,
|
||||
12C065AA4D4AEFAE0CFF1E11 /* Pods */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
};
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7618EFBF5D56DEA9B5BC6858 /* Pods_Runner.framework */,
|
||||
12AE3247A53A0A6C66A10AB1 /* Pods_RunnerTests.framework */,
|
||||
481C7185A6A29B7356E7BC4F /* Pods_Runner.framework */,
|
||||
3ACAB56CBFCEA91F07EF49FE /* Pods_RunnerTests.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@ -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;
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
LastUpgradeVersion = "1510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -15,7 +15,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "bluetooth_low_energy_example.app"
|
||||
BuildableName = "bluetooth_low_energy_darwin_example.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
@ -31,7 +31,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "bluetooth_low_energy_example.app"
|
||||
BuildableName = "bluetooth_low_energy_darwin_example.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
@ -65,7 +65,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "bluetooth_low_energy_example.app"
|
||||
BuildableName = "bluetooth_low_energy_darwin_example.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
@ -82,7 +82,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "bluetooth_low_energy_example.app"
|
||||
BuildableName = "bluetooth_low_energy_darwin_example.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
|
@ -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.
|
||||
|
@ -22,8 +22,6 @@
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>Hello Bluetooth LowEnergy!</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>$(PRODUCT_COPYRIGHT)</string>
|
||||
<key>NSMainNibFile</key>
|
||||
|
@ -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: [])
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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() {}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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<ManufacturerSpecificData> 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<String>().map((args) => args.toUUID()).toList();
|
||||
final serviceData = serviceDataArgs.cast<String, Uint8List>().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<String>()
|
||||
.map((args) => UUID.fromString(args))
|
||||
.toList(),
|
||||
serviceData: serviceDataArgs.cast<String, Uint8List>().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<int>().map(
|
||||
(args) {
|
||||
final propertyArgs = MyGattCharacteristicPropertyArgs.values[args];
|
||||
extension MyGATTCharacteristicArgsX on MyGATTCharacteristicArgs {
|
||||
MyGATTCharacteristic toCharacteristic() {
|
||||
return MyGATTCharacteristic(
|
||||
hashCodeArgs: hashCodeArgs,
|
||||
uuid: UUID.fromString(uuidArgs),
|
||||
properties: propertyNumbersArgs.cast<int>().map((args) {
|
||||
final propertyArgs = MyGATTCharacteristicPropertyArgs.values[args];
|
||||
return propertyArgs.toProperty();
|
||||
},
|
||||
).toList();
|
||||
final descriptors = descriptorsArgs
|
||||
.cast<MyGattDescriptorArgs>()
|
||||
.map((args) => args.toDescriptor2(peripheral))
|
||||
.toList();
|
||||
return MyGattCharacteristic2(
|
||||
peripheral: peripheral,
|
||||
hashCode: hashCode,
|
||||
uuid: uuid,
|
||||
properties: properties,
|
||||
descriptors: descriptors,
|
||||
}).toList(),
|
||||
descriptors: descriptorsArgs
|
||||
.cast<MyGATTDescriptorArgs>()
|
||||
.map((args) => args.toDescriptor())
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattServiceArgsX on MyGattServiceArgs {
|
||||
MyGattService2 toService2(MyPeripheral peripheral) {
|
||||
final hashCode = hashCodeArgs;
|
||||
final uuid = uuidArgs.toUUID();
|
||||
final characteristics = characteristicsArgs
|
||||
.cast<MyGattCharacteristicArgs>()
|
||||
.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<MyGATTServiceArgs>()
|
||||
.map((args) => args.toService())
|
||||
.toList(),
|
||||
characteristics: characteristicsArgs
|
||||
.cast<MyGATTCharacteristicArgs>()
|
||||
.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<MyGattDescriptorArgs> 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<MutableGATTDescriptor>()
|
||||
.map((descriptor) => descriptor.toArgs())
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattServiceX on MyGattService {
|
||||
MyGattServiceArgs toArgs(List<MyGattCharacteristicArgs> 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<MutableGATTCharacteristic>()
|
||||
.map((characteristic) => characteristic.toArgs())
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension UuidX on UUID {
|
||||
String toArgs() {
|
||||
return toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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<BluetoothLowEnergyStateChangedEventArgs>
|
||||
_stateChangedController;
|
||||
final StreamController<DiscoveredEventArgs> _discoveredController;
|
||||
final StreamController<ConnectionStateChangedEventArgs>
|
||||
final StreamController<PeripheralConnectionStateChangedEventArgs>
|
||||
_connectionStateChangedController;
|
||||
final StreamController<GattCharacteristicNotifiedEventArgs>
|
||||
final StreamController<GATTCharacteristicNotifiedEventArgs>
|
||||
_characteristicNotifiedController;
|
||||
final Map<String, MyPeripheral> _peripherals;
|
||||
final Map<String, Map<int, MyGattCharacteristic2>> _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<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
|
||||
_stateChangedController.stream;
|
||||
@override
|
||||
Stream<DiscoveredEventArgs> get discovered => _discoveredController.stream;
|
||||
@override
|
||||
Stream<ConnectionStateChangedEventArgs> get connectionStateChanged =>
|
||||
_connectionStateChangedController.stream;
|
||||
Stream<PeripheralConnectionStateChangedEventArgs>
|
||||
get connectionStateChanged => _connectionStateChangedController.stream;
|
||||
@override
|
||||
Stream<GattCharacteristicNotifiedEventArgs> get characteristicNotified =>
|
||||
Stream<PeripheralMTUChangedEventArgs> get mtuChanged =>
|
||||
throw UnsupportedError('mtuChanged is not supported on Darwin.');
|
||||
@override
|
||||
Stream<GATTCharacteristicNotifiedEventArgs> get characteristicNotified =>
|
||||
_characteristicNotifiedController.stream;
|
||||
|
||||
@override
|
||||
Future<void> setUp() async {
|
||||
logger.info('setUp');
|
||||
await _api.setUp();
|
||||
MyCentralManagerFlutterApi.setup(this);
|
||||
void initialize() {
|
||||
MyCentralManagerFlutterAPI.setUp(this);
|
||||
_initialize();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BluetoothLowEnergyState> getState() {
|
||||
logger.info('getState');
|
||||
return Future.value(_state);
|
||||
Future<bool> authorize() {
|
||||
throw UnsupportedError('authorize is not supported on Darwin.');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startDiscovery() async {
|
||||
logger.info('startDiscovery');
|
||||
await _api.startDiscovery();
|
||||
Future<void> showAppSettings() async {
|
||||
if (Platform.isIOS) {
|
||||
logger.info('showAppSettings');
|
||||
await _api.showAppSettings();
|
||||
} else {
|
||||
throw UnsupportedError(
|
||||
'showAppSettings is not supported on ${Platform.operatingSystem}.');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startDiscovery({
|
||||
List<UUID>? 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<List<Peripheral>> retrieveConnectedPeripherals() async {
|
||||
logger.info('retrieveConnectedPeripherals');
|
||||
final peripheralsArgs = await _api.retrieveConnectedPeripherals();
|
||||
final peripherals = peripheralsArgs
|
||||
.cast<MyPeripheralArgs>()
|
||||
.map((args) => args.toPeripheral())
|
||||
.toList();
|
||||
return peripherals;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> 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<void> 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<int> requestMTU(
|
||||
Peripheral peripheral, {
|
||||
required int mtu,
|
||||
}) {
|
||||
throw UnsupportedError('requestMTU is not supported on Darwin.');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> 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<int> 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<List<GattService>> discoverGATT(Peripheral peripheral) async {
|
||||
if (peripheral is! MyPeripheral) {
|
||||
throw TypeError();
|
||||
}
|
||||
Future<List<GATTService>> discoverGATT(Peripheral peripheral) async {
|
||||
// 发现 GATT 服务
|
||||
final uuidArgs = peripheral.uuid.toArgs();
|
||||
logger.info('discoverServices: $uuidArgs');
|
||||
final servicesArgs = await _api
|
||||
.discoverServices(uuidArgs)
|
||||
.then((args) => args.cast<MyGattServiceArgs>());
|
||||
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<MyGattCharacteristicArgs>());
|
||||
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<MyGattDescriptorArgs>());
|
||||
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<Uint8List> 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<void> 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<void> 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<Uint8List> readDescriptor(GattDescriptor descriptor) async {
|
||||
if (descriptor is! MyGattDescriptor2) {
|
||||
Future<Uint8List> 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<void> 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<void> _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<void> _getState() async {
|
||||
try {
|
||||
logger.info('getState');
|
||||
final stateArgs = await _api.getState();
|
||||
onStateChanged(stateArgs);
|
||||
} catch (e) {
|
||||
logger.severe('getState failed.', e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<MyGATTServiceArgs>> _discoverServices(String uuidArgs) async {
|
||||
logger.info('discoverServices: $uuidArgs');
|
||||
final servicesArgs = await _api
|
||||
.discoverServices(uuidArgs)
|
||||
.then((args) => args.cast<MyGATTServiceArgs>());
|
||||
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<List<MyGATTServiceArgs>> _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<MyGATTServiceArgs>());
|
||||
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<List<MyGATTCharacteristicArgs>> _discoverCharacteristics(
|
||||
String uuidArgs,
|
||||
int hashCodeArgs,
|
||||
) async {
|
||||
logger.info('discoverCharacteristics: $uuidArgs.$hashCodeArgs');
|
||||
final characteristicsArgs = await _api
|
||||
.discoverCharacteristics(uuidArgs, hashCodeArgs)
|
||||
.then((args) => args.cast<MyGATTCharacteristicArgs>());
|
||||
for (var characteristicArgs in characteristicsArgs) {
|
||||
final hashCodeArgs = characteristicArgs.hashCodeArgs;
|
||||
final descriptorsArgs = await _discoverDescriptors(
|
||||
uuidArgs,
|
||||
hashCodeArgs,
|
||||
);
|
||||
characteristicArgs.descriptorsArgs = descriptorsArgs;
|
||||
}
|
||||
return characteristicsArgs;
|
||||
}
|
||||
|
||||
Future<List<MyGATTDescriptorArgs>> _discoverDescriptors(
|
||||
String uuidArgs,
|
||||
int hashCodeArgs,
|
||||
) async {
|
||||
logger.info('discoverDescriptors: $uuidArgs.$hashCodeArgs');
|
||||
final descriptorsArgs = await _api
|
||||
.discoverDescriptors(uuidArgs, hashCodeArgs)
|
||||
.then((args) => args.cast<MyGATTDescriptorArgs>());
|
||||
return descriptorsArgs;
|
||||
}
|
||||
}
|
||||
|
93
bluetooth_low_energy_darwin/lib/src/my_gatt.dart
Normal file
93
bluetooth_low_energy_darwin/lib/src/my_gatt.dart
Normal file
@ -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<MyGATTDescriptor> descriptors,
|
||||
}) : super(
|
||||
descriptors: descriptors,
|
||||
);
|
||||
|
||||
@override
|
||||
List<MyGATTDescriptor> get descriptors =>
|
||||
super.descriptors.cast<MyGATTDescriptor>();
|
||||
|
||||
@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<MyGATTService> includedServices,
|
||||
required List<MyGATTCharacteristic> characteristics,
|
||||
}) : super(
|
||||
includedServices: includedServices,
|
||||
characteristics: characteristics,
|
||||
);
|
||||
|
||||
@override
|
||||
List<MyGATTService> get includedServices =>
|
||||
super.includedServices.cast<MyGATTService>();
|
||||
|
||||
@override
|
||||
List<MyGATTCharacteristic> get characteristics =>
|
||||
super.characteristics.cast<MyGATTCharacteristic>();
|
||||
|
||||
@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,
|
||||
});
|
||||
}
|
@ -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<MyGattDescriptor2> descriptors,
|
||||
}) : super(descriptors: descriptors);
|
||||
|
||||
@override
|
||||
List<MyGattDescriptor2> get descriptors =>
|
||||
super.descriptors.cast<MyGattDescriptor2>();
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is MyGattCharacteristic2 &&
|
||||
other.peripheral == peripheral &&
|
||||
other.hashCode == hashCode;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<MyGattCharacteristic2> characteristics,
|
||||
}) : super(characteristics: characteristics);
|
||||
|
||||
@override
|
||||
List<MyGattCharacteristic2> get characteristics =>
|
||||
super.characteristics.cast<MyGattCharacteristic2>();
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is MyGattService2 &&
|
||||
other.peripheral == peripheral &&
|
||||
other.hashCode == hashCode;
|
||||
}
|
||||
}
|
@ -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<BluetoothLowEnergyStateChangedEventArgs>
|
||||
_stateChangedController;
|
||||
final StreamController<GattCharacteristicReadEventArgs>
|
||||
_characteristicReadController;
|
||||
final StreamController<GattCharacteristicWrittenEventArgs>
|
||||
_characteristicWrittenController;
|
||||
final StreamController<GattCharacteristicNotifyStateChangedEventArgs>
|
||||
final StreamController<GATTCharacteristicReadRequestedEventArgs>
|
||||
_characteristicReadRequestedController;
|
||||
final StreamController<GATTCharacteristicWriteRequestedEventArgs>
|
||||
_characteristicWriteRequestedController;
|
||||
final StreamController<GATTCharacteristicNotifyStateChangedEventArgs>
|
||||
_characteristicNotifyStateChangedController;
|
||||
final StreamController<EventArgs> _isReadyController;
|
||||
|
||||
final Map<int, Map<int, MyGattCharacteristic>> _characteristics;
|
||||
final Map<String, Map<int, bool>> _listeners;
|
||||
final Map<int, MutableGATTCharacteristic> _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<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
|
||||
_stateChangedController.stream;
|
||||
@override
|
||||
Stream<GattCharacteristicReadEventArgs> get characteristicRead =>
|
||||
_characteristicReadController.stream;
|
||||
Stream<CentralConnectionStateChangedEventArgs> get connectionStateChanged =>
|
||||
throw UnsupportedError(
|
||||
'connectionStateChanged is not supported on Darwin.');
|
||||
@override
|
||||
Stream<GattCharacteristicWrittenEventArgs> get characteristicWritten =>
|
||||
_characteristicWrittenController.stream;
|
||||
Stream<CentralMTUChangedEventArgs> get mtuChanged =>
|
||||
throw UnsupportedError('mtuChanged is not supported on Darwin.');
|
||||
@override
|
||||
Stream<GattCharacteristicNotifyStateChangedEventArgs>
|
||||
Stream<GATTCharacteristicReadRequestedEventArgs>
|
||||
get characteristicReadRequested =>
|
||||
_characteristicReadRequestedController.stream;
|
||||
@override
|
||||
Stream<GATTCharacteristicWriteRequestedEventArgs>
|
||||
get characteristicWriteRequested =>
|
||||
_characteristicWriteRequestedController.stream;
|
||||
@override
|
||||
Stream<GATTCharacteristicNotifyStateChangedEventArgs>
|
||||
get characteristicNotifyStateChanged =>
|
||||
_characteristicNotifyStateChangedController.stream;
|
||||
@override
|
||||
Stream<GATTDescriptorReadRequestedEventArgs> get descriptorReadRequested =>
|
||||
throw UnsupportedError(
|
||||
'descriptorReadRequested is not supported on Darwin.');
|
||||
@override
|
||||
Stream<GATTDescriptorWriteRequestedEventArgs> get descriptorWriteRequested =>
|
||||
throw UnsupportedError(
|
||||
'descriptorWriteRequested is not supported on Darwin.');
|
||||
Stream<EventArgs> get _isReady => _isReadyController.stream;
|
||||
|
||||
@override
|
||||
Future<void> setUp() async {
|
||||
logger.info('setUp');
|
||||
await _api.setUp();
|
||||
MyPeripheralManagerFlutterApi.setup(this);
|
||||
void initialize() {
|
||||
MyPeripheralManagerFlutterAPI.setUp(this);
|
||||
_initialize();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BluetoothLowEnergyState> getState() {
|
||||
logger.info('getState');
|
||||
return Future.value(_state);
|
||||
Future<bool> authorize() {
|
||||
throw UnsupportedError('authorize is not supported on Darwin.');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> addService(GattService service) async {
|
||||
if (service is! MyGattService) {
|
||||
throw TypeError();
|
||||
Future<void> showAppSettings() async {
|
||||
if (Platform.isIOS) {
|
||||
logger.info('showAppSettings');
|
||||
await _api.showAppSettings();
|
||||
} else {
|
||||
throw UnsupportedError(
|
||||
'showAppSettings is not supported on ${Platform.operatingSystem}.');
|
||||
}
|
||||
final characteristics = <int, MyGattCharacteristic>{};
|
||||
final characteristicsArgs = <MyGattCharacteristicArgs>[];
|
||||
for (var characteristic in service.characteristics) {
|
||||
final descriptorsArgs = <MyGattDescriptorArgs>[];
|
||||
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<void> addService(GATTService service) async {
|
||||
final serviceArgs = service.toArgs();
|
||||
logger.info('addService: $serviceArgs');
|
||||
await _api.addService(serviceArgs);
|
||||
_characteristics[serviceArgs.hashCodeArgs] = characteristics;
|
||||
_addService(service);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeService(GattService service) async {
|
||||
Future<void> removeService(GATTService service) async {
|
||||
final hashCodeArgs = service.hashCode;
|
||||
logger.info('removeService: $hashCodeArgs');
|
||||
await _api.removeService(hashCodeArgs);
|
||||
_characteristics.remove(hashCodeArgs);
|
||||
_removeService(service);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clearServices() async {
|
||||
logger.info('clearServices');
|
||||
await _api.clearServices();
|
||||
Future<void> removeAllServices() async {
|
||||
logger.info('removeAllServices');
|
||||
await _api.removeAllServices();
|
||||
_characteristics.clear();
|
||||
}
|
||||
|
||||
@ -112,62 +129,112 @@ class MyPeripheralManager extends PeripheralManager
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> 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<int> getMaximumNotifyLength(Central central) async {
|
||||
final uuidArgs = central.uuid.toArgs();
|
||||
logger.info('getMaximumNotifyLength: $uuidArgs');
|
||||
final maximumNotifyLength = await _api.getMaximumNotifyLength(uuidArgs);
|
||||
return maximumNotifyLength;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> writeCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
Future<void> 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<void> 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<void> 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<void> 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<void> 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<MyATTRequestArgs?> 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<MyATTRequestArgs>().first;
|
||||
final centralArgs = requestArgs.centralArgs;
|
||||
final hashCodeArgs = requestArgs.hashCodeArgs;
|
||||
final characteristicHashCodeArgs = requestArgs.characteristicHashCodeArgs;
|
||||
final offsetArgs = requestArgs.offsetArgs;
|
||||
final unsupported = requestsArgs.cast<MyATTRequestArgs>().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<MyATTRequestArgs>().fold(
|
||||
<int>[],
|
||||
(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<void> _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<void> _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<void> _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<void> _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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<String?> serviceUUIDsArgs;
|
||||
final Map<String?, Uint8List?> 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<int?> propertyNumbersArgs;
|
||||
final List<MyGattDescriptorArgs?> descriptorsArgs;
|
||||
final List<MyGATTDescriptorArgs?> 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<MyGattCharacteristicArgs?> characteristicsArgs;
|
||||
final bool isPrimaryArgs;
|
||||
final List<MyGATTServiceArgs?> includedServicesArgs;
|
||||
final List<MyGATTCharacteristicArgs?> 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<int?> propertyNumbersArgs;
|
||||
final List<int?> permissionNumbersArgs;
|
||||
final Uint8List? valueArgs;
|
||||
final List<MyMutableGATTDescriptorArgs?> 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<MyMutableGATTServiceArgs?> includedServicesArgs;
|
||||
final List<MyMutableGATTCharacteristicArgs?> 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<String> serviceUUIDsArgs);
|
||||
void stopDiscovery();
|
||||
List<MyPeripheralArgs> 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<MyGattServiceArgs> discoverServices(String uuidArgs);
|
||||
List<MyGATTServiceArgs> discoverServices(String uuidArgs);
|
||||
@async
|
||||
List<MyGattCharacteristicArgs> discoverCharacteristics(
|
||||
List<MyGATTServiceArgs> discoverIncludedServices(
|
||||
String uuidArgs,
|
||||
int hashCodeArgs,
|
||||
);
|
||||
@async
|
||||
List<MyGattDescriptorArgs> discoverDescriptors(
|
||||
List<MyGATTCharacteristicArgs> discoverCharacteristics(
|
||||
String uuidArgs,
|
||||
int hashCodeArgs,
|
||||
);
|
||||
@async
|
||||
List<MyGATTDescriptorArgs> 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<String>? 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<MyATTRequestArgs> requestsArgs);
|
||||
void isReady();
|
||||
void onCharacteristicNotifyStateChanged(
|
||||
MyCentralArgs centralArgs,
|
||||
int hashCodeArgs,
|
||||
|
@ -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
|
||||
|
@ -0,0 +1 @@
|
||||
void main() {}
|
Reference in New Issue
Block a user