* 调整接口

* 临时提交

* 重构 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:
渐渐被你吸引
2024-06-04 00:44:39 +08:00
committed by GitHub
parent 71de531ceb
commit 108b6a804f
380 changed files with 23782 additions and 14127 deletions

View File

@ -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();
}
}

View File

@ -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

View File

@ -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;
}
}

View 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,
});
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}