feat: 5.0.0 (#35)

* draft: 临时提交

* feat: 实现扫描功能

* fix: 优化广播逻辑

* feat: 添加协程方法

* fix: 修改宏定义

* draft: 临时提交

* feat: 调整接口

* fix: 修改版本号

* feat: 4.1.1

* draft: 临时提交

* feat: 5.0.0-dev.2

* fix: 修复版本号错误

* draft: 临时提交

* fix: 修复连接断开异常

* fix: 修复问题

* fix: 优化代码

* fix:  优化 short UUID 格式化逻辑

* fix: 尝试实现 read_rssi 接口,当前此接口不可用,会报异常

* feat: 删除 getMaximumWriteLength 方法

* fix: 更新 CHANGELOG.md

* feat: 5.0.0-dev.1

* fix: 更新依赖项

* feat: linux-5.0.0-dev.1

* fix: 更新 CHANGELOG.md

* fix: 开始搜索设备时清空设备列表

* fix: 开始扫描时清空设备列表

* feat: 5.0.0-dev.2

* fix: 优化 MyGattService 和 MyGattCharacteristic

* feat: 更新 interface 版本 -> 5.0.0-dev.4

* feat: 更新 interface 版本 -> 5.0.0-dev.4

* feat: 实现 flutter 部分 5.0.0

* fix: 移除 maximumWriteLength

* fix: 移除 rssi

* feat: 5.0.0-dev.1

* feat: 5.0.0-dev.2

* fix: 更新依赖项

* fix: 5.0.0-dev.4

* fix: 更新依赖项

* draft: 临时提交

* feat: 5.0.0-dev.5

* draft: 删除 MyCentralManager 和 MyPeripheralManager

* fix: 更新依赖项

* fix: 更新依赖项

* feat: 适配新接口

* feat: 5.0.0-dev.6

* draft: 临时提交

* feat: 5.0.0-dev.7

* fix: 修改版本号

* feat: 5.0.0-dev.8

* feat: 5.0.0-dev.9

* fix: 修复 trimGATT 错误

* feat: 5.0.0-dev.6

* feat: 5.0.0-dev.3

* feat: 5.0.0-dev.4

* fix: 更新 pubspec.lock

* feat: 5.0.0-dev.7

* feat: 5.0.0-dev.3

* fix: balabala

* fix: balabala

* draft: 5.0.0-dev.1

* fix: trim GATT when call the `writeCharacteristic` method.

* fix: make difference of `trim` and `fragment`.

* feat: 5.0.0-dev.1

* feat: 5.0.0-dev.1

* feat: 优化示例程序

* fix: 更新 README.md

* fix: 修复插件引用

* draft: XXXX

* feat: 增加调试信息

* fix: 更新 pubspec.lock

* feat: 5.0.0-dev.4

* feat: 5.0.0-dev.3

* feat: 5.0.0

* feat: 5.0.0

* feat: 5.0.0

* feat: 5.0.0

* feat: 5.0.0

* feat: 5.0.0
This commit is contained in:
iAMD
2023-12-31 00:53:48 +08:00
committed by GitHub
parent cfe0eda4a3
commit 87fe3e2447
137 changed files with 14108 additions and 8393 deletions

View File

@ -1,3 +1,55 @@
## 5.0.0
* Now `CentralManager#writeCharacteristic` and `PeripheralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes.
* Add implementation of `CentralManager` on windows platform.
* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`.
* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`.
* Add `PeripheralManager#readCharacteristic`.
* Remove `CentralManager#getMaximumWriteLength` method.
* Remove `PeripheralManager#getMaximumWriteLength` method.
* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`.
* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`.
* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`.
* Move `CentralManager#state` to `CentralManager#getState()`.
* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`.
* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`.
* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`.
* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`.
* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`.
* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#writeCharacteristic`.
* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`.
* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`.
## 5.0.0-dev.3
* Add logs on Linux platform.
## 5.0.0-dev.2
* Add default_package of windows in pubspec.yaml.
## 5.0.0-dev.1
* Now `CentralManager#writeCharacteristic` and `PeripheralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes.
* Add `UUID#fromAddress` constructor.
* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`.
* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`.
* Add `PeripheralManager#readCharacteristic`.
* Remove `CentralManager#getMaximumWriteLength` method.
* Remove `PeripheralManager#getMaximumWriteLength` method.
* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`.
* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`.
* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`.
* Move `CentralManager#state` to `CentralManager#getState()`.
* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`.
* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`.
* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`.
* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`.
* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`.
* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#writeCharacteristic`.
* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`.
* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`.
## 4.0.0 ## 4.0.0
* Remove `BluetoothLowEnergy` class. * Remove `BluetoothLowEnergy` class.

View File

@ -6,29 +6,24 @@ A Flutter plugin for controlling the bluetooth low energy.
### CentralManager ### CentralManager
- [x] Set up the central manager.
- [x] Get/Listen the state of the central manager. - [x] Get/Listen the state of the central manager.
- [x] Listen connection state cahgned.
- [x] Listen GATT characteristic notified.
- [x] Start/Stop discovery. - [x] Start/Stop discovery.
- [x] Connect/Disconnect peripherals. - [x] Connect/Disconnect peripherals.
- [x] Get maximum write length of peripherals.
- [x] Read RSSI of peripherals. - [x] Read RSSI of peripherals.
- [x] Discover GATT. - [x] Discover GATT.
- [x] Get GATT services. - [x] Read/Write GATT characteristics.
- [x] Get GATT characteristics. - [x] Set GATT characteristics notify state.
- [x] Get GATT descriptors.
- [x] Read/Write/Notify GATT characteristics.
- [x] Read/Write GATT descriptors. - [x] Read/Write GATT descriptors.
### PeripheralManager ### PeripheralManager
- [x] Set up the peripheral manager.
- [x] Get/Listen the state of the peripheral manager. - [x] Get/Listen the state of the peripheral manager.
- [x] Listen GATT characteristic read/written/notifyStateChanged.
- [x] Add/Remove/Clear service(s). - [x] Add/Remove/Clear service(s).
- [x] Start/Stop advertising. - [x] Start/Stop advertising.
- [x] Get maximum write length of centrals. - [x] Read/Write(Notify) GATT characteristics.
- [x] Listen read/write/notify characteristic requests from centrals.
- [x] Send read/write characteristic replies to centrals.
- [x] Notify characteristic value changed to centrals.
## Getting Started ## Getting Started
@ -55,10 +50,10 @@ According to Apple's [documents](https://developer.apple.com/documentation/coreb
### Linux ### Linux
Not tested enough, if you occured any problems, file an issue to let me know about it, i will fix it as soon as possible. PeripheralManager is not implemented because the `bluez` plugin doesn't support this yet, see [How to use bluez to act as bluetooth peripheral](https://github.com/canonical/bluez.dart/issues/85)
PeripheralManager api is not supported because the `bluez` plugin doesn't support this yet, see [How to use bluez to act as bluetooth peripheral](https://github.com/canonical/bluez.dart/issues/85)
### Windows ### Windows
Not implemented yet but maybe someday or someone can use the `win32` api to implement this plugin_interface or someday the flutter team support C# on windows platform or someday I am familiar with C++ language... PeripheralManager is not implemented, it will be implemented in the future.
*Note:* The `CentralManager#readRSSI` method is not implemented on windows(windows doesn't support read RSSI after connected), avoid call this when running on windows devices.

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; import 'package:bluetooth_low_energy/bluetooth_low_energy.dart';
@ -8,8 +9,7 @@ import 'package:convert/convert.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
CentralManager get centralManager => CentralManager.instance; bool get enablePeripheral => !Platform.isLinux && !Platform.isWindows;
PeripheralManager get peripheralManager => PeripheralManager.instance;
void main() { void main() {
runZonedGuarded(onStartUp, onCrashed); runZonedGuarded(onStartUp, onCrashed);
@ -17,9 +17,13 @@ void main() {
void onStartUp() async { void onStartUp() async {
Logger.root.onRecord.listen(onLogRecord); Logger.root.onRecord.listen(onLogRecord);
// hierarchicalLoggingEnabled = true;
// CentralManager.instance.logLevel = Level.WARNING;
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await centralManager.setUp(); await CentralManager.instance.setUp();
await peripheralManager.setUp(); if (enablePeripheral) {
await PeripheralManager.instance.setUp();
}
runApp(const MyApp()); runApp(const MyApp());
} }
@ -58,8 +62,10 @@ class _MyAppState extends State<MyApp> {
return MaterialApp( return MaterialApp(
theme: ThemeData.light( theme: ThemeData.light(
useMaterial3: true, useMaterial3: true,
).copyWith(
materialTapTargetSize: MaterialTapTargetSize.padded,
), ),
home: const HomeView(), home: enablePeripheral ? const HomeView() : const ScannerView(),
routes: { routes: {
'peripheral': (context) { 'peripheral': (context) {
final route = ModalRoute.of(context); final route = ModalRoute.of(context);
@ -168,15 +174,15 @@ class _ScannerViewState extends State<ScannerView> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
state = ValueNotifier(centralManager.state); state = ValueNotifier(BluetoothLowEnergyState.unknown);
discovering = ValueNotifier(false); discovering = ValueNotifier(false);
discoveredEventArgs = ValueNotifier([]); discoveredEventArgs = ValueNotifier([]);
stateChangedSubscription = centralManager.stateChanged.listen( stateChangedSubscription = CentralManager.instance.stateChanged.listen(
(eventArgs) { (eventArgs) {
state.value = eventArgs.state; state.value = eventArgs.state;
}, },
); );
discoveredSubscription = centralManager.discovered.listen( discoveredSubscription = CentralManager.instance.discovered.listen(
(eventArgs) { (eventArgs) {
final items = discoveredEventArgs.value; final items = discoveredEventArgs.value;
final i = items.indexWhere( final i = items.indexWhere(
@ -190,6 +196,11 @@ class _ScannerViewState extends State<ScannerView> {
} }
}, },
); );
_initialize();
}
void _initialize() async {
state.value = await CentralManager.instance.getState();
} }
@override @override
@ -233,12 +244,13 @@ class _ScannerViewState extends State<ScannerView> {
} }
Future<void> startDiscovery() async { Future<void> startDiscovery() async {
await centralManager.startDiscovery(); discoveredEventArgs.value = [];
await CentralManager.instance.startDiscovery();
discovering.value = true; discovering.value = true;
} }
Future<void> stopDiscovery() async { Future<void> stopDiscovery() async {
await centralManager.stopDiscovery(); await CentralManager.instance.stopDiscovery();
discovering.value = false; discovering.value = false;
} }
@ -340,7 +352,13 @@ class _ScannerViewState extends State<ScannerView> {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
trailing: RssiWidget(rssi), trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
RssiWidget(rssi),
Text('$rssi'),
],
),
); );
}, },
separatorBuilder: (context, i) { separatorBuilder: (context, i) {
@ -378,44 +396,39 @@ class PeripheralView extends StatefulWidget {
} }
class _PeripheralViewState extends State<PeripheralView> { class _PeripheralViewState extends State<PeripheralView> {
late final ValueNotifier<bool> state; late final ValueNotifier<bool> connectionState;
late final DiscoveredEventArgs eventArgs; late final DiscoveredEventArgs eventArgs;
late final ValueNotifier<List<GattService>> services; late final ValueNotifier<List<GattService>> services;
late final ValueNotifier<List<GattCharacteristic>> characteristics; late final ValueNotifier<List<GattCharacteristic>> characteristics;
late final ValueNotifier<GattService?> service; late final ValueNotifier<GattService?> service;
late final ValueNotifier<GattCharacteristic?> characteristic; late final ValueNotifier<GattCharacteristic?> characteristic;
late final ValueNotifier<GattCharacteristicWriteType> writeType; late final ValueNotifier<GattCharacteristicWriteType> writeType;
late final ValueNotifier<int> maximumWriteLength;
late final ValueNotifier<int> rssi;
late final ValueNotifier<List<Log>> logs; late final ValueNotifier<List<Log>> logs;
late final TextEditingController writeController; late final TextEditingController writeController;
late final StreamSubscription stateChangedSubscription; late final StreamSubscription connectionStateChangedSubscription;
late final StreamSubscription valueChangedSubscription; late final StreamSubscription characteristicNotifiedSubscription;
late final StreamSubscription rssiChangedSubscription;
late final Timer rssiTimer;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
eventArgs = widget.eventArgs; eventArgs = widget.eventArgs;
state = ValueNotifier(false); connectionState = ValueNotifier(false);
services = ValueNotifier([]); services = ValueNotifier([]);
characteristics = ValueNotifier([]); characteristics = ValueNotifier([]);
service = ValueNotifier(null); service = ValueNotifier(null);
characteristic = ValueNotifier(null); characteristic = ValueNotifier(null);
writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); writeType = ValueNotifier(GattCharacteristicWriteType.withResponse);
maximumWriteLength = ValueNotifier(0);
rssi = ValueNotifier(-100);
logs = ValueNotifier([]); logs = ValueNotifier([]);
writeController = TextEditingController(); writeController = TextEditingController();
stateChangedSubscription = centralManager.peripheralStateChanged.listen( connectionStateChangedSubscription =
CentralManager.instance.connectionStateChanged.listen(
(eventArgs) { (eventArgs) {
if (eventArgs.peripheral != this.eventArgs.peripheral) { if (eventArgs.peripheral != this.eventArgs.peripheral) {
return; return;
} }
final state = eventArgs.state; final connectionState = eventArgs.connectionState;
this.state.value = state; this.connectionState.value = connectionState;
if (!state) { if (!connectionState) {
services.value = []; services.value = [];
characteristics.value = []; characteristics.value = [];
service.value = null; service.value = null;
@ -424,12 +437,13 @@ class _PeripheralViewState extends State<PeripheralView> {
} }
}, },
); );
valueChangedSubscription = centralManager.characteristicValueChanged.listen( characteristicNotifiedSubscription =
CentralManager.instance.characteristicNotified.listen(
(eventArgs) { (eventArgs) {
final characteristic = this.characteristic.value; // final characteristic = this.characteristic.value;
if (eventArgs.characteristic != characteristic) { // if (eventArgs.characteristic != characteristic) {
return; // return;
} // }
const type = LogType.notify; const type = LogType.notify;
final log = Log(type, eventArgs.value); final log = Log(type, eventArgs.value);
logs.value = [ logs.value = [
@ -438,28 +452,16 @@ class _PeripheralViewState extends State<PeripheralView> {
]; ];
}, },
); );
rssiTimer = Timer.periodic(
const Duration(seconds: 5),
(timer) async {
final state = this.state.value;
if (state) {
rssi.value = await centralManager.readRSSI(eventArgs.peripheral);
} else {
rssi.value = -100;
}
},
);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return WillPopScope( return PopScope(
onWillPop: () async { onPopInvoked: (didPop) async {
if (state.value) { if (connectionState.value) {
final peripheral = eventArgs.peripheral; final peripheral = eventArgs.peripheral;
await centralManager.disconnect(peripheral); await CentralManager.instance.disconnect(peripheral);
} }
return true;
}, },
child: Scaffold( child: Scaffold(
appBar: buildAppBar(context), appBar: buildAppBar(context),
@ -474,25 +476,17 @@ class _PeripheralViewState extends State<PeripheralView> {
title: Text(title), title: Text(title),
actions: [ actions: [
ValueListenableBuilder( ValueListenableBuilder(
valueListenable: state, valueListenable: connectionState,
builder: (context, state, child) { builder: (context, state, child) {
return TextButton( return TextButton(
onPressed: () async { onPressed: () async {
final peripheral = eventArgs.peripheral; final peripheral = eventArgs.peripheral;
if (state) { if (state) {
await centralManager.disconnect(peripheral); await CentralManager.instance.disconnect(peripheral);
maximumWriteLength.value = 0;
rssi.value = 0;
} else { } else {
await centralManager.connect(peripheral); await CentralManager.instance.connect(peripheral);
services.value = services.value =
await centralManager.discoverGATT(peripheral); await CentralManager.instance.discoverGATT(peripheral);
maximumWriteLength.value =
await centralManager.getMaximumWriteLength(
peripheral,
type: writeType.value,
);
rssi.value = await centralManager.readRSSI(peripheral);
} }
}, },
child: Text(state ? 'DISCONNECT' : 'CONNECT'), child: Text(state ? 'DISCONNECT' : 'CONNECT'),
@ -566,7 +560,32 @@ class _PeripheralViewState extends State<PeripheralView> {
hint: const Text('CHOOSE A CHARACTERISTIC'), hint: const Text('CHOOSE A CHARACTERISTIC'),
value: characteristic, value: characteristic,
onChanged: (characteristic) { onChanged: (characteristic) {
if (characteristic == null) {
return;
}
this.characteristic.value = characteristic; this.characteristic.value = characteristic;
final writeType = this.writeType.value;
final canWrite = characteristic.properties.contains(
GattCharacteristicProperty.write,
);
final canWriteWithoutResponse =
characteristic.properties.contains(
GattCharacteristicProperty.writeWithoutResponse,
);
if (writeType ==
GattCharacteristicWriteType.withResponse &&
!canWrite &&
canWriteWithoutResponse) {
this.writeType.value =
GattCharacteristicWriteType.withoutResponse;
}
if (writeType ==
GattCharacteristicWriteType.withoutResponse &&
!canWriteWithoutResponse &&
canWrite) {
this.writeType.value =
GattCharacteristicWriteType.withResponse;
}
}, },
); );
}, },
@ -624,21 +643,101 @@ class _PeripheralViewState extends State<PeripheralView> {
}, },
), ),
), ),
ValueListenableBuilder(
valueListenable: characteristic,
builder: (context, characteristic, chld) {
final bool canNotify, canRead, canWrite, canWriteWithoutResponse;
if (characteristic == null) {
canNotify =
canRead = canWrite = canWriteWithoutResponse = false;
} else {
final properties = characteristic.properties;
canNotify = properties.contains(
GattCharacteristicProperty.notify,
) ||
properties.contains(
GattCharacteristicProperty.indicate,
);
canRead = properties.contains(
GattCharacteristicProperty.read,
);
canWrite = properties.contains(
GattCharacteristicProperty.write,
);
canWriteWithoutResponse = properties.contains(
GattCharacteristicProperty.writeWithoutResponse,
);
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
margin: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: characteristic != null && canNotify
? () async {
await CentralManager.instance
.setCharacteristicNotifyState(
characteristic,
state: true,
);
}
: null,
child: const Text('NOTIFY'),
),
const SizedBox(width: 8.0),
ElevatedButton(
onPressed: characteristic != null && canRead
? () async {
final value = await CentralManager.instance
.readCharacteristic(characteristic);
const type = LogType.read;
final log = Log(type, value);
logs.value = [...logs.value, log];
}
: null,
child: const Text('READ'),
)
],
),
),
SizedBox(
height: 160.0,
child: TextField(
controller: writeController,
enabled: canWrite || canWriteWithoutResponse,
expands: true,
maxLines: null,
textAlignVertical: TextAlignVertical.top,
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
),
),
Row( Row(
children: [ children: [
ValueListenableBuilder( ValueListenableBuilder(
valueListenable: writeType, valueListenable: writeType,
builder: (context, writeType, child) { builder: (context, writeType, child) {
return ToggleButtons( return ToggleButtons(
onPressed: (i) async { onPressed: canWrite || canWriteWithoutResponse
final type = GattCharacteristicWriteType.values[i]; ? (i) {
if (!canWrite || !canWriteWithoutResponse) {
return;
}
final type =
GattCharacteristicWriteType.values[i];
this.writeType.value = type; this.writeType.value = type;
maximumWriteLength.value = }
await centralManager.getMaximumWriteLength( : null,
eventArgs.peripheral,
type: type,
);
},
constraints: const BoxConstraints( constraints: const BoxConstraints(
minWidth: 0.0, minWidth: 0.0,
minHeight: 0.0, minHeight: 0.0,
@ -647,7 +746,8 @@ class _PeripheralViewState extends State<PeripheralView> {
isSelected: GattCharacteristicWriteType.values isSelected: GattCharacteristicWriteType.values
.map((type) => type == writeType) .map((type) => type == writeType)
.toList(), .toList(),
children: GattCharacteristicWriteType.values.map((type) { children: GattCharacteristicWriteType.values.map(
(type) {
return Container( return Container(
margin: const EdgeInsets.symmetric( margin: const EdgeInsets.symmetric(
horizontal: 8.0, horizontal: 8.0,
@ -655,7 +755,8 @@ class _PeripheralViewState extends State<PeripheralView> {
), ),
child: Text(type.name), child: Text(type.name),
); );
}).toList(), },
).toList(),
); );
// final segments = // final segments =
// GattCharacteristicWriteType.values.map((type) { // GattCharacteristicWriteType.values.map((type) {
@ -679,118 +780,46 @@ class _PeripheralViewState extends State<PeripheralView> {
// ); // );
}, },
), ),
const SizedBox(width: 8.0),
ValueListenableBuilder(
valueListenable: state,
builder: (context, state, child) {
return ValueListenableBuilder(
valueListenable: maximumWriteLength,
builder: (context, maximumWriteLength, child) {
return Text('$maximumWriteLength');
},
);
},
),
const Spacer(), const Spacer(),
ValueListenableBuilder( ElevatedButton(
valueListenable: rssi,
builder: (context, rssi, child) {
return RssiWidget(rssi);
},
),
],
),
Container(
margin: const EdgeInsets.only(bottom: 16.0),
height: 160.0,
child: ValueListenableBuilder(
valueListenable: characteristic,
builder: (context, characteristic, child) {
final bool canNotify, canRead, canWrite;
if (characteristic == null) {
canNotify = canRead = canWrite = false;
} else {
final properties = characteristic.properties;
canNotify = properties.contains(
GattCharacteristicProperty.notify,
);
canRead = properties.contains(
GattCharacteristicProperty.read,
);
canWrite = properties.contains(
GattCharacteristicProperty.write,
);
}
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: TextField(
controller: writeController,
enabled: canWrite,
expands: true,
maxLines: null,
textAlignVertical: TextAlignVertical.top,
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
),
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: characteristic != null && canNotify
? () async {
await centralManager.notifyCharacteristic(
characteristic,
state: true,
);
}
: null,
child: const Text('NOTIFY'),
),
TextButton(
onPressed: characteristic != null && canRead
? () async {
final value = await centralManager
.readCharacteristic(characteristic);
const type = LogType.read;
final log = Log(type, value);
logs.value = [...logs.value, log];
}
: null,
child: const Text('READ'),
),
TextButton(
onPressed: characteristic != null && canWrite onPressed: characteristic != null && canWrite
? () async { ? () async {
final text = writeController.text; final text = writeController.text;
final elements = utf8.encode(text); final elements = utf8.encode(text);
final value = Uint8List.fromList(elements); final value = Uint8List.fromList(elements);
final type = writeType.value; final type = writeType.value;
await centralManager.writeCharacteristic( // Fragments the value by 512 bytes.
const fragmentSize = 512;
var start = 0;
while (start < value.length) {
final end = start + fragmentSize;
final fragmentedValue = end < value.length
? value.sublist(start, end)
: value.sublist(start);
await CentralManager.instance
.writeCharacteristic(
characteristic, characteristic,
value: value, value: fragmentedValue,
type: type, type: type,
); );
final log = Log(LogType.write, value); final log = Log(
LogType.write,
fragmentedValue,
);
logs.value = [...logs.value, log]; logs.value = [...logs.value, log];
start = end;
}
} }
: null, : null,
child: const Text('WRITE'), child: const Text('WRITE'),
), ),
], ],
), ),
const SizedBox(height: 16.0),
], ],
); );
}, },
), ),
),
], ],
), ),
); );
@ -799,17 +828,14 @@ class _PeripheralViewState extends State<PeripheralView> {
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
rssiTimer.cancel(); connectionStateChangedSubscription.cancel();
stateChangedSubscription.cancel(); characteristicNotifiedSubscription.cancel();
valueChangedSubscription.cancel(); connectionState.dispose();
state.dispose();
services.dispose(); services.dispose();
characteristics.dispose(); characteristics.dispose();
service.dispose(); service.dispose();
characteristic.dispose(); characteristic.dispose();
writeType.dispose(); writeType.dispose();
maximumWriteLength.dispose();
rssi.dispose();
logs.dispose(); logs.dispose();
writeController.dispose(); writeController.dispose();
} }
@ -828,87 +854,63 @@ class _AdvertiserViewState extends State<AdvertiserView>
late final ValueNotifier<bool> advertising; late final ValueNotifier<bool> advertising;
late final ValueNotifier<List<Log>> logs; late final ValueNotifier<List<Log>> logs;
late final StreamSubscription stateChangedSubscription; late final StreamSubscription stateChangedSubscription;
late final StreamSubscription readCharacteristicCommandReceivedSubscription; late final StreamSubscription characteristicReadSubscription;
late final StreamSubscription writeCharacteristicCommandReceivedSubscription; late final StreamSubscription characteristicWrittenSubscription;
late final StreamSubscription notifyCharacteristicCommandReceivedSubscription; late final StreamSubscription characteristicNotifyStateChangedSubscription;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
state = ValueNotifier(peripheralManager.state); state = ValueNotifier(BluetoothLowEnergyState.unknown);
advertising = ValueNotifier(false); advertising = ValueNotifier(false);
logs = ValueNotifier([]); logs = ValueNotifier([]);
stateChangedSubscription = peripheralManager.stateChanged.listen( stateChangedSubscription = PeripheralManager.instance.stateChanged.listen(
(eventArgs) { (eventArgs) {
state.value = eventArgs.state; state.value = eventArgs.state;
}, },
); );
readCharacteristicCommandReceivedSubscription = characteristicReadSubscription =
peripheralManager.readCharacteristicCommandReceived.listen( PeripheralManager.instance.characteristicRead.listen(
(eventArgs) async { (eventArgs) async {
final central = eventArgs.central; final central = eventArgs.central;
final characteristic = eventArgs.characteristic; final characteristic = eventArgs.characteristic;
final id = eventArgs.id; final value = eventArgs.value;
final offset = eventArgs.offset;
final log = Log( final log = Log(
LogType.read, LogType.read,
Uint8List.fromList([]), value,
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', 'central: ${central.uuid}; characteristic: ${characteristic.uuid}',
); );
logs.value = [ logs.value = [
...logs.value, ...logs.value,
log, log,
]; ];
// final maximumWriteLength = peripheralManager.getMaximumWriteLength(
// central,
// );
const status = true;
final value = Uint8List.fromList([0x01, 0x02, 0x03]);
await peripheralManager.sendReadCharacteristicReply(
central,
characteristic: characteristic,
id: id,
offset: offset,
status: status,
value: value,
);
}, },
); );
writeCharacteristicCommandReceivedSubscription = characteristicWrittenSubscription =
peripheralManager.writeCharacteristicCommandReceived.listen( PeripheralManager.instance.characteristicWritten.listen(
(eventArgs) async { (eventArgs) async {
final central = eventArgs.central; final central = eventArgs.central;
final characteristic = eventArgs.characteristic; final characteristic = eventArgs.characteristic;
final id = eventArgs.id;
final offset = eventArgs.offset;
final value = eventArgs.value; final value = eventArgs.value;
final log = Log( final log = Log(
LogType.write, LogType.write,
value, value,
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', 'central: ${central.uuid}; characteristic: ${characteristic.uuid}',
); );
logs.value = [ logs.value = [
...logs.value, ...logs.value,
log, log,
]; ];
const status = true;
await peripheralManager.sendWriteCharacteristicReply(
central,
characteristic: characteristic,
id: id,
offset: offset,
status: status,
);
}, },
); );
notifyCharacteristicCommandReceivedSubscription = characteristicNotifyStateChangedSubscription =
peripheralManager.notifyCharacteristicCommandReceived.listen( PeripheralManager.instance.characteristicNotifyStateChanged.listen(
(eventArgs) async { (eventArgs) async {
final central = eventArgs.central; final central = eventArgs.central;
final characteristic = eventArgs.characteristic; final characteristic = eventArgs.characteristic;
final state = eventArgs.state; final state = eventArgs.state;
final log = Log( final log = Log(
LogType.write, LogType.notify,
Uint8List.fromList([]), Uint8List.fromList([]),
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state', 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state',
); );
@ -918,15 +920,21 @@ class _AdvertiserViewState extends State<AdvertiserView>
]; ];
// Write someting to the central when notify started. // Write someting to the central when notify started.
if (state) { if (state) {
final value = Uint8List.fromList([0x03, 0x02, 0x01]); final elements = List.generate(2000, (i) => i % 256);
await peripheralManager.notifyCharacteristicValueChanged( final value = Uint8List.fromList(elements);
central, await PeripheralManager.instance.writeCharacteristic(
characteristic: characteristic, characteristic,
value: value, value: value,
central: central,
); );
} }
}, },
); );
_initialize();
}
void _initialize() async {
state.value = await PeripheralManager.instance.getState();
} }
@override @override
@ -970,7 +978,9 @@ class _AdvertiserViewState extends State<AdvertiserView>
} }
Future<void> startAdvertising() async { Future<void> startAdvertising() async {
await peripheralManager.clearServices(); await PeripheralManager.instance.clearServices();
final elements = List.generate(1000, (i) => i % 256);
final value = Uint8List.fromList(elements);
final service = GattService( final service = GattService(
uuid: UUID.short(100), uuid: UUID.short(100),
characteristics: [ characteristics: [
@ -979,15 +989,16 @@ class _AdvertiserViewState extends State<AdvertiserView>
properties: [ properties: [
GattCharacteristicProperty.read, GattCharacteristicProperty.read,
], ],
value: value,
descriptors: [], descriptors: [],
), ),
GattCharacteristic( GattCharacteristic(
uuid: UUID.short(201), uuid: UUID.short(201),
properties: [ properties: [
GattCharacteristicProperty.read,
GattCharacteristicProperty.write, GattCharacteristicProperty.write,
GattCharacteristicProperty.writeWithoutResponse, GattCharacteristicProperty.writeWithoutResponse,
], ],
value: Uint8List.fromList([]),
descriptors: [], descriptors: [],
), ),
GattCharacteristic( GattCharacteristic(
@ -996,24 +1007,41 @@ class _AdvertiserViewState extends State<AdvertiserView>
GattCharacteristicProperty.notify, GattCharacteristicProperty.notify,
GattCharacteristicProperty.indicate, GattCharacteristicProperty.indicate,
], ],
value: Uint8List.fromList([]),
descriptors: [],
),
GattCharacteristic(
uuid: UUID.short(203),
properties: [
GattCharacteristicProperty.notify,
],
value: Uint8List.fromList([]),
descriptors: [],
),
GattCharacteristic(
uuid: UUID.short(204),
properties: [
GattCharacteristicProperty.indicate,
],
value: Uint8List.fromList([]),
descriptors: [], descriptors: [],
), ),
], ],
); );
await peripheralManager.addService(service); await PeripheralManager.instance.addService(service);
final advertisement = Advertisement( final advertisement = Advertisement(
name: 'flutter', name: 'le12138',
manufacturerSpecificData: ManufacturerSpecificData( manufacturerSpecificData: ManufacturerSpecificData(
id: 0x2e19, id: 0x2e19,
data: Uint8List.fromList([0x01, 0x02, 0x03]), data: Uint8List.fromList([0x01, 0x02, 0x03]),
), ),
); );
await peripheralManager.startAdvertising(advertisement); await PeripheralManager.instance.startAdvertising(advertisement);
advertising.value = true; advertising.value = true;
} }
Future<void> stopAdvertising() async { Future<void> stopAdvertising() async {
await peripheralManager.stopAdvertising(); await PeripheralManager.instance.stopAdvertising();
advertising.value = false; advertising.value = false;
} }
@ -1074,9 +1102,9 @@ class _AdvertiserViewState extends State<AdvertiserView>
void dispose() { void dispose() {
super.dispose(); super.dispose();
stateChangedSubscription.cancel(); stateChangedSubscription.cancel();
readCharacteristicCommandReceivedSubscription.cancel(); characteristicReadSubscription.cancel();
writeCharacteristicCommandReceivedSubscription.cancel(); characteristicWrittenSubscription.cancel();
notifyCharacteristicCommandReceivedSubscription.cancel(); characteristicNotifyStateChangedSubscription.cancel();
state.dispose(); state.dispose();
advertising.dispose(); advertising.dispose();
logs.dispose(); logs.dispose();

View File

@ -20,4 +20,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
COCOAPODS: 1.12.1 COCOAPODS: 1.14.3

View File

@ -23,39 +23,47 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "4.0.0" version: "5.0.0"
bluetooth_low_energy_android: bluetooth_low_energy_android:
dependency: transitive dependency: transitive
description: description:
name: bluetooth_low_energy_android name: bluetooth_low_energy_android
sha256: "7668f695f195ed67b9985a76a21f10d181a26ebfc2124b4cd272fb6f202c1ee9" sha256: "502382b2bc6d0bf9e7aa635bafa28057cb8538d6f2dd9c2eceb6d0111bcee94a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "5.0.0"
bluetooth_low_energy_darwin: bluetooth_low_energy_darwin:
dependency: transitive dependency: transitive
description: description:
name: bluetooth_low_energy_darwin name: bluetooth_low_energy_darwin
sha256: "027b46d8efea5c726cc32ab6b2ca387f0ef15507afcd1077d75712ad4e7f941d" sha256: "9f28475bb878b5a48c2746c776010bc77d7040b5fead128295d22f0ce6ef5995"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "5.0.0"
bluetooth_low_energy_linux: bluetooth_low_energy_linux:
dependency: transitive dependency: transitive
description: description:
name: bluetooth_low_energy_linux name: bluetooth_low_energy_linux
sha256: "548a869579684ce7602577ef5e0498d18eef552fb2abb3ee7909814bef57807f" sha256: "100dc824a9b409442e7018994de74d56d65faa95b8c6fbbf5a8c0ae70cd58286"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "5.0.0"
bluetooth_low_energy_platform_interface: bluetooth_low_energy_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: bluetooth_low_energy_platform_interface name: bluetooth_low_energy_platform_interface
sha256: a01819f4ef89d033edaa979465ec8c3af13b2618dc718d90fe681be91b6c4356 sha256: "54f92ab2d7746fb6f2b4a40a48cd7eb8e3bf772f3ee89e1979d4d7b741fb2a05"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "5.0.0"
bluetooth_low_energy_windows:
dependency: transitive
description:
name: bluetooth_low_energy_windows
sha256: "1a4382a1c3d480a35b4de5b7065b00e739cf44129108006c2a3ed8f3ca99fd3d"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
bluez: bluez:
dependency: transitive dependency: transitive
description: description:
@ -92,10 +100,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.2" version: "1.18.0"
convert: convert:
dependency: "direct main" dependency: "direct main"
description: description:
@ -116,10 +124,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: dbus name: dbus
sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.8" version: "0.7.10"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -158,10 +166,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.1"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -181,10 +189,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: intl name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.18.1" version: "0.19.0"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -229,10 +237,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.10.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -245,26 +253,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: petitparser name: petitparser
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.4.0" version: "6.0.2"
platform: platform:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.1.2"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.6" version: "2.1.7"
process: process:
dependency: transitive dependency: transitive
description: description:
@ -290,18 +298,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.11.1"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
@ -330,10 +338,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.6.1"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -354,18 +362,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.7.1" version: "11.10.0"
web: web:
dependency: transitive dependency: transitive
description: description:
name: web name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.4-beta" version: "0.3.0"
webdriver: webdriver:
dependency: transitive dependency: transitive
description: description:
@ -378,10 +386,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: xml name: xml
sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.0" version: "6.5.0"
sdks: sdks:
dart: ">=3.1.0-185.0.dev <4.0.0" dart: ">=3.2.0 <4.0.0"
flutter: ">=3.3.0" flutter: ">=3.3.0"

View File

@ -29,7 +29,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
convert: ^3.1.1 convert: ^3.1.1
intl: ^0.18.1 intl: ^0.19.0
dev_dependencies: dev_dependencies:
integration_test: integration_test:

View File

@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake)
# https://github.com/flutter/flutter/issues/57146. # https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# Set fallback configurations for older versions of the flutter tool.
if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
set(FLUTTER_TARGET_PLATFORM "windows-x64")
endif()
# === Flutter Library === # === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
@ -92,7 +97,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E env COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT} ${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG> ${FLUTTER_TARGET_PLATFORM} $<CONFIG>
VERBATIM VERBATIM
) )
add_custom_target(flutter_assemble DEPENDS add_custom_target(flutter_assemble DEPENDS

View File

@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <bluetooth_low_energy_windows/bluetooth_low_energy_windows_c_api.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
BluetoothLowEnergyWindowsCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("BluetoothLowEnergyWindowsCApi"));
} }

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
bluetooth_low_energy_windows
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -1,14 +1,14 @@
/// A Flutter plugin for controlling the bluetooth low energy, supports central /// A Flutter plugin for controlling the bluetooth low energy, supports central
/// and peripheral apis. /// and peripheral apis.
library; library bluetooth_low_energy;
export 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart' export 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'
hide hide
MyCentralManager, MyBluetoothLowEnergyPeer,
MyPeripheralManager,
MyObject,
MyCentral, MyCentral,
MyPeripheral, MyPeripheral,
MyGattService, MyGattAttribute,
MyGattAttributeUint8List,
MyGattDescriptor,
MyGattCharacteristic, MyGattCharacteristic,
MyGattDescriptor; MyGattService;

View File

@ -1,6 +1,6 @@
name: bluetooth_low_energy name: bluetooth_low_energy
description: A Flutter plugin for controlling the bluetooth low energy, supports central and peripheral apis. description: A Flutter plugin for controlling the bluetooth low energy, supports central and peripheral apis.
version: 4.0.0 version: 5.0.0
homepage: https://github.com/yanshouwang/bluetooth_low_energy homepage: https://github.com/yanshouwang/bluetooth_low_energy
environment: environment:
@ -10,10 +10,11 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
bluetooth_low_energy_platform_interface: ^4.0.0 bluetooth_low_energy_platform_interface: ^5.0.0
bluetooth_low_energy_android: ^4.0.0 bluetooth_low_energy_android: ^5.0.0
bluetooth_low_energy_darwin: ^4.0.0 bluetooth_low_energy_darwin: ^5.0.0
bluetooth_low_energy_linux: ^4.0.0 bluetooth_low_energy_windows: ^5.0.0
bluetooth_low_energy_linux: ^5.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -29,5 +30,7 @@ flutter:
default_package: bluetooth_low_energy_darwin default_package: bluetooth_low_energy_darwin
macos: macos:
default_package: bluetooth_low_energy_darwin default_package: bluetooth_low_energy_darwin
windows:
default_package: bluetooth_low_energy_windows
linux: linux:
default_package: bluetooth_low_energy_linux default_package: bluetooth_low_energy_linux

View File

@ -1,3 +1,50 @@
## 5.0.0
* Now `CentralManager#writeCharacteristic` and `PeripheralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes.
* Add `UUID#fromAddress` constructor.
* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`.
* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`.
* Add `PeripheralManager#readCharacteristic`.
* Remove `CentralManager#getMaximumWriteLength` method.
* Remove `PeripheralManager#getMaximumWriteLength` method.
* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`.
* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`.
* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`.
* Move `CentralManager#state` to `CentralManager#getState()`.
* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`.
* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`.
* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`.
* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`.
* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`.
* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#writeCharacteristic`.
* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`.
* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`.
## 5.0.0-dev.4
* Fix the issue that `PeripheralMananger#startAdvertising` throws after powered off.\
* Optimize project structure.
## 5.0.0-dev.3
* Implements new Api.
## 5.0.0-dev.2
* Optimize example.
* Add event logs.
* Fix the issue that PeripheralManager's service duplicated after hot reload.
* Fix the issue that `PeripheralManager#notifyCharacteristicChanged` lost data when value is larger then the MTU size.
* Optimize instances' retrieve speed.
* Update dependency.
## 5.0.0-dev.1
* Implement the `5.0.0` api.
* Optimize example.
* Remove `CentralManager#getMaximumWriteLength` method.
* Remove `PeripheralManager#getMaximumWriteLength` method.
## 4.0.0 ## 4.0.0
* Remove `BluetoothLowEnergy` class. * Remove `BluetoothLowEnergy` class.

View File

@ -6,16 +6,16 @@ import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
/** BluetoothLowEnergyAndroid */ /** BluetoothLowEnergyAndroid */
class BluetoothLowEnergyAndroid : FlutterPlugin, ActivityAware { class BluetoothLowEnergyAndroid : FlutterPlugin, ActivityAware {
private lateinit var centralManager: MyCentralManager private lateinit var mCentralManager: MyCentralManager
private lateinit var peripheralManager: MyPeripheralManager private lateinit var mPeripheralManager: MyPeripheralManager
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
val context = binding.applicationContext val context = binding.applicationContext
val binaryMessenger = binding.binaryMessenger val binaryMessenger = binding.binaryMessenger
centralManager = MyCentralManager(context, binaryMessenger) mCentralManager = MyCentralManager(context, binaryMessenger)
peripheralManager = MyPeripheralManager(context, binaryMessenger) mPeripheralManager = MyPeripheralManager(context, binaryMessenger)
MyCentralManagerHostApi.setUp(binaryMessenger, centralManager) MyCentralManagerHostApi.setUp(binaryMessenger, mCentralManager)
MyPeripheralManagerHostApi.setUp(binaryMessenger, peripheralManager) MyPeripheralManagerHostApi.setUp(binaryMessenger, mPeripheralManager)
} }
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
@ -25,13 +25,13 @@ class BluetoothLowEnergyAndroid : FlutterPlugin, ActivityAware {
} }
override fun onAttachedToActivity(binding: ActivityPluginBinding) { override fun onAttachedToActivity(binding: ActivityPluginBinding) {
centralManager.onAttachedToActivity(binding) mCentralManager.onAttachedToActivity(binding)
peripheralManager.onAttachedToActivity(binding) mPeripheralManager.onAttachedToActivity(binding)
} }
override fun onDetachedFromActivity() { override fun onDetachedFromActivity() {
centralManager.onDetachedFromActivity() mCentralManager.onDetachedFromActivity()
peripheralManager.onDetachedFromActivity() mPeripheralManager.onDetachedFromActivity()
} }
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {

View File

@ -3,14 +3,20 @@ package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.le.AdvertiseCallback import android.bluetooth.le.AdvertiseCallback
import android.bluetooth.le.AdvertiseSettings import android.bluetooth.le.AdvertiseSettings
class MyAdvertiseCallback(private val peripheralManager: MyPeripheralManager) : AdvertiseCallback() { class MyAdvertiseCallback(manager: MyPeripheralManager) : AdvertiseCallback() {
private val mManager: MyPeripheralManager
init {
mManager = manager
}
override fun onStartSuccess(settingsInEffect: AdvertiseSettings) { override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
super.onStartSuccess(settingsInEffect) super.onStartSuccess(settingsInEffect)
peripheralManager.onStartSuccess(settingsInEffect) mManager.onStartSuccess(settingsInEffect)
} }
override fun onStartFailure(errorCode: Int) { override fun onStartFailure(errorCode: Int) {
super.onStartFailure(errorCode) super.onStartFailure(errorCode)
peripheralManager.onStartFailure(errorCode) mManager.onStartFailure(errorCode)
} }
} }

View File

@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v12.0.1), do not edit directly. // Autogenerated from Pigeon (v15.0.2), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
package dev.yanshouwang.bluetooth_low_energy_android package dev.yanshouwang.bluetooth_low_energy_android
@ -31,6 +31,9 @@ private fun wrapError(exception: Throwable): List<Any?> {
} }
} }
private fun createConnectionError(channelName: String): FlutterError {
return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "")}
/** /**
* Error class for passing custom error details to Flutter via a thrown PlatformException. * Error class for passing custom error details to Flutter via a thrown PlatformException.
* @property code The error code. * @property code The error code.
@ -82,84 +85,55 @@ enum class MyGattCharacteristicWriteTypeArgs(val raw: Int) {
} }
} }
/** Generated class from Pigeon that represents data sent in messages. */ enum class MyGattCharacteristicNotifyStateArgs(val raw: Int) {
data class MyCentralManagerArgs ( NONE(0),
val stateNumberArgs: Long NOTIFY(1),
INDICATE(2);
) {
companion object { companion object {
@Suppress("UNCHECKED_CAST") fun ofRaw(raw: Int): MyGattCharacteristicNotifyStateArgs? {
fun fromList(list: List<Any?>): MyCentralManagerArgs { return values().firstOrNull { it.raw == raw }
val stateNumberArgs = list[0].let { if (it is Int) it.toLong() else it as Long }
return MyCentralManagerArgs(stateNumberArgs)
} }
} }
fun toList(): List<Any?> { }
return listOf<Any?>(
stateNumberArgs, enum class MyGattStatusArgs(val raw: Int) {
) SUCCESS(0),
READNOTPERMITTED(1),
WRITENOTPERMITTED(2),
REQUESTNOTSUPPORTED(3),
INVALIDOFFSET(4),
INSUFFICIENTAUTHENTICATION(5),
INSUFFICIENTENCRYPTION(6),
INVALIDATTRIBUTELENGTH(7),
CONNECTIONCONGESTED(8),
FAILURE(9);
companion object {
fun ofRaw(raw: Int): MyGattStatusArgs? {
return values().firstOrNull { it.raw == raw }
}
} }
} }
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
data class MyPeripheralManagerArgs ( data class MyManufacturerSpecificDataArgs (
val stateNumberArgs: Long val idArgs: Long,
val dataArgs: ByteArray
) { ) {
companion object { companion object {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyPeripheralManagerArgs { fun fromList(list: List<Any?>): MyManufacturerSpecificDataArgs {
val stateNumberArgs = list[0].let { if (it is Int) it.toLong() else it as Long } val idArgs = list[0].let { if (it is Int) it.toLong() else it as Long }
return MyPeripheralManagerArgs(stateNumberArgs) val dataArgs = list[1] as ByteArray
return MyManufacturerSpecificDataArgs(idArgs, dataArgs)
} }
} }
fun toList(): List<Any?> { fun toList(): List<Any?> {
return listOf<Any?>( return listOf<Any?>(
stateNumberArgs, idArgs,
) dataArgs,
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyCentralArgs (
val hashCodeArgs: Long,
val uuidArgs: String
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyCentralArgs {
val hashCodeArgs = list[0].let { if (it is Int) it.toLong() else it as Long }
val uuidArgs = list[1] as String
return MyCentralArgs(hashCodeArgs, uuidArgs)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
hashCodeArgs,
uuidArgs,
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyPeripheralArgs (
val hashCodeArgs: Long,
val uuidArgs: String
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyPeripheralArgs {
val hashCodeArgs = list[0].let { if (it is Int) it.toLong() else it as Long }
val uuidArgs = list[1] as String
return MyPeripheralArgs(hashCodeArgs, uuidArgs)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
hashCodeArgs,
uuidArgs,
) )
} }
} }
@ -195,48 +169,64 @@ data class MyAdvertisementArgs (
} }
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
data class MyManufacturerSpecificDataArgs ( data class MyCentralArgs (
val idArgs: Long, val addressArgs: String
val dataArgs: ByteArray
) { ) {
companion object { companion object {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyManufacturerSpecificDataArgs { fun fromList(list: List<Any?>): MyCentralArgs {
val idArgs = list[0].let { if (it is Int) it.toLong() else it as Long } val addressArgs = list[0] as String
val dataArgs = list[1] as ByteArray return MyCentralArgs(addressArgs)
return MyManufacturerSpecificDataArgs(idArgs, dataArgs)
} }
} }
fun toList(): List<Any?> { fun toList(): List<Any?> {
return listOf<Any?>( return listOf<Any?>(
idArgs, addressArgs,
dataArgs,
) )
} }
} }
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
data class MyGattServiceArgs ( data class MyPeripheralArgs (
val hashCodeArgs: Long, val addressArgs: String
val uuidArgs: String,
val characteristicsArgs: List<MyGattCharacteristicArgs?>
) { ) {
companion object { companion object {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyGattServiceArgs { fun fromList(list: List<Any?>): MyPeripheralArgs {
val addressArgs = list[0] as String
return MyPeripheralArgs(addressArgs)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
addressArgs,
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyGattDescriptorArgs (
val hashCodeArgs: Long,
val uuidArgs: String,
val valueArgs: ByteArray? = null
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyGattDescriptorArgs {
val hashCodeArgs = list[0].let { if (it is Int) it.toLong() else it as Long } val hashCodeArgs = list[0].let { if (it is Int) it.toLong() else it as Long }
val uuidArgs = list[1] as String val uuidArgs = list[1] as String
val characteristicsArgs = list[2] as List<MyGattCharacteristicArgs?> val valueArgs = list[2] as ByteArray?
return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs) return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, valueArgs)
} }
} }
fun toList(): List<Any?> { fun toList(): List<Any?> {
return listOf<Any?>( return listOf<Any?>(
hashCodeArgs, hashCodeArgs,
uuidArgs, uuidArgs,
characteristicsArgs, valueArgs,
) )
} }
} }
@ -270,26 +260,26 @@ data class MyGattCharacteristicArgs (
} }
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
data class MyGattDescriptorArgs ( data class MyGattServiceArgs (
val hashCodeArgs: Long, val hashCodeArgs: Long,
val uuidArgs: String, val uuidArgs: String,
val valueArgs: ByteArray? = null val characteristicsArgs: List<MyGattCharacteristicArgs?>
) { ) {
companion object { companion object {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyGattDescriptorArgs { fun fromList(list: List<Any?>): MyGattServiceArgs {
val hashCodeArgs = list[0].let { if (it is Int) it.toLong() else it as Long } val hashCodeArgs = list[0].let { if (it is Int) it.toLong() else it as Long }
val uuidArgs = list[1] as String val uuidArgs = list[1] as String
val valueArgs = list[2] as ByteArray? val characteristicsArgs = list[2] as List<MyGattCharacteristicArgs?>
return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, valueArgs) return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs)
} }
} }
fun toList(): List<Any?> { fun toList(): List<Any?> {
return listOf<Any?>( return listOf<Any?>(
hashCodeArgs, hashCodeArgs,
uuidArgs, uuidArgs,
valueArgs, characteristicsArgs,
) )
} }
} }
@ -300,20 +290,15 @@ private object MyCentralManagerHostApiCodec : StandardMessageCodec() {
return when (type) { return when (type) {
128.toByte() -> { 128.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
MyCentralManagerArgs.fromList(it) MyGattCharacteristicArgs.fromList(it)
} }
} }
129.toByte() -> { 129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyGattCharacteristicArgs.fromList(it)
}
}
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
MyGattDescriptorArgs.fromList(it) MyGattDescriptorArgs.fromList(it)
} }
} }
131.toByte() -> { 130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
MyGattServiceArgs.fromList(it) MyGattServiceArgs.fromList(it)
} }
@ -323,20 +308,16 @@ private object MyCentralManagerHostApiCodec : StandardMessageCodec() {
} }
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
when (value) { when (value) {
is MyCentralManagerArgs -> { is MyGattCharacteristicArgs -> {
stream.write(128) stream.write(128)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is MyGattCharacteristicArgs -> { is MyGattDescriptorArgs -> {
stream.write(129) stream.write(129)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is MyGattDescriptorArgs -> {
stream.write(130)
writeValue(stream, value.toList())
}
is MyGattServiceArgs -> { is MyGattServiceArgs -> {
stream.write(131) stream.write(130)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
else -> super.writeValue(stream, value) else -> super.writeValue(stream, value)
@ -346,20 +327,19 @@ private object MyCentralManagerHostApiCodec : StandardMessageCodec() {
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface MyCentralManagerHostApi { interface MyCentralManagerHostApi {
fun setUp(callback: (Result<MyCentralManagerArgs>) -> Unit) fun setUp()
fun startDiscovery(callback: (Result<Unit>) -> Unit) fun startDiscovery(callback: (Result<Unit>) -> Unit)
fun stopDiscovery() fun stopDiscovery()
fun connect(peripheralHashCodeArgs: Long, callback: (Result<Unit>) -> Unit) fun connect(addressArgs: String, callback: (Result<Unit>) -> Unit)
fun disconnect(peripheralHashCodeArgs: Long, callback: (Result<Unit>) -> Unit) fun disconnect(addressArgs: String, callback: (Result<Unit>) -> Unit)
fun getMaximumWriteLength(peripheralHashCodeArgs: Long, typeNumberArgs: Long): Long fun requestMTU(addressArgs: String, mtuArgs: Long, callback: (Result<Long>) -> Unit)
fun readRSSI(peripheralHashCodeArgs: Long, callback: (Result<Long>) -> Unit) fun readRSSI(addressArgs: String, callback: (Result<Long>) -> Unit)
fun discoverGATT(peripheralHashCodeArgs: Long, callback: (Result<List<MyGattServiceArgs>>) -> Unit) fun discoverServices(addressArgs: String, callback: (Result<List<MyGattServiceArgs>>) -> Unit)
fun requestMTU(peripheralHashCodeArgs: Long, mtuArgs: Long, callback: (Result<Long>) -> Unit) fun readCharacteristic(addressArgs: String, hashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit)
fun readCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit) fun writeCharacteristic(addressArgs: String, hashCodeArgs: Long, valueArgs: ByteArray, typeNumberArgs: Long, callback: (Result<Unit>) -> Unit)
fun writeCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, typeNumberArgs: Long, callback: (Result<Unit>) -> Unit) fun setCharacteristicNotifyState(addressArgs: String, hashCodeArgs: Long, stateNumberArgs: Long, callback: (Result<Unit>) -> Unit)
fun notifyCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, stateArgs: Boolean, callback: (Result<Unit>) -> Unit) fun readDescriptor(addressArgs: String, hashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit)
fun readDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit) fun writeDescriptor(addressArgs: String, hashCodeArgs: Long, valueArgs: ByteArray, callback: (Result<Unit>) -> Unit)
fun writeDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result<Unit>) -> Unit)
companion object { companion object {
/** The codec used by MyCentralManagerHostApi. */ /** The codec used by MyCentralManagerHostApi. */
@ -373,15 +353,14 @@ interface MyCentralManagerHostApi {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setUp", codec) val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setUp", codec)
if (api != null) { if (api != null) {
channel.setMessageHandler { _, reply -> channel.setMessageHandler { _, reply ->
api.setUp() { result: Result<MyCentralManagerArgs> -> var wrapped: List<Any?>
val error = result.exceptionOrNull() try {
if (error != null) { api.setUp()
reply.reply(wrapError(error)) wrapped = listOf<Any?>(null)
} else { } catch (exception: Throwable) {
val data = result.getOrNull() wrapped = wrapError(exception)
reply.reply(wrapResult(data))
}
} }
reply.reply(wrapped)
} }
} else { } else {
channel.setMessageHandler(null) channel.setMessageHandler(null)
@ -426,8 +405,8 @@ interface MyCentralManagerHostApi {
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[0] as String
api.connect(peripheralHashCodeArgsArg) { result: Result<Unit> -> api.connect(addressArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull() val error = result.exceptionOrNull()
if (error != null) { if (error != null) {
reply.reply(wrapError(error)) reply.reply(wrapError(error))
@ -445,8 +424,8 @@ interface MyCentralManagerHostApi {
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[0] as String
api.disconnect(peripheralHashCodeArgsArg) { result: Result<Unit> -> api.disconnect(addressArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull() val error = result.exceptionOrNull()
if (error != null) { if (error != null) {
reply.reply(wrapError(error)) reply.reply(wrapError(error))
@ -460,19 +439,21 @@ interface MyCentralManagerHostApi {
} }
} }
run { run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.getMaximumWriteLength", codec) val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.requestMTU", codec)
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[0] as String
val typeNumberArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } val mtuArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
var wrapped: List<Any?> api.requestMTU(addressArgsArg, mtuArgsArg) { result: Result<Long> ->
try { val error = result.exceptionOrNull()
wrapped = listOf<Any?>(api.getMaximumWriteLength(peripheralHashCodeArgsArg, typeNumberArgsArg)) if (error != null) {
} catch (exception: Throwable) { reply.reply(wrapError(error))
wrapped = wrapError(exception) } else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
} }
reply.reply(wrapped)
} }
} else { } else {
channel.setMessageHandler(null) channel.setMessageHandler(null)
@ -483,8 +464,8 @@ interface MyCentralManagerHostApi {
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[0] as String
api.readRSSI(peripheralHashCodeArgsArg) { result: Result<Long> -> api.readRSSI(addressArgsArg) { result: Result<Long> ->
val error = result.exceptionOrNull() val error = result.exceptionOrNull()
if (error != null) { if (error != null) {
reply.reply(wrapError(error)) reply.reply(wrapError(error))
@ -499,33 +480,12 @@ interface MyCentralManagerHostApi {
} }
} }
run { run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.discoverGATT", codec) val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.discoverServices", codec)
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[0] as String
api.discoverGATT(peripheralHashCodeArgsArg) { result: Result<List<MyGattServiceArgs>> -> api.discoverServices(addressArgsArg) { result: Result<List<MyGattServiceArgs>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.requestMTU", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val mtuArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
api.requestMTU(peripheralHashCodeArgsArg, mtuArgsArg) { result: Result<Long> ->
val error = result.exceptionOrNull() val error = result.exceptionOrNull()
if (error != null) { if (error != null) {
reply.reply(wrapError(error)) reply.reply(wrapError(error))
@ -544,9 +504,9 @@ interface MyCentralManagerHostApi {
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[0] as String
val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
api.readCharacteristic(peripheralHashCodeArgsArg, characteristicHashCodeArgsArg) { result: Result<ByteArray> -> api.readCharacteristic(addressArgsArg, hashCodeArgsArg) { result: Result<ByteArray> ->
val error = result.exceptionOrNull() val error = result.exceptionOrNull()
if (error != null) { if (error != null) {
reply.reply(wrapError(error)) reply.reply(wrapError(error))
@ -565,11 +525,11 @@ interface MyCentralManagerHostApi {
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[0] as String
val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val valueArgsArg = args[2] as ByteArray val valueArgsArg = args[2] as ByteArray
val typeNumberArgsArg = args[3].let { if (it is Int) it.toLong() else it as Long } val typeNumberArgsArg = args[3].let { if (it is Int) it.toLong() else it as Long }
api.writeCharacteristic(peripheralHashCodeArgsArg, characteristicHashCodeArgsArg, valueArgsArg, typeNumberArgsArg) { result: Result<Unit> -> api.writeCharacteristic(addressArgsArg, hashCodeArgsArg, valueArgsArg, typeNumberArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull() val error = result.exceptionOrNull()
if (error != null) { if (error != null) {
reply.reply(wrapError(error)) reply.reply(wrapError(error))
@ -583,14 +543,14 @@ interface MyCentralManagerHostApi {
} }
} }
run { run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.notifyCharacteristic", codec) val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerHostApi.setCharacteristicNotifyState", codec)
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[0] as String
val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val stateArgsArg = args[2] as Boolean val stateNumberArgsArg = args[2].let { if (it is Int) it.toLong() else it as Long }
api.notifyCharacteristic(peripheralHashCodeArgsArg, characteristicHashCodeArgsArg, stateArgsArg) { result: Result<Unit> -> api.setCharacteristicNotifyState(addressArgsArg, hashCodeArgsArg, stateNumberArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull() val error = result.exceptionOrNull()
if (error != null) { if (error != null) {
reply.reply(wrapError(error)) reply.reply(wrapError(error))
@ -608,9 +568,9 @@ interface MyCentralManagerHostApi {
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[0] as String
val descriptorHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
api.readDescriptor(peripheralHashCodeArgsArg, descriptorHashCodeArgsArg) { result: Result<ByteArray> -> api.readDescriptor(addressArgsArg, hashCodeArgsArg) { result: Result<ByteArray> ->
val error = result.exceptionOrNull() val error = result.exceptionOrNull()
if (error != null) { if (error != null) {
reply.reply(wrapError(error)) reply.reply(wrapError(error))
@ -629,10 +589,10 @@ interface MyCentralManagerHostApi {
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[0] as String
val descriptorHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val valueArgsArg = args[2] as ByteArray val valueArgsArg = args[2] as ByteArray
api.writeDescriptor(peripheralHashCodeArgsArg, descriptorHashCodeArgsArg, valueArgsArg) { result: Result<Unit> -> api.writeDescriptor(addressArgsArg, hashCodeArgsArg, valueArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull() val error = result.exceptionOrNull()
if (error != null) { if (error != null) {
reply.reply(wrapError(error)) reply.reply(wrapError(error))
@ -658,21 +618,11 @@ private object MyCentralManagerFlutterApiCodec : StandardMessageCodec() {
} }
} }
129.toByte() -> { 129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyGattCharacteristicArgs.fromList(it)
}
}
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyGattDescriptorArgs.fromList(it)
}
}
131.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
MyManufacturerSpecificDataArgs.fromList(it) MyManufacturerSpecificDataArgs.fromList(it)
} }
} }
132.toByte() -> { 130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
MyPeripheralArgs.fromList(it) MyPeripheralArgs.fromList(it)
} }
@ -686,20 +636,12 @@ private object MyCentralManagerFlutterApiCodec : StandardMessageCodec() {
stream.write(128) stream.write(128)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is MyGattCharacteristicArgs -> { is MyManufacturerSpecificDataArgs -> {
stream.write(129) stream.write(129)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is MyGattDescriptorArgs -> {
stream.write(130)
writeValue(stream, value.toList())
}
is MyManufacturerSpecificDataArgs -> {
stream.write(131)
writeValue(stream, value.toList())
}
is MyPeripheralArgs -> { is MyPeripheralArgs -> {
stream.write(132) stream.write(130)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
else -> super.writeValue(stream, value) else -> super.writeValue(stream, value)
@ -717,58 +659,77 @@ class MyCentralManagerFlutterApi(private val binaryMessenger: BinaryMessenger) {
} }
} }
fun onStateChanged(stateNumberArgsArg: Long, callback: (Result<Unit>) -> Unit) { fun onStateChanged(stateNumberArgsArg: Long, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onStateChanged", codec) val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onStateChanged"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(stateNumberArgsArg)) { channel.send(listOf(stateNumberArgsArg)) {
if (it is List<*>) { if (it is List<*>) {
if (it.size > 1) { if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else { } else {
callback(Result.success(Unit)); callback(Result.success(Unit))
} }
} else { } else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); callback(Result.failure(createConnectionError(channelName)))
} }
} }
} }
fun onDiscovered(peripheralArgsArg: MyPeripheralArgs, rssiArgsArg: Long, advertisementArgsArg: MyAdvertisementArgs, callback: (Result<Unit>) -> Unit) { fun onDiscovered(peripheralArgsArg: MyPeripheralArgs, rssiArgsArg: Long, advertisementArgsArg: MyAdvertisementArgs, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onDiscovered", codec) val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onDiscovered"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(peripheralArgsArg, rssiArgsArg, advertisementArgsArg)) { channel.send(listOf(peripheralArgsArg, rssiArgsArg, advertisementArgsArg)) {
if (it is List<*>) { if (it is List<*>) {
if (it.size > 1) { if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else { } else {
callback(Result.success(Unit)); callback(Result.success(Unit))
} }
} else { } else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); callback(Result.failure(createConnectionError(channelName)))
} }
} }
} }
fun onPeripheralStateChanged(peripheralArgsArg: MyPeripheralArgs, stateArgsArg: Boolean, callback: (Result<Unit>) -> Unit) { fun onConnectionStateChanged(addressArgsArg: String, stateArgsArg: Boolean, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onPeripheralStateChanged", codec) val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onConnectionStateChanged"
channel.send(listOf(peripheralArgsArg, stateArgsArg)) { val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, stateArgsArg)) {
if (it is List<*>) { if (it is List<*>) {
if (it.size > 1) { if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else { } else {
callback(Result.success(Unit)); callback(Result.success(Unit))
} }
} else { } else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); callback(Result.failure(createConnectionError(channelName)))
} }
} }
} }
fun onCharacteristicValueChanged(characteristicArgsArg: MyGattCharacteristicArgs, valueArgsArg: ByteArray, callback: (Result<Unit>) -> Unit) { fun onMtuChanged(addressArgsArg: String, mtuArgsArg: Long, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicValueChanged", codec) val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onMtuChanged"
channel.send(listOf(characteristicArgsArg, valueArgsArg)) { val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, mtuArgsArg)) {
if (it is List<*>) { if (it is List<*>) {
if (it.size > 1) { if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else { } else {
callback(Result.success(Unit)); callback(Result.success(Unit))
} }
} else { } else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onCharacteristicNotified(addressArgsArg: String, hashCodeArgsArg: Long, valueArgsArg: ByteArray, callback: (Result<Unit>) -> Unit) {
val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicNotified"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, hashCodeArgsArg, valueArgsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
} }
} }
} }
@ -802,11 +763,6 @@ private object MyPeripheralManagerHostApiCodec : StandardMessageCodec() {
MyManufacturerSpecificDataArgs.fromList(it) MyManufacturerSpecificDataArgs.fromList(it)
} }
} }
133.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyPeripheralManagerArgs.fromList(it)
}
}
else -> super.readValueOfType(type, buffer) else -> super.readValueOfType(type, buffer)
} }
} }
@ -832,10 +788,6 @@ private object MyPeripheralManagerHostApiCodec : StandardMessageCodec() {
stream.write(132) stream.write(132)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is MyPeripheralManagerArgs -> {
stream.write(133)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value) else -> super.writeValue(stream, value)
} }
} }
@ -843,16 +795,14 @@ private object MyPeripheralManagerHostApiCodec : StandardMessageCodec() {
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface MyPeripheralManagerHostApi { interface MyPeripheralManagerHostApi {
fun setUp(callback: (Result<MyPeripheralManagerArgs>) -> Unit) fun setUp()
fun addService(serviceArgs: MyGattServiceArgs, callback: (Result<Unit>) -> Unit) fun addService(serviceArgs: MyGattServiceArgs, callback: (Result<Unit>) -> Unit)
fun removeService(serviceHashCodeArgs: Long) fun removeService(hashCodeArgs: Long)
fun clearServices() fun clearServices()
fun startAdvertising(advertisementArgs: MyAdvertisementArgs, callback: (Result<Unit>) -> Unit) fun startAdvertising(advertisementArgs: MyAdvertisementArgs, callback: (Result<Unit>) -> Unit)
fun stopAdvertising() fun stopAdvertising()
fun getMaximumWriteLength(centralHashCodeArgs: Long): Long fun sendResponse(addressArgs: String, idArgs: Long, statusNumberArgs: Long, offsetArgs: Long, valueArgs: ByteArray?)
fun sendReadCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean, valueArgs: ByteArray) fun notifyCharacteristicChanged(hashCodeArgs: Long, valueArgs: ByteArray, confirmArgs: Boolean, addressArgs: String, callback: (Result<Unit>) -> Unit)
fun sendWriteCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean)
fun notifyCharacteristicValueChanged(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result<Unit>) -> Unit)
companion object { companion object {
/** The codec used by MyPeripheralManagerHostApi. */ /** The codec used by MyPeripheralManagerHostApi. */
@ -866,15 +816,14 @@ interface MyPeripheralManagerHostApi {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.setUp", codec) val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.setUp", codec)
if (api != null) { if (api != null) {
channel.setMessageHandler { _, reply -> channel.setMessageHandler { _, reply ->
api.setUp() { result: Result<MyPeripheralManagerArgs> -> var wrapped: List<Any?>
val error = result.exceptionOrNull() try {
if (error != null) { api.setUp()
reply.reply(wrapError(error)) wrapped = listOf<Any?>(null)
} else { } catch (exception: Throwable) {
val data = result.getOrNull() wrapped = wrapError(exception)
reply.reply(wrapResult(data))
}
} }
reply.reply(wrapped)
} }
} else { } else {
channel.setMessageHandler(null) channel.setMessageHandler(null)
@ -904,10 +853,10 @@ interface MyPeripheralManagerHostApi {
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val serviceHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val hashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
var wrapped: List<Any?> var wrapped: List<Any?>
try { try {
api.removeService(serviceHashCodeArgsArg) api.removeService(hashCodeArgsArg)
wrapped = listOf<Any?>(null) wrapped = listOf<Any?>(null)
} catch (exception: Throwable) { } catch (exception: Throwable) {
wrapped = wrapError(exception) wrapped = wrapError(exception)
@ -972,37 +921,18 @@ interface MyPeripheralManagerHostApi {
} }
} }
run { run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.getMaximumWriteLength", codec) val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.sendResponse", codec)
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val centralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[0] as String
var wrapped: List<Any?> val idArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
try { val statusNumberArgsArg = args[2].let { if (it is Int) it.toLong() else it as Long }
wrapped = listOf<Any?>(api.getMaximumWriteLength(centralHashCodeArgsArg))
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.sendReadCharacteristicReply", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val centralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val idArgsArg = args[2].let { if (it is Int) it.toLong() else it as Long }
val offsetArgsArg = args[3].let { if (it is Int) it.toLong() else it as Long } val offsetArgsArg = args[3].let { if (it is Int) it.toLong() else it as Long }
val statusArgsArg = args[4] as Boolean val valueArgsArg = args[4] as ByteArray?
val valueArgsArg = args[5] as ByteArray
var wrapped: List<Any?> var wrapped: List<Any?>
try { try {
api.sendReadCharacteristicReply(centralHashCodeArgsArg, characteristicHashCodeArgsArg, idArgsArg, offsetArgsArg, statusArgsArg, valueArgsArg) api.sendResponse(addressArgsArg, idArgsArg, statusNumberArgsArg, offsetArgsArg, valueArgsArg)
wrapped = listOf<Any?>(null) wrapped = listOf<Any?>(null)
} catch (exception: Throwable) { } catch (exception: Throwable) {
wrapped = wrapError(exception) wrapped = wrapError(exception)
@ -1014,37 +944,15 @@ interface MyPeripheralManagerHostApi {
} }
} }
run { run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.sendWriteCharacteristicReply", codec) val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.notifyCharacteristicChanged", codec)
if (api != null) { if (api != null) {
channel.setMessageHandler { message, reply -> channel.setMessageHandler { message, reply ->
val args = message as List<Any?> val args = message as List<Any?>
val centralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long } val hashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long } val valueArgsArg = args[1] as ByteArray
val idArgsArg = args[2].let { if (it is Int) it.toLong() else it as Long } val confirmArgsArg = args[2] as Boolean
val offsetArgsArg = args[3].let { if (it is Int) it.toLong() else it as Long } val addressArgsArg = args[3] as String
val statusArgsArg = args[4] as Boolean api.notifyCharacteristicChanged(hashCodeArgsArg, valueArgsArg, confirmArgsArg, addressArgsArg) { result: Result<Unit> ->
var wrapped: List<Any?>
try {
api.sendWriteCharacteristicReply(centralHashCodeArgsArg, characteristicHashCodeArgsArg, idArgsArg, offsetArgsArg, statusArgsArg)
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerHostApi.notifyCharacteristicValueChanged", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val centralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val valueArgsArg = args[2] as ByteArray
api.notifyCharacteristicValueChanged(centralHashCodeArgsArg, characteristicHashCodeArgsArg, valueArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull() val error = result.exceptionOrNull()
if (error != null) { if (error != null) {
reply.reply(wrapError(error)) reply.reply(wrapError(error))
@ -1069,16 +977,6 @@ private object MyPeripheralManagerFlutterApiCodec : StandardMessageCodec() {
MyCentralArgs.fromList(it) MyCentralArgs.fromList(it)
} }
} }
129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyGattCharacteristicArgs.fromList(it)
}
}
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyGattDescriptorArgs.fromList(it)
}
}
else -> super.readValueOfType(type, buffer) else -> super.readValueOfType(type, buffer)
} }
} }
@ -1088,14 +986,6 @@ private object MyPeripheralManagerFlutterApiCodec : StandardMessageCodec() {
stream.write(128) stream.write(128)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is MyGattCharacteristicArgs -> {
stream.write(129)
writeValue(stream, value.toList())
}
is MyGattDescriptorArgs -> {
stream.write(130)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value) else -> super.writeValue(stream, value)
} }
} }
@ -1111,58 +1001,107 @@ class MyPeripheralManagerFlutterApi(private val binaryMessenger: BinaryMessenger
} }
} }
fun onStateChanged(stateNumberArgsArg: Long, callback: (Result<Unit>) -> Unit) { fun onStateChanged(stateNumberArgsArg: Long, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onStateChanged", codec) val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onStateChanged"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(stateNumberArgsArg)) { channel.send(listOf(stateNumberArgsArg)) {
if (it is List<*>) { if (it is List<*>) {
if (it.size > 1) { if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else { } else {
callback(Result.success(Unit)); callback(Result.success(Unit))
} }
} else { } else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); callback(Result.failure(createConnectionError(channelName)))
} }
} }
} }
fun onReadCharacteristicCommandReceived(centralArgsArg: MyCentralArgs, characteristicArgsArg: MyGattCharacteristicArgs, idArgsArg: Long, offsetArgsArg: Long, callback: (Result<Unit>) -> Unit) { fun onConnectionStateChanged(centralArgsArg: MyCentralArgs, stateArgsArg: Boolean, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived", codec) val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onConnectionStateChanged"
channel.send(listOf(centralArgsArg, characteristicArgsArg, idArgsArg, offsetArgsArg)) { val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(centralArgsArg, stateArgsArg)) {
if (it is List<*>) { if (it is List<*>) {
if (it.size > 1) { if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else { } else {
callback(Result.success(Unit)); callback(Result.success(Unit))
} }
} else { } else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); callback(Result.failure(createConnectionError(channelName)))
} }
} }
} }
fun onWriteCharacteristicCommandReceived(centralArgsArg: MyCentralArgs, characteristicArgsArg: MyGattCharacteristicArgs, idArgsArg: Long, offsetArgsArg: Long, valueArgsArg: ByteArray, callback: (Result<Unit>) -> Unit) { fun onMtuChanged(addressArgsArg: String, mtuArgsArg: Long, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived", codec) val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onMtuChanged"
channel.send(listOf(centralArgsArg, characteristicArgsArg, idArgsArg, offsetArgsArg, valueArgsArg)) { val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, mtuArgsArg)) {
if (it is List<*>) { if (it is List<*>) {
if (it.size > 1) { if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else { } else {
callback(Result.success(Unit)); callback(Result.success(Unit))
} }
} else { } else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); callback(Result.failure(createConnectionError(channelName)))
} }
} }
} }
fun onNotifyCharacteristicCommandReceived(centralArgsArg: MyCentralArgs, characteristicArgsArg: MyGattCharacteristicArgs, stateArgsArg: Boolean, callback: (Result<Unit>) -> Unit) { fun onCharacteristicReadRequest(addressArgsArg: String, hashCodeArgsArg: Long, idArgsArg: Long, offsetArgsArg: Long, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived", codec) val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest"
channel.send(listOf(centralArgsArg, characteristicArgsArg, stateArgsArg)) { val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg)) {
if (it is List<*>) { if (it is List<*>) {
if (it.size > 1) { if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))); callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else { } else {
callback(Result.success(Unit)); callback(Result.success(Unit))
} }
} else { } else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", ""))); callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onCharacteristicWriteRequest(addressArgsArg: String, hashCodeArgsArg: Long, idArgsArg: Long, offsetArgsArg: Long, valueArgsArg: ByteArray, preparedWriteArgsArg: Boolean, responseNeededArgsArg: Boolean, callback: (Result<Unit>) -> Unit) {
val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg, valueArgsArg, preparedWriteArgsArg, responseNeededArgsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onExecuteWrite(addressArgsArg: String, idArgsArg: Long, executeArgsArg: Boolean, callback: (Result<Unit>) -> Unit) {
val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onExecuteWrite"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, idArgsArg, executeArgsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onCharacteristicNotifyStateChanged(addressArgsArg: String, hashCodeArgsArg: Long, stateNumberArgsArg: Long, callback: (Result<Unit>) -> Unit) {
val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, hashCodeArgsArg, stateNumberArgsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
} }
} }
} }

View File

@ -2,101 +2,45 @@ package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattService import android.bluetooth.BluetoothGattService
import android.bluetooth.le.AdvertiseData import android.bluetooth.le.AdvertiseData
import android.bluetooth.le.ScanRecord
import android.bluetooth.le.ScanResult import android.bluetooth.le.ScanResult
import android.os.ParcelUuid import android.os.ParcelUuid
import android.util.SparseArray import android.util.SparseArray
import java.util.UUID import java.util.UUID
val Any.TAG get() = this::class.java.simpleName as String //region ToObj
fun MyGattCharacteristicWriteTypeArgs.toType(): Int {
val BluetoothAdapter.stateArgs: MyBluetoothLowEnergyStateArgs
get() = state.toBluetoothLowEnergyStateArgs()
fun Int.toBluetoothLowEnergyStateArgs(): MyBluetoothLowEnergyStateArgs {
return when (this) { return when (this) {
BluetoothAdapter.STATE_ON -> MyBluetoothLowEnergyStateArgs.POWEREDON MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
else -> MyBluetoothLowEnergyStateArgs.POWEREDOFF MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
} }
} }
fun BluetoothDevice.toPeripheralArgs(): MyPeripheralArgs { fun MyGattCharacteristicNotifyStateArgs.toValue(): ByteArray {
val hashCodeArgs = hashCode().toLong() return when (this) {
val uuid = this.uuid.toString() MyGattCharacteristicNotifyStateArgs.NONE -> BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
return MyPeripheralArgs(hashCodeArgs, uuid) MyGattCharacteristicNotifyStateArgs.NOTIFY -> BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
} MyGattCharacteristicNotifyStateArgs.INDICATE -> BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
fun BluetoothDevice.toCentralArgs(): MyCentralArgs {
val hashCodeArgs = hashCode().toLong()
val uuid = this.uuid.toString()
return MyCentralArgs(hashCodeArgs, uuid)
}
val BluetoothDevice.uuid: UUID
get() {
val node = address.filter { char -> char != ':' }
// We don't know the timestamp of the bluetooth device, use nil UUID as prefix.
return UUID.fromString("00000000-0000-0000-0000-$node")
}
val ScanResult.advertisementArgs: MyAdvertisementArgs
get() {
val record = scanRecord
return if (record == null) {
val nameArgs = null
val serviceUUIDsArgs = emptyList<String?>()
val serviceDataArgs = emptyMap<String?, ByteArray>()
val manufacturerSpecificDataArgs = null
MyAdvertisementArgs(nameArgs, serviceUUIDsArgs, serviceDataArgs, manufacturerSpecificDataArgs)
} else {
val nameArgs = record.deviceName
val serviceUUIDsArgs = record.serviceUuids?.map { uuid -> uuid.toString() }
?: emptyList()
val pairs = record.serviceData.map { (uuid, value) ->
val key = uuid.toString()
return@map Pair(key, value)
}.toTypedArray()
val serviceDataArgs = mapOf<String?, ByteArray?>(*pairs)
val manufacturerSpecificDataArgs = record.manufacturerSpecificData.toManufacturerSpecificDataArgs()
MyAdvertisementArgs(nameArgs, serviceUUIDsArgs, serviceDataArgs, manufacturerSpecificDataArgs)
} }
} }
fun SparseArray<ByteArray>.toManufacturerSpecificDataArgs(): MyManufacturerSpecificDataArgs? { fun MyGattStatusArgs.toStatus(): Int {
var index = 0 return when (this) {
val size = size() MyGattStatusArgs.SUCCESS -> BluetoothGatt.GATT_SUCCESS
val itemsArgs = mutableListOf<MyManufacturerSpecificDataArgs>() MyGattStatusArgs.READNOTPERMITTED -> BluetoothGatt.GATT_READ_NOT_PERMITTED
while (index < size) { MyGattStatusArgs.WRITENOTPERMITTED -> BluetoothGatt.GATT_READ_NOT_PERMITTED
val idArgs = keyAt(index).toLong() MyGattStatusArgs.REQUESTNOTSUPPORTED -> BluetoothGatt.GATT_READ_NOT_PERMITTED
val dataArgs = valueAt(index) MyGattStatusArgs.INVALIDOFFSET -> BluetoothGatt.GATT_INVALID_OFFSET
val itemArgs = MyManufacturerSpecificDataArgs(idArgs, dataArgs) MyGattStatusArgs.INSUFFICIENTAUTHENTICATION -> BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION
itemsArgs.add(itemArgs) MyGattStatusArgs.INSUFFICIENTENCRYPTION -> BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION
index++ MyGattStatusArgs.INVALIDATTRIBUTELENGTH -> BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH
MyGattStatusArgs.CONNECTIONCONGESTED -> BluetoothGatt.GATT_CONNECTION_CONGESTED
MyGattStatusArgs.FAILURE -> BluetoothGatt.GATT_FAILURE
} }
return itemsArgs.lastOrNull()
}
val ScanRecord.rawValues: Map<Byte, ByteArray>
get() {
val rawValues = mutableMapOf<Byte, ByteArray>()
var index = 0
val size = bytes.size
while (index < size) {
val length = bytes[index++].toInt() and 0xff
if (length == 0) {
break
}
val end = index + length
val type = bytes[index++]
val value = bytes.slice(index until end).toByteArray()
rawValues[type] = value
index = end
}
return rawValues.toMap()
} }
fun MyAdvertisementArgs.toAdvertiseData(adapter: BluetoothAdapter): AdvertiseData { fun MyAdvertisementArgs.toAdvertiseData(adapter: BluetoothAdapter): AdvertiseData {
@ -104,6 +48,8 @@ fun MyAdvertisementArgs.toAdvertiseData(adapter: BluetoothAdapter): AdvertiseDat
if (nameArgs == null) { if (nameArgs == null) {
advertiseDataBuilder.setIncludeDeviceName(false) advertiseDataBuilder.setIncludeDeviceName(false)
} else { } else {
// TODO: There is an issue that Android will use the cached name before setName takes effect.
// see https://stackoverflow.com/questions/8377558/change-the-android-bluetooth-device-name
adapter.name = nameArgs adapter.name = nameArgs
advertiseDataBuilder.setIncludeDeviceName(true) advertiseDataBuilder.setIncludeDeviceName(true)
} }
@ -124,20 +70,138 @@ fun MyAdvertisementArgs.toAdvertiseData(adapter: BluetoothAdapter): AdvertiseDat
return advertiseDataBuilder.build() return advertiseDataBuilder.build()
} }
fun BluetoothGattService.toManufacturerSpecificDataArgs(characteristicsArgs: List<MyGattCharacteristicArgs>): MyGattServiceArgs { fun MyGattDescriptorArgs.toDescriptor(): BluetoothGattDescriptor {
val uuid = UUID.fromString(uuidArgs)
val permissions =
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
return BluetoothGattDescriptor(uuid, permissions)
}
fun MyGattCharacteristicArgs.toCharacteristic(): BluetoothGattCharacteristic {
val uuid = UUID.fromString(uuidArgs)
val properties = getProperties()
val permissions = getPermissions()
return BluetoothGattCharacteristic(uuid, properties, permissions)
}
fun MyGattCharacteristicArgs.getProperties(): Int {
val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args ->
val raw = args.toInt()
MyGattCharacteristicPropertyArgs.ofRaw(raw)
}
val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ)
val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE)
val writeWithoutResponse =
propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE)
val notify = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.NOTIFY)
val indicate = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.INDICATE)
var properties = 0
if (read) properties = properties or BluetoothGattCharacteristic.PROPERTY_READ
if (write) properties = properties or BluetoothGattCharacteristic.PROPERTY_WRITE
if (writeWithoutResponse) properties =
properties or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
if (notify) properties = properties or BluetoothGattCharacteristic.PROPERTY_NOTIFY
if (indicate) properties = properties or BluetoothGattCharacteristic.PROPERTY_INDICATE
return properties
}
fun MyGattCharacteristicArgs.getPermissions(): Int {
val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args ->
val raw = args.toInt()
MyGattCharacteristicPropertyArgs.ofRaw(raw)
}
val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ)
val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE)
val writeWithoutResponse =
propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE)
var permissions = 0
if (read) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_READ
if (write || writeWithoutResponse) permissions =
permissions or BluetoothGattCharacteristic.PERMISSION_WRITE
return permissions
}
fun MyGattServiceArgs.toService(): BluetoothGattService {
val uuid = UUID.fromString(uuidArgs)
val serviceType = BluetoothGattService.SERVICE_TYPE_PRIMARY
return BluetoothGattService(uuid, serviceType)
}
//endregion
//region ToArgs
fun Int.toBluetoothLowEnergyStateArgs(): MyBluetoothLowEnergyStateArgs {
return when (this) {
BluetoothAdapter.STATE_ON -> MyBluetoothLowEnergyStateArgs.POWEREDON
else -> MyBluetoothLowEnergyStateArgs.POWEREDOFF
}
}
fun SparseArray<ByteArray>.toManufacturerSpecificDataArgs(): MyManufacturerSpecificDataArgs? {
var index = 0
val size = size()
val itemsArgs = mutableListOf<MyManufacturerSpecificDataArgs>()
while (index < size) {
val idArgs = keyAt(index).toLong()
val dataArgs = valueAt(index)
val itemArgs = MyManufacturerSpecificDataArgs(idArgs, dataArgs)
itemsArgs.add(itemArgs)
index++
}
return itemsArgs.lastOrNull()
}
fun ScanResult.toAdvertisementArgs(): MyAdvertisementArgs {
val record = scanRecord
return if (record == null) {
val nameArgs = null
val serviceUUIDsArgs = emptyList<String?>()
val serviceDataArgs = emptyMap<String?, ByteArray>()
val manufacturerSpecificDataArgs = null
MyAdvertisementArgs(
nameArgs, serviceUUIDsArgs, serviceDataArgs, manufacturerSpecificDataArgs
)
} else {
val nameArgs = record.deviceName
val serviceUUIDsArgs = record.serviceUuids?.map { uuid -> uuid.toString() } ?: emptyList()
val pairs = record.serviceData.map { (uuid, value) ->
val key = uuid.toString()
return@map Pair(key, value)
}.toTypedArray()
val serviceDataArgs = mapOf<String?, ByteArray?>(*pairs)
val manufacturerSpecificDataArgs =
record.manufacturerSpecificData.toManufacturerSpecificDataArgs()
MyAdvertisementArgs(
nameArgs, serviceUUIDsArgs, serviceDataArgs, manufacturerSpecificDataArgs
)
}
}
fun BluetoothDevice.toCentralArgs(): MyCentralArgs {
val addressArgs = address
return MyCentralArgs(addressArgs)
}
fun BluetoothDevice.toPeripheralArgs(): MyPeripheralArgs {
val addressArgs = address
return MyPeripheralArgs(addressArgs)
}
fun BluetoothGattService.toArgs(): MyGattServiceArgs {
val hashCodeArgs = hashCode().toLong() val hashCodeArgs = hashCode().toLong()
val uuidArgs = this.uuid.toString() val uuidArgs = this.uuid.toString()
val characteristicsArgs = characteristics.map { it.toArgs() }
return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs) return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs)
} }
fun BluetoothGattCharacteristic.toManufacturerSpecificDataArgs(descriptorsArgs: List<MyGattDescriptorArgs>): MyGattCharacteristicArgs { fun BluetoothGattCharacteristic.toArgs(): MyGattCharacteristicArgs {
val hashCodeArgs = hashCode().toLong() val hashCodeArgs = hashCode().toLong()
val uuidArgs = this.uuid.toString() val uuidArgs = this.uuid.toString()
val propertyNumbersArgs = getPropertyNumbersArgs()
val descriptorsArgs = descriptors.map { it.toArgs() }
return MyGattCharacteristicArgs(hashCodeArgs, uuidArgs, propertyNumbersArgs, descriptorsArgs) return MyGattCharacteristicArgs(hashCodeArgs, uuidArgs, propertyNumbersArgs, descriptorsArgs)
} }
val BluetoothGattCharacteristic.propertyNumbersArgs: List<Long> fun BluetoothGattCharacteristic.getPropertyNumbersArgs(): List<Long> {
get() {
val numbersArgs = mutableListOf<Long>() val numbersArgs = mutableListOf<Long>()
if (properties and BluetoothGattCharacteristic.PROPERTY_READ != 0) { if (properties and BluetoothGattCharacteristic.PROPERTY_READ != 0) {
val number = MyGattCharacteristicPropertyArgs.READ.raw.toLong() val number = MyGattCharacteristicPropertyArgs.READ.raw.toLong()
@ -162,72 +226,42 @@ val BluetoothGattCharacteristic.propertyNumbersArgs: List<Long>
return numbersArgs return numbersArgs
} }
fun BluetoothGattDescriptor.toManufacturerSpecificDataArgs(): MyGattDescriptorArgs { fun BluetoothGattDescriptor.toArgs(): MyGattDescriptorArgs {
val hashCodeArgs = hashCode().toLong() val hashCodeArgs = hashCode().toLong()
val uuidArgs = this.uuid.toString() val uuidArgs = this.uuid.toString()
return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, null) return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, null)
} }
fun MyGattServiceArgs.toService(): BluetoothGattService { fun ByteArray.toNotifyStateArgs(): MyGattCharacteristicNotifyStateArgs {
val uuid = UUID.fromString(uuidArgs) return if (this contentEquals BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) {
val serviceType = BluetoothGattService.SERVICE_TYPE_PRIMARY MyGattCharacteristicNotifyStateArgs.NOTIFY
return BluetoothGattService(uuid, serviceType) } else if (this contentEquals BluetoothGattDescriptor.ENABLE_INDICATION_VALUE) {
MyGattCharacteristicNotifyStateArgs.INDICATE
} else if (this contentEquals BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE) {
MyGattCharacteristicNotifyStateArgs.NONE
} else {
throw IllegalArgumentException()
} }
}
//endregion
fun MyGattCharacteristicArgs.toCharacteristic(): BluetoothGattCharacteristic { val Any.TAG get() = this::class.java.simpleName as String
val uuid = UUID.fromString(uuidArgs)
return BluetoothGattCharacteristic(uuid, properties, permissions)
}
val MyGattCharacteristicArgs.properties: Int //val ScanRecord.rawValues: Map<Byte, ByteArray>
get() { // get() {
val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args -> // val rawValues = mutableMapOf<Byte, ByteArray>()
val raw = args.toInt() // var index = 0
MyGattCharacteristicPropertyArgs.ofRaw(raw) // val size = bytes.size
} // while (index < size) {
val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ) // val length = bytes[index++].toInt() and 0xff
val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE) // if (length == 0) {
val writeWithoutResponse = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE) // break
val notify = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.NOTIFY) // }
val indicate = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.INDICATE) // val end = index + length
var properties = 0 // val type = bytes[index++]
if (read) properties = properties or BluetoothGattCharacteristic.PROPERTY_READ // val value = bytes.slice(index until end).toByteArray()
if (write) properties = properties or BluetoothGattCharacteristic.PROPERTY_WRITE // rawValues[type] = value
if (writeWithoutResponse) properties = properties or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE // index = end
if (notify) properties = properties or BluetoothGattCharacteristic.PROPERTY_NOTIFY // }
if (indicate) properties = properties or BluetoothGattCharacteristic.PROPERTY_INDICATE // return rawValues.toMap()
return properties // }
}
val MyGattCharacteristicArgs.permissions: Int
get() {
val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args ->
val raw = args.toInt()
MyGattCharacteristicPropertyArgs.ofRaw(raw)
}
val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ)
val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE)
val writeWithoutResponse = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE)
var permissions = 0
if (read) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_READ
if (write || writeWithoutResponse) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_WRITE
return permissions
}
fun MyGattDescriptorArgs.toDescriptor(): BluetoothGattDescriptor {
val uuid = UUID.fromString(uuidArgs)
val permissions = BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
return BluetoothGattDescriptor(uuid, permissions)
}
fun Long.toWriteTypeArgs(): MyGattCharacteristicWriteTypeArgs {
val raw = toInt()
return MyGattCharacteristicWriteTypeArgs.ofRaw(raw) ?: throw IllegalArgumentException()
}
fun MyGattCharacteristicWriteTypeArgs.toType(): Int {
return when (this) {
MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
}
}

View File

@ -7,103 +7,131 @@ import android.bluetooth.BluetoothGattDescriptor
import android.os.Build import android.os.Build
import java.util.concurrent.Executor import java.util.concurrent.Executor
class MyBluetoothGattCallback(private val centralManager: MyCentralManager, private val executor: Executor) : BluetoothGattCallback() { class MyBluetoothGattCallback(manager: MyCentralManager, executor: Executor) :
BluetoothGattCallback() {
private val mManager: MyCentralManager
private val mExecutor: Executor
init {
mManager = manager
mExecutor = executor
}
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
super.onConnectionStateChange(gatt, status, newState) super.onConnectionStateChange(gatt, status, newState)
executor.execute { mExecutor.execute {
centralManager.onConnectionStateChange(gatt, status, newState) mManager.onConnectionStateChange(gatt, status, newState)
} }
} }
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
super.onMtuChanged(gatt, mtu, status) super.onMtuChanged(gatt, mtu, status)
executor.execute { mExecutor.execute {
centralManager.onMtuChanged(gatt, mtu, status) mManager.onMtuChanged(gatt, mtu, status)
} }
} }
override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) { override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
super.onReadRemoteRssi(gatt, rssi, status) super.onReadRemoteRssi(gatt, rssi, status)
executor.execute { mExecutor.execute {
centralManager.onReadRemoteRssi(gatt, rssi, status) mManager.onReadRemoteRssi(gatt, rssi, status)
} }
} }
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
super.onServicesDiscovered(gatt, status) super.onServicesDiscovered(gatt, status)
executor.execute { mExecutor.execute {
centralManager.onServicesDiscovered(gatt, status) mManager.onServicesDiscovered(gatt, status)
} }
} }
override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray, status: Int) { override fun onCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
status: Int
) {
super.onCharacteristicRead(gatt, characteristic, value, status) super.onCharacteristicRead(gatt, characteristic, value, status)
executor.execute { mExecutor.execute {
centralManager.onCharacteristicRead(characteristic, status, value) mManager.onCharacteristicRead(gatt, characteristic, status, value)
} }
} }
// TODO: remove this override when minSdkVersion >= 33 // TODO: remove this override when minSdkVersion >= 33
override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { override fun onCharacteristicRead(
gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int
) {
super.onCharacteristicRead(gatt, characteristic, status) super.onCharacteristicRead(gatt, characteristic, status)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return return
} }
val value = characteristic.value val value = characteristic.value
executor.execute { mExecutor.execute {
centralManager.onCharacteristicRead(characteristic, status, value) mManager.onCharacteristicRead(gatt, characteristic, status, value)
} }
} }
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { override fun onCharacteristicWrite(
gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int
) {
super.onCharacteristicWrite(gatt, characteristic, status) super.onCharacteristicWrite(gatt, characteristic, status)
executor.execute { mExecutor.execute {
centralManager.onCharacteristicWrite(characteristic, status) mManager.onCharacteristicWrite(gatt, characteristic, status)
} }
} }
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) { override fun onCharacteristicChanged(
gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray
) {
super.onCharacteristicChanged(gatt, characteristic, value) super.onCharacteristicChanged(gatt, characteristic, value)
executor.execute { mExecutor.execute {
centralManager.onCharacteristicChanged(characteristic, value) mManager.onCharacteristicChanged(gatt, characteristic, value)
} }
} }
// TODO: remove this override when minSdkVersion >= 33 // TODO: remove this override when minSdkVersion >= 33
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { override fun onCharacteristicChanged(
gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic
) {
super.onCharacteristicChanged(gatt, characteristic) super.onCharacteristicChanged(gatt, characteristic)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return return
} }
val value = characteristic.value val value = characteristic.value
executor.execute { mExecutor.execute {
centralManager.onCharacteristicChanged(characteristic, value) mManager.onCharacteristicChanged(gatt, characteristic, value)
} }
} }
override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) { override fun onDescriptorRead(
gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray
) {
super.onDescriptorRead(gatt, descriptor, status, value) super.onDescriptorRead(gatt, descriptor, status, value)
executor.execute { mExecutor.execute {
centralManager.onDescriptorRead(descriptor, status, value) mManager.onDescriptorRead(gatt, descriptor, status, value)
} }
} }
// TODO: remove this override when minSdkVersion >= 33 // TODO: remove this override when minSdkVersion >= 33
override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { override fun onDescriptorRead(
gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int
) {
super.onDescriptorRead(gatt, descriptor, status) super.onDescriptorRead(gatt, descriptor, status)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return return
} }
val value = descriptor.value val value = descriptor.value
executor.execute { mExecutor.execute {
centralManager.onDescriptorRead(descriptor, status, value) mManager.onDescriptorRead(gatt, descriptor, status, value)
} }
} }
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { override fun onDescriptorWrite(
gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int
) {
super.onDescriptorWrite(gatt, descriptor, status) super.onDescriptorWrite(gatt, descriptor, status)
executor.execute { mExecutor.execute {
centralManager.onDescriptorWrite(descriptor, status) mManager.onDescriptorWrite(gatt, descriptor, status)
} }
} }
} }

View File

@ -7,67 +7,107 @@ import android.bluetooth.BluetoothGattServerCallback
import android.bluetooth.BluetoothGattService import android.bluetooth.BluetoothGattService
import java.util.concurrent.Executor import java.util.concurrent.Executor
class MyBluetoothGattServerCallback(private val peripheralManager: MyPeripheralManager, private val executor: Executor) : BluetoothGattServerCallback() { class MyBluetoothGattServerCallback(manager: MyPeripheralManager, executor: Executor) :
BluetoothGattServerCallback() {
private val mManager: MyPeripheralManager
private val mExecutor: Executor
init {
mManager = manager
mExecutor = executor
}
override fun onServiceAdded(status: Int, service: BluetoothGattService) { override fun onServiceAdded(status: Int, service: BluetoothGattService) {
super.onServiceAdded(status, service) super.onServiceAdded(status, service)
executor.execute { mExecutor.execute {
peripheralManager.onServiceAdded(status, service) mManager.onServiceAdded(status, service)
} }
} }
override fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) { override fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) {
super.onConnectionStateChange(device, status, newState) super.onConnectionStateChange(device, status, newState)
executor.execute { mExecutor.execute {
peripheralManager.onConnectionStateChange(device, status, newState) mManager.onConnectionStateChange(device, status, newState)
} }
} }
override fun onMtuChanged(device: BluetoothDevice, mtu: Int) { override fun onMtuChanged(device: BluetoothDevice, mtu: Int) {
super.onMtuChanged(device, mtu) super.onMtuChanged(device, mtu)
executor.execute { mExecutor.execute {
peripheralManager.onMtuChanged(device, mtu) mManager.onMtuChanged(device, mtu)
} }
} }
override fun onCharacteristicReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, characteristic: BluetoothGattCharacteristic) { override fun onCharacteristicReadRequest(
device: BluetoothDevice,
requestId: Int,
offset: Int,
characteristic: BluetoothGattCharacteristic
) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic) super.onCharacteristicReadRequest(device, requestId, offset, characteristic)
executor.execute { mExecutor.execute {
peripheralManager.onCharacteristicReadRequest(device, requestId, offset, characteristic) mManager.onCharacteristicReadRequest(device, requestId, offset, characteristic)
} }
} }
override fun onCharacteristicWriteRequest(device: BluetoothDevice, requestId: Int, characteristic: BluetoothGattCharacteristic, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) { override fun onCharacteristicWriteRequest(
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value) device: BluetoothDevice,
executor.execute { requestId: Int,
peripheralManager.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value) characteristic: BluetoothGattCharacteristic,
preparedWrite: Boolean,
responseNeeded: Boolean,
offset: Int,
value: ByteArray
) {
super.onCharacteristicWriteRequest(
device, requestId, characteristic, preparedWrite, responseNeeded, offset, value
)
mExecutor.execute {
mManager.onCharacteristicWriteRequest(
device, requestId, characteristic, preparedWrite, responseNeeded, offset, value
)
} }
} }
override fun onExecuteWrite(device: BluetoothDevice, requestId: Int, execute: Boolean) { override fun onExecuteWrite(device: BluetoothDevice, requestId: Int, execute: Boolean) {
super.onExecuteWrite(device, requestId, execute) super.onExecuteWrite(device, requestId, execute)
executor.execute { mExecutor.execute {
peripheralManager.onExecuteWrite(device, requestId, execute) mManager.onExecuteWrite(device, requestId, execute)
} }
} }
override fun onNotificationSent(device: BluetoothDevice, status: Int) { override fun onNotificationSent(device: BluetoothDevice, status: Int) {
super.onNotificationSent(device, status) super.onNotificationSent(device, status)
executor.execute { mExecutor.execute {
peripheralManager.onNotificationSent(device, status) mManager.onNotificationSent(device, status)
} }
} }
override fun onDescriptorReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor) { override fun onDescriptorReadRequest(
device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor
) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor) super.onDescriptorReadRequest(device, requestId, offset, descriptor)
executor.execute { mExecutor.execute {
peripheralManager.onDescriptorReadRequest(device, requestId, offset, descriptor) mManager.onDescriptorReadRequest(device, requestId, offset, descriptor)
} }
} }
override fun onDescriptorWriteRequest(device: BluetoothDevice, requestId: Int, descriptor: BluetoothGattDescriptor, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) { override fun onDescriptorWriteRequest(
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value) device: BluetoothDevice,
executor.execute { requestId: Int,
peripheralManager.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value) descriptor: BluetoothGattDescriptor,
preparedWrite: Boolean,
responseNeeded: Boolean,
offset: Int,
value: ByteArray
) {
super.onDescriptorWriteRequest(
device, requestId, descriptor, preparedWrite, responseNeeded, offset, value
)
mExecutor.execute {
mManager.onDescriptorWriteRequest(
device, requestId, descriptor, preparedWrite, responseNeeded, offset, value
)
} }
} }
} }

View File

@ -2,62 +2,123 @@ package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager import android.bluetooth.BluetoothManager
import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.PackageManager import android.content.pm.PackageManager
import androidx.annotation.CallSuper
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener
import java.util.UUID import java.util.UUID
import java.util.concurrent.Executor import java.util.concurrent.Executor
abstract class MyBluetoothLowEnergyManager(private val context: Context) { typealias MyBluetoothLowEnergyState = MyBluetoothLowEnergyStateArgs
companion object {
const val DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xff.toByte()
const val REQUEST_CODE = 443
val HEART_RATE_MEASUREMENT_UUID: UUID = UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb") abstract class MyBluetoothLowEnergyManager(context: Context) {
val CLIENT_CHARACTERISTIC_CONFIG_UUID: UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb") companion object {
val CLIENT_CHARACTERISTIC_CONFIG_UUID =
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb") as UUID
} }
private lateinit var binding: ActivityPluginBinding private val mContext: Context
protected val unsupported = !context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) private val mRequestPermissionsResultListener: RequestPermissionsResultListener by lazy {
protected val executor = ContextCompat.getMainExecutor(context) as Executor MyRequestPermissionResultListener(this)
}
private val mBroadcastReceiver: BroadcastReceiver by lazy {
MyBroadcastReceiver(this)
}
protected val manager get() = ContextCompat.getSystemService(context, BluetoothManager::class.java) as BluetoothManager private var mRegistered: Boolean
private lateinit var mBinding: ActivityPluginBinding
init {
mContext = context
mRegistered = false
}
protected val executor get() = ContextCompat.getMainExecutor(mContext) as Executor
protected val manager
get() = ContextCompat.getSystemService(
mContext, BluetoothManager::class.java
) as BluetoothManager
protected val adapter get() = manager.adapter as BluetoothAdapter protected val adapter get() = manager.adapter as BluetoothAdapter
private val listener by lazy { MyRequestPermissionResultListener(this) } protected fun initialize() {
private val receiver by lazy { MyBroadcastReceiver(this) } val hasFeature =
mContext.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
if (hasFeature) {
val authorized = permissions.all { permission ->
ActivityCompat.checkSelfPermission(
mContext, permission
) == PackageManager.PERMISSION_GRANTED
}
if (authorized) {
mOnAuthorizationStateChanged(true)
} else {
val activity = mBinding.activity
ActivityCompat.requestPermissions(activity, permissions, requestCode)
}
} else {
val state = MyBluetoothLowEnergyState.UNSUPPORTED
onStateChanged(state)
}
}
fun onAttachedToActivity(binding: ActivityPluginBinding) { fun onAttachedToActivity(binding: ActivityPluginBinding) {
binding.addRequestPermissionsResultListener(listener) binding.addRequestPermissionsResultListener(mRequestPermissionsResultListener)
this.binding = binding mBinding = binding
} }
fun onDetachedFromActivity() { fun onDetachedFromActivity() {
binding.removeRequestPermissionsResultListener(listener) mBinding.removeRequestPermissionsResultListener(mRequestPermissionsResultListener)
} }
protected fun authorize(permissions: Array<String>) { fun onRequestPermissionsResult(
val activity = binding.activity requestCode: Int, permissions: Array<out String>, results: IntArray
ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE) ): Boolean {
if (this.requestCode != requestCode) {
return false
}
val authorized = results.all { r -> r == PackageManager.PERMISSION_GRANTED }
mOnAuthorizationStateChanged(authorized)
return true
} }
@CallSuper fun onReceive(context: Context, intent: Intent) {
protected open fun register() { val action = intent.action
if (action != BluetoothAdapter.ACTION_STATE_CHANGED) {
return
}
val extra = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
val state = extra.toBluetoothLowEnergyStateArgs()
onStateChanged(state)
}
private fun mOnAuthorizationStateChanged(authorized: Boolean) {
val state = if (authorized) {
mRegisterReceiver()
if (adapter.state == BluetoothAdapter.STATE_ON) MyBluetoothLowEnergyState.POWEREDON
else MyBluetoothLowEnergyState.POWEREDOFF
} else {
MyBluetoothLowEnergyState.UNAUTHORIZED
}
onStateChanged(state)
}
private fun mRegisterReceiver() {
if (mRegistered) {
return
}
val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED) val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
context.registerReceiver(receiver, filter) mContext.registerReceiver(mBroadcastReceiver, filter)
mRegistered = true
} }
@CallSuper abstract val permissions: Array<String>
protected open fun unregister() { abstract val requestCode: Int
context.unregisterReceiver(receiver) abstract fun onStateChanged(state: MyBluetoothLowEnergyState)
} }
abstract fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean
abstract fun onReceive(intent: Intent)
}

View File

@ -4,8 +4,14 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
class MyBroadcastReceiver(private val bluetoothLowEnergyManager: MyBluetoothLowEnergyManager) : BroadcastReceiver() { class MyBroadcastReceiver(manager: MyBluetoothLowEnergyManager) : BroadcastReceiver() {
private val mManager: MyBluetoothLowEnergyManager
init {
mManager = manager
}
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
bluetoothLowEnergyManager.onReceive(intent) mManager.onReceive(context, intent)
} }
} }

View File

@ -1,267 +1,224 @@
package dev.yanshouwang.bluetooth_low_energy_android package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothProfile import android.bluetooth.BluetoothProfile
import android.bluetooth.BluetoothStatusCodes import android.bluetooth.BluetoothStatusCodes
import android.bluetooth.le.BluetoothLeScanner
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanFilter import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings import android.bluetooth.le.ScanSettings
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.BinaryMessenger
class MyCentralManager(private val context: Context, binaryMessenger: BinaryMessenger) : MyBluetoothLowEnergyManager(context), MyCentralManagerHostApi { class MyCentralManager(context: Context, binaryMessenger: BinaryMessenger) :
private val scanner get() = adapter.bluetoothLeScanner MyBluetoothLowEnergyManager(context), MyCentralManagerHostApi {
companion object {
private val api = MyCentralManagerFlutterApi(binaryMessenger) const val REQUEST_CODE = 443
private val scanCallback = MyScanCallback(this)
private val bluetoothGattCallback = MyBluetoothGattCallback(this, executor)
private val devices = mutableMapOf<Long, BluetoothDevice>()
private val bluetoothGATTs = mutableMapOf<Long, BluetoothGatt>()
private val services = mutableMapOf<Long, BluetoothGattService>()
private val characteristics = mutableMapOf<Long, BluetoothGattCharacteristic>()
private val descriptors = mutableMapOf<Long, BluetoothGattDescriptor>()
private val mtus = mutableMapOf<Long, Int>()
private val peripheralsArgs = mutableMapOf<Int, MyPeripheralArgs>()
private val servicesArgsOfPeripherals = mutableMapOf<Long, List<MyGattServiceArgs>>()
private val servicesArgs = mutableMapOf<Int, MyGattServiceArgs>()
private val characteristicsArgs = mutableMapOf<Int, MyGattCharacteristicArgs>()
private val descriptorsArgs = mutableMapOf<Int, MyGattDescriptorArgs>()
private var registered = false
private var discovering = false
private var setUpCallback: ((Result<MyCentralManagerArgs>) -> Unit)? = null
private var startDiscoveryCallback: ((Result<Unit>) -> Unit)? = null
private val connectCallbacks = mutableMapOf<Long, (Result<Unit>) -> Unit>()
private val disconnectCallbacks = mutableMapOf<Long, (Result<Unit>) -> Unit>()
private val requestMtuCallbacks = mutableMapOf<Long, (Result<Long>) -> Unit>()
private val readRssiCallbacks = mutableMapOf<Long, (Result<Long>) -> Unit>()
private val discoverGattCallbacks = mutableMapOf<Long, (Result<List<MyGattServiceArgs>>) -> Unit>()
private val readCharacteristicCallbacks = mutableMapOf<Long, (Result<ByteArray>) -> Unit>()
private val writeCharacteristicCallbacks = mutableMapOf<Long, (Result<Unit>) -> Unit>()
private val readDescriptorCallbacks = mutableMapOf<Long, (Result<ByteArray>) -> Unit>()
private val writeDescriptorCallbacks = mutableMapOf<Long, (Result<Unit>) -> Unit>()
override fun setUp(callback: (Result<MyCentralManagerArgs>) -> Unit) {
try {
if (setUpCallback != null) {
throw IllegalStateException()
} }
tearDown()
if (unsupported) { private val mContext: Context
val stateNumberArgs = MyBluetoothLowEnergyStateArgs.UNSUPPORTED.raw.toLong() private val mApi: MyCentralManagerFlutterApi
val args = MyCentralManagerArgs(stateNumberArgs)
callback(Result.success(args)) private val mScanCallback: ScanCallback by lazy {
MyScanCallback(this)
} }
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { private val mBluetoothGattCallback: BluetoothGattCallback by lazy {
arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_CONNECT) MyBluetoothGattCallback(this, executor)
}
private var mDiscovering: Boolean
private val mDevices: MutableMap<String, BluetoothDevice>
private val mGATTs: MutableMap<String, BluetoothGatt>
private val mCharacteristics: MutableMap<String, Map<Long, BluetoothGattCharacteristic>>
private val mDescriptors: MutableMap<String, Map<Long, BluetoothGattDescriptor>>
private var mStartDiscoveryCallback: ((Result<Unit>) -> Unit)?
private val mConnectCallbacks: MutableMap<String, (Result<Unit>) -> Unit>
private val mDisconnectCallbacks: MutableMap<String, (Result<Unit>) -> Unit>
private val mRequestMtuCallbacks: MutableMap<String, (Result<Long>) -> Unit>
private val mReadRssiCallbacks: MutableMap<String, (Result<Long>) -> Unit>
private val mDiscoverServicesCallbacks: MutableMap<String, (Result<List<MyGattServiceArgs>>) -> Unit>
private val mReadCharacteristicCallbacks: MutableMap<String, MutableMap<Long, (Result<ByteArray>) -> Unit>>
private val mWriteCharacteristicCallbacks: MutableMap<String, MutableMap<Long, (Result<Unit>) -> Unit>>
private val mReadDescriptorCallbacks: MutableMap<String, MutableMap<Long, (Result<ByteArray>) -> Unit>>
private val mWriteDescriptorCallbacks: MutableMap<String, MutableMap<Long, (Result<Unit>) -> Unit>>
init {
mContext = context
mApi = MyCentralManagerFlutterApi(binaryMessenger)
mDiscovering = false
mDevices = mutableMapOf()
mGATTs = mutableMapOf()
mCharacteristics = mutableMapOf()
mDescriptors = mutableMapOf()
mStartDiscoveryCallback = null
mConnectCallbacks = mutableMapOf()
mDisconnectCallbacks = mutableMapOf()
mRequestMtuCallbacks = mutableMapOf()
mReadRssiCallbacks = mutableMapOf()
mDiscoverServicesCallbacks = mutableMapOf()
mReadCharacteristicCallbacks = mutableMapOf()
mWriteCharacteristicCallbacks = mutableMapOf()
mReadDescriptorCallbacks = mutableMapOf()
mWriteDescriptorCallbacks = mutableMapOf()
}
private val mScanner: BluetoothLeScanner get() = adapter.bluetoothLeScanner
override val permissions: Array<String>
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(
android.Manifest.permission.ACCESS_COARSE_LOCATION,
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.BLUETOOTH_SCAN,
android.Manifest.permission.BLUETOOTH_CONNECT
)
} else { } else {
arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION) arrayOf(
} android.Manifest.permission.ACCESS_COARSE_LOCATION,
authorize(permissions) android.Manifest.permission.ACCESS_FINE_LOCATION
setUpCallback = callback )
} catch (e: Throwable) {
callback(Result.failure(e))
}
} }
private fun tearDown() { override val requestCode: Int
if (registered) { get() = REQUEST_CODE
unregister()
}
if (discovering) {
stopDiscovery()
}
for (gatt in bluetoothGATTs.values) {
gatt.disconnect()
}
devices.clear()
bluetoothGATTs.clear()
services.clear()
characteristics.clear()
descriptors.clear()
mtus.clear()
peripheralsArgs.clear()
servicesArgsOfPeripherals.clear()
servicesArgs.clear()
characteristicsArgs.clear()
descriptorsArgs.clear()
setUpCallback = null
startDiscoveryCallback = null
connectCallbacks.clear()
disconnectCallbacks.clear()
requestMtuCallbacks.clear()
readRssiCallbacks.clear()
discoverGattCallbacks.clear()
readCharacteristicCallbacks.clear()
writeCharacteristicCallbacks.clear()
readDescriptorCallbacks.clear()
writeDescriptorCallbacks.clear()
}
override fun register() { override fun setUp() {
super.register() mClearState()
registered = true initialize()
}
override fun unregister() {
super.unregister()
registered = false
} }
override fun startDiscovery(callback: (Result<Unit>) -> Unit) { override fun startDiscovery(callback: (Result<Unit>) -> Unit) {
try { try {
if (startDiscoveryCallback != null) {
throw IllegalStateException()
}
val filters = emptyList<ScanFilter>() val filters = emptyList<ScanFilter>()
val settings = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build() val settings =
scanner.startScan(filters, settings, scanCallback) ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build()
executor.execute { onScanSucceed() } mScanner.startScan(filters, settings, mScanCallback)
startDiscoveryCallback = callback mStartDiscoveryCallback = callback
executor.execute {
onScanSucceed()
}
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun stopDiscovery() { override fun stopDiscovery() {
scanner.stopScan(scanCallback) mScanner.stopScan(mScanCallback)
discovering = false mDiscovering = false
} }
override fun connect(peripheralHashCodeArgs: Long, callback: (Result<Unit>) -> Unit) { override fun connect(addressArgs: String, callback: (Result<Unit>) -> Unit) {
try { try {
val unfinishedCallback = connectCallbacks[peripheralHashCodeArgs] val device = mDevices[addressArgs] as BluetoothDevice
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val device = devices[peripheralHashCodeArgs] as BluetoothDevice
val autoConnect = false val autoConnect = false
// Add to bluetoothGATTs cache. // Add to bluetoothGATTs cache.
bluetoothGATTs[peripheralHashCodeArgs] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { mGATTs[addressArgs] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val transport = BluetoothDevice.TRANSPORT_LE val transport = BluetoothDevice.TRANSPORT_LE
device.connectGatt(context, autoConnect, bluetoothGattCallback, transport) device.connectGatt(mContext, autoConnect, mBluetoothGattCallback, transport)
} else { } else {
device.connectGatt(context, autoConnect, bluetoothGattCallback) device.connectGatt(mContext, autoConnect, mBluetoothGattCallback)
} }
connectCallbacks[peripheralHashCodeArgs] = callback mConnectCallbacks[addressArgs] = callback
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun disconnect(peripheralHashCodeArgs: Long, callback: (Result<Unit>) -> Unit) { override fun disconnect(addressArgs: String, callback: (Result<Unit>) -> Unit) {
try { try {
val unfinishedCallback = disconnectCallbacks[peripheralHashCodeArgs] val gatt = mGATTs[addressArgs] as BluetoothGatt
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
gatt.disconnect() gatt.disconnect()
disconnectCallbacks[peripheralHashCodeArgs] = callback mDisconnectCallbacks[addressArgs] = callback
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun requestMTU(peripheralHashCodeArgs: Long, mtuArgs: Long, callback: (Result<Long>) -> Unit) { override fun requestMTU(addressArgs: String, mtuArgs: Long, callback: (Result<Long>) -> Unit) {
try { try {
val unfinishedCallback = requestMtuCallbacks[peripheralHashCodeArgs] val gatt = mGATTs[addressArgs] as BluetoothGatt
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val mtu = mtuArgs.toInt() val mtu = mtuArgs.toInt()
val requesting = gatt.requestMtu(mtu) val requesting = gatt.requestMtu(mtu)
if (!requesting) { if (!requesting) {
throw IllegalStateException() throw IllegalStateException()
} }
requestMtuCallbacks[peripheralHashCodeArgs] = callback mRequestMtuCallbacks[addressArgs] = callback
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun getMaximumWriteLength(peripheralHashCodeArgs: Long, typeNumberArgs: Long): Long { override fun readRSSI(addressArgs: String, callback: (Result<Long>) -> Unit) {
val mtu = mtus[peripheralHashCodeArgs] ?: 23
val maximumWriteLength = when (typeNumberArgs.toWriteTypeArgs()) {
MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> 512
MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> (mtu - 3).coerceIn(20, 512)
}
return maximumWriteLength.toLong()
}
override fun readRSSI(peripheralHashCodeArgs: Long, callback: (Result<Long>) -> Unit) {
try { try {
val unfinishedCallback = readRssiCallbacks[peripheralHashCodeArgs] val gatt = mGATTs[addressArgs] as BluetoothGatt
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val reading = gatt.readRemoteRssi() val reading = gatt.readRemoteRssi()
if (!reading) { if (!reading) {
throw IllegalStateException() throw IllegalStateException()
} }
readRssiCallbacks[peripheralHashCodeArgs] = callback mReadRssiCallbacks[addressArgs] = callback
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun discoverGATT(peripheralHashCodeArgs: Long, callback: (Result<List<MyGattServiceArgs>>) -> Unit) { override fun discoverServices(
addressArgs: String, callback: (Result<List<MyGattServiceArgs>>) -> Unit
) {
try { try {
val unfinishedCallback = discoverGattCallbacks[peripheralHashCodeArgs] val gatt = mGATTs[addressArgs] as BluetoothGatt
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val discovering = gatt.discoverServices() val discovering = gatt.discoverServices()
if (!discovering) { if (!discovering) {
throw IllegalStateException() throw IllegalStateException()
} }
discoverGattCallbacks[peripheralHashCodeArgs] = callback mDiscoverServicesCallbacks[addressArgs] = callback
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun readCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit) { override fun readCharacteristic(
addressArgs: String, hashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit
) {
try { try {
val unfinishedCallback = readCharacteristicCallbacks[characteristicHashCodeArgs] val gatt = mGATTs[addressArgs] as BluetoothGatt
if (unfinishedCallback != null) { val characteristic =
throw IllegalStateException() mRetrieveCharacteristic(addressArgs, hashCodeArgs) as BluetoothGattCharacteristic
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic
val reading = gatt.readCharacteristic(characteristic) val reading = gatt.readCharacteristic(characteristic)
if (!reading) { if (!reading) {
throw IllegalStateException() throw IllegalStateException()
} }
readCharacteristicCallbacks[characteristicHashCodeArgs] = callback val callbacks = mReadCharacteristicCallbacks.getOrPut(addressArgs) { mutableMapOf() }
callbacks[hashCodeArgs] = callback
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun writeCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, typeNumberArgs: Long, callback: (Result<Unit>) -> Unit) { override fun writeCharacteristic(
addressArgs: String,
hashCodeArgs: Long,
valueArgs: ByteArray,
typeNumberArgs: Long,
callback: (Result<Unit>) -> Unit
) {
try { try {
val unfinishedCallback = writeCharacteristicCallbacks[characteristicHashCodeArgs] val gatt = mGATTs[addressArgs] as BluetoothGatt
if (unfinishedCallback != null) { val characteristic =
throw IllegalStateException() mRetrieveCharacteristic(addressArgs, hashCodeArgs) as BluetoothGattCharacteristic
} val typeRawArgs = typeNumberArgs.toInt()
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt val typeArgs = MyGattCharacteristicWriteTypeArgs.ofRaw(typeRawArgs)
val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic ?: throw IllegalArgumentException()
val typeArgs = typeNumberArgs.toWriteTypeArgs()
val type = typeArgs.toType() val type = typeArgs.toType()
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val code = gatt.writeCharacteristic(characteristic, valueArgs, type) val code = gatt.writeCharacteristic(characteristic, valueArgs, type)
@ -275,17 +232,28 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
if (!writing) { if (!writing) {
throw IllegalStateException() throw IllegalStateException()
} }
writeCharacteristicCallbacks[characteristicHashCodeArgs] = callback val callbacks = mWriteCharacteristicCallbacks.getOrPut(addressArgs) { mutableMapOf() }
callbacks[hashCodeArgs] = callback
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun notifyCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, stateArgs: Boolean, callback: (Result<Unit>) -> Unit) { override fun setCharacteristicNotifyState(
addressArgs: String,
hashCodeArgs: Long,
stateNumberArgs: Long,
callback: (Result<Unit>) -> Unit
) {
try { try {
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt val gatt = mGATTs[addressArgs] as BluetoothGatt
val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic val characteristic =
val notifying = gatt.setCharacteristicNotification(characteristic, stateArgs) mRetrieveCharacteristic(addressArgs, hashCodeArgs) as BluetoothGattCharacteristic
val stateRawArgs = stateNumberArgs.toInt()
val stateArgs = MyGattCharacteristicNotifyStateArgs.ofRaw(stateRawArgs)
?: throw IllegalArgumentException()
val enable = stateArgs != MyGattCharacteristicNotifyStateArgs.NONE
val notifying = gatt.setCharacteristicNotification(characteristic, enable)
if (!notifying) { if (!notifying) {
throw IllegalStateException() throw IllegalStateException()
} }
@ -293,28 +261,10 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
// https://developer.android.com/guide/topics/connectivity/bluetooth/transfer-ble-data#notification // https://developer.android.com/guide/topics/connectivity/bluetooth/transfer-ble-data#notification
// This is specific to Heart Rate Measurement. // This is specific to Heart Rate Measurement.
//if (characteristic.uuid == UUID_HEART_RATE_MEASUREMENT) { //if (characteristic.uuid == UUID_HEART_RATE_MEASUREMENT) {
val descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID) val cccDescriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID)
val descriptorHashCode = descriptor.hashCode() val cccHashCodeArgs = cccDescriptor.hashCode().toLong()
val descriptorArgs = descriptorsArgs[descriptorHashCode] as MyGattDescriptorArgs val valueArgs = stateArgs.toValue()
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs writeDescriptor(addressArgs, cccHashCodeArgs, valueArgs, callback)
val unfinishedCallback = writeDescriptorCallbacks[descriptorHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val value = if (stateArgs) BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
else BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val code = gatt.writeDescriptor(descriptor, value)
code == BluetoothStatusCodes.SUCCESS
} else {
// TODO: remove this when minSdkVersion >= 33
descriptor.value = value
gatt.writeDescriptor(descriptor)
}
if (!writing) {
throw IllegalStateException()
}
writeDescriptorCallbacks[descriptorHashCodeArgs] = callback
//} else { //} else {
// callback(Result.success(Unit)) // callback(Result.success(Unit))
//} //}
@ -323,32 +273,34 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
} }
} }
override fun readDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit) { override fun readDescriptor(
addressArgs: String, hashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit
) {
try { try {
val unfinishedCallback = readDescriptorCallbacks[descriptorHashCodeArgs] val gatt = mGATTs[addressArgs] as BluetoothGatt
if (unfinishedCallback != null) { val descriptor =
throw IllegalStateException() mRetrieveDescriptor(addressArgs, hashCodeArgs) as BluetoothGattDescriptor
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val descriptor = descriptors[descriptorHashCodeArgs] as BluetoothGattDescriptor
val reading = gatt.readDescriptor(descriptor) val reading = gatt.readDescriptor(descriptor)
if (!reading) { if (!reading) {
throw IllegalStateException() throw IllegalStateException()
} }
readDescriptorCallbacks[descriptorHashCodeArgs] = callback val callbacks = mReadDescriptorCallbacks.getOrPut(addressArgs) { mutableMapOf() }
callbacks[hashCodeArgs] = callback
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun writeDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result<Unit>) -> Unit) { override fun writeDescriptor(
addressArgs: String,
hashCodeArgs: Long,
valueArgs: ByteArray,
callback: (Result<Unit>) -> Unit
) {
try { try {
val unfinishedCallback = writeDescriptorCallbacks[descriptorHashCodeArgs] val gatt = mGATTs[addressArgs] as BluetoothGatt
if (unfinishedCallback != null) { val descriptor =
throw IllegalStateException() mRetrieveDescriptor(addressArgs, hashCodeArgs) as BluetoothGattDescriptor
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val descriptor = descriptors[descriptorHashCodeArgs] as BluetoothGattDescriptor
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val code = gatt.writeDescriptor(descriptor, valueArgs) val code = gatt.writeDescriptor(descriptor, valueArgs)
code == BluetoothStatusCodes.SUCCESS code == BluetoothStatusCodes.SUCCESS
@ -360,51 +312,28 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
if (!writing) { if (!writing) {
throw IllegalStateException() throw IllegalStateException()
} }
writeDescriptorCallbacks[descriptorHashCodeArgs] = callback val callbacks = mWriteDescriptorCallbacks.getOrPut(addressArgs) { mutableMapOf() }
callbacks[hashCodeArgs] = callback
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean { override fun onStateChanged(state: MyBluetoothLowEnergyState) {
if (requestCode != REQUEST_CODE) { val stateNumberArgs = state.raw.toLong()
return false mApi.onStateChanged(stateNumberArgs) {}
}
val authorized = results.all { r -> r == PackageManager.PERMISSION_GRANTED }
val callback = setUpCallback ?: return false
setUpCallback = null
val stateArgs = if (authorized) adapter.stateArgs
else MyBluetoothLowEnergyStateArgs.UNAUTHORIZED
val stateNumberArgs = stateArgs.raw.toLong()
val args = MyCentralManagerArgs(stateNumberArgs)
callback(Result.success(args))
if (authorized) {
register()
}
return true
}
override fun onReceive(intent: Intent) {
val action = intent.action
if (action != BluetoothAdapter.ACTION_STATE_CHANGED) {
return
}
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
val stateArgs = state.toBluetoothLowEnergyStateArgs()
val stateNumberArgs = stateArgs.raw.toLong()
api.onStateChanged(stateNumberArgs) {}
} }
private fun onScanSucceed() { private fun onScanSucceed() {
discovering = true mDiscovering = true
val callback = startDiscoveryCallback ?: return val callback = mStartDiscoveryCallback ?: return
startDiscoveryCallback = null mStartDiscoveryCallback = null
callback(Result.success(Unit)) callback(Result.success(Unit))
} }
fun onScanFailed(errorCode: Int) { fun onScanFailed(errorCode: Int) {
val callback = startDiscoveryCallback ?: return val callback = mStartDiscoveryCallback ?: return
startDiscoveryCallback = null mStartDiscoveryCallback = null
val error = IllegalStateException("Start discovery failed with error code: $errorCode") val error = IllegalStateException("Start discovery failed with error code: $errorCode")
callback(Result.failure(error)) callback(Result.failure(error))
} }
@ -412,82 +341,68 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
fun onScanResult(result: ScanResult) { fun onScanResult(result: ScanResult) {
val device = result.device val device = result.device
val peripheralArgs = device.toPeripheralArgs() val peripheralArgs = device.toPeripheralArgs()
val hashCode = device.hashCode() val addressArgs = peripheralArgs.addressArgs
val hashCodeArgs = peripheralArgs.hashCodeArgs
this.devices[hashCodeArgs] = device
this.peripheralsArgs[hashCode] = peripheralArgs
val rssiArgs = result.rssi.toLong() val rssiArgs = result.rssi.toLong()
val advertisementArgs = result.advertisementArgs val advertisementArgs = result.toAdvertisementArgs()
api.onDiscovered(peripheralArgs, rssiArgs, advertisementArgs) {} mDevices[addressArgs] = device
mApi.onDiscovered(peripheralArgs, rssiArgs, advertisementArgs) {}
} }
fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
val device = gatt.device val device = gatt.device
val deviceHashCode = device.hashCode() val addressArgs = device.address
val peripheralArgs = peripheralsArgs[deviceHashCode] as MyPeripheralArgs
val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
// check connection state. // check connection state.
if (newState == BluetoothProfile.STATE_DISCONNECTED) { if (newState == BluetoothProfile.STATE_DISCONNECTED) {
gatt.close() gatt.close()
bluetoothGATTs.remove(peripheralHashCodeArgs) mGATTs.remove(addressArgs)
mtus.remove(peripheralHashCodeArgs) mCharacteristics.remove(addressArgs)
mDescriptors.remove(addressArgs)
val error = IllegalStateException("GATT is disconnected with status: $status") val error = IllegalStateException("GATT is disconnected with status: $status")
val requestMtuCallback = requestMtuCallbacks.remove(peripheralHashCodeArgs) val requestMtuCallback = mRequestMtuCallbacks.remove(addressArgs)
if (requestMtuCallback != null) { if (requestMtuCallback != null) {
requestMtuCallback(Result.failure(error)) requestMtuCallback(Result.failure(error))
} }
val readRssiCallback = readRssiCallbacks.remove(peripheralHashCodeArgs) val readRssiCallback = mReadRssiCallbacks.remove(addressArgs)
if (readRssiCallback != null) { if (readRssiCallback != null) {
readRssiCallback(Result.failure(error)) readRssiCallback(Result.failure(error))
} }
val discoverGattCallback = discoverGattCallbacks.remove(peripheralHashCodeArgs) val discoverServicesCallback = mDiscoverServicesCallbacks.remove(addressArgs)
if (discoverGattCallback != null) { if (discoverServicesCallback != null) {
discoverGattCallback(Result.failure(error)) discoverServicesCallback(Result.failure(error))
} }
val servicesArgs = servicesArgsOfPeripherals.remove(peripheralHashCodeArgs) val readCharacteristicCallbacks = mReadCharacteristicCallbacks.remove(addressArgs)
?: emptyList() if (readCharacteristicCallbacks != null) {
for (serviceArgs in servicesArgs) { val callbacks = readCharacteristicCallbacks.values
val serviceHashCodeArgs = serviceArgs.hashCodeArgs for (callback in callbacks) {
val service = services.remove(serviceHashCodeArgs) as BluetoothGattService callback(Result.failure(error))
val serviceHashCode = service.hashCode()
this.servicesArgs.remove(serviceHashCode)
val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull()
for (characteristicArgs in characteristicsArgs) {
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
val characteristic = characteristics.remove(characteristicHashCodeArgs) as BluetoothGattCharacteristic
val characteristicHashCode = characteristic.hashCode()
this.characteristicsArgs.remove(characteristicHashCode)
val readCharacteristicCallback = readCharacteristicCallbacks.remove(characteristicHashCodeArgs)
val writeCharacteristicCallback = writeCharacteristicCallbacks.remove(characteristicHashCodeArgs)
if (readCharacteristicCallback != null) {
readCharacteristicCallback(Result.failure(error))
}
if (writeCharacteristicCallback != null) {
writeCharacteristicCallback(Result.failure(error))
}
val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull()
for (descriptorArgs in descriptorsArgs) {
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
val descriptor = descriptors.remove(descriptorHashCodeArgs) as BluetoothGattDescriptor
val descriptorHashCode = descriptor.hashCode()
this.descriptorsArgs.remove(descriptorHashCode)
val readDescriptorCallback = readDescriptorCallbacks.remove(descriptorHashCodeArgs)
val writeDescriptorCallback = writeDescriptorCallbacks.remove(descriptorHashCodeArgs)
if (readDescriptorCallback != null) {
readDescriptorCallback(Result.failure(error))
}
if (writeDescriptorCallback != null) {
writeDescriptorCallback(Result.failure(error))
} }
} }
val writeCharacteristicCallbacks = mWriteCharacteristicCallbacks.remove(addressArgs)
if (writeCharacteristicCallbacks != null) {
val callbacks = writeCharacteristicCallbacks.values
for (callback in callbacks) {
callback(Result.failure(error))
}
}
val readDescriptorCallbacks = mReadDescriptorCallbacks.remove(addressArgs)
if (readDescriptorCallbacks != null) {
val callbacks = readDescriptorCallbacks.values
for (callback in callbacks) {
callback(Result.failure(error))
}
}
val writeDescriptorCallbacks = mWriteDescriptorCallbacks.remove(addressArgs)
if (writeDescriptorCallbacks != null) {
val callbacks = writeDescriptorCallbacks.values
for (callback in callbacks) {
callback(Result.failure(error))
} }
} }
} }
val stateArgs = newState == BluetoothProfile.STATE_CONNECTED val stateArgs = newState == BluetoothProfile.STATE_CONNECTED
api.onPeripheralStateChanged(peripheralArgs, stateArgs) {} mApi.onConnectionStateChanged(addressArgs, stateArgs) {}
// check connect & disconnect callbacks. // check connect & disconnect callbacks.
val connectCallback = connectCallbacks.remove(peripheralHashCodeArgs) val connectCallback = mConnectCallbacks.remove(addressArgs)
val disconnectCallback = disconnectCallbacks.remove(peripheralHashCodeArgs)
if (connectCallback != null) { if (connectCallback != null) {
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
connectCallback(Result.success(Unit)) connectCallback(Result.success(Unit))
@ -496,6 +411,7 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
connectCallback(Result.failure(error)) connectCallback(Result.failure(error))
} }
} }
val disconnectCallback = mDisconnectCallbacks.remove(addressArgs)
if (disconnectCallback != null) { if (disconnectCallback != null) {
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
disconnectCallback(Result.success(Unit)) disconnectCallback(Result.success(Unit))
@ -508,75 +424,43 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
val device = gatt.device val device = gatt.device
val hashCode = device.hashCode() val addressArgs = device.address
val peripheralArgs = peripheralsArgs[hashCode] as MyPeripheralArgs val result = if (status == BluetoothGatt.GATT_SUCCESS) {
val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
if (status == BluetoothGatt.GATT_SUCCESS) {
mtus[peripheralHashCodeArgs] = mtu
}
val callback = requestMtuCallbacks.remove(peripheralHashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
val mtuArgs = mtu.toLong() val mtuArgs = mtu.toLong()
callback(Result.success(mtuArgs)) mApi.onMtuChanged(addressArgs, mtuArgs) {}
Result.success(mtuArgs)
} else { } else {
val error = IllegalStateException("Request MTU failed with status: $status") val error = IllegalStateException("Read RSSI failed with status: $status")
callback(Result.failure(error)) Result.failure(error)
} }
val callback = mRequestMtuCallbacks.remove(addressArgs) ?: return
callback(result)
} }
fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) { fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
val device = gatt.device val device = gatt.device
val hashCode = device.hashCode() val addressArgs = device.address
val peripheralArgs = peripheralsArgs[hashCode] as MyPeripheralArgs val callback = mReadRssiCallbacks.remove(addressArgs) ?: return
val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
val callback = readRssiCallbacks.remove(peripheralHashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
val rssiArgs = rssi.toLong() val rssiArgs = rssi.toLong()
callback(Result.success(rssiArgs)) callback(Result.success(rssiArgs))
} else { } else {
val error = IllegalStateException("Read rssi failed with status: $status") val error = IllegalStateException("Read RSSI failed with status: $status")
callback(Result.failure(error)) callback(Result.failure(error))
} }
} }
fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
val device = gatt.device val device = gatt.device
val deviceHashCode = device.hashCode() val addressArgs = device.address
val peripheralArgs = peripheralsArgs[deviceHashCode] as MyPeripheralArgs val callback = mDiscoverServicesCallbacks.remove(addressArgs) ?: return
val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
val callback = discoverGattCallbacks.remove(peripheralHashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
val services = gatt.services val services = gatt.services
val servicesArgs = mutableListOf<MyGattServiceArgs>() val characteristics = services.flatMap { it.characteristics }
for (service in services) { val descriptors = characteristics.flatMap { it.descriptors }
val characteristics = service.characteristics mCharacteristics[addressArgs] = characteristics.associateBy { it.hashCode().toLong() }
val characteristicsArgs = mutableListOf<MyGattCharacteristicArgs>() mDescriptors[addressArgs] = descriptors.associateBy { it.hashCode().toLong() }
for (characteristic in characteristics) { val servicesArgs = services.map { it.toArgs() }
val descriptors = characteristic.descriptors
val descriptorsArgs = mutableListOf<MyGattDescriptorArgs>()
for (descriptor in descriptors) {
val descriptorArgs = descriptor.toManufacturerSpecificDataArgs()
val descriptorHashCode = descriptor.hashCode()
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
this.descriptors[descriptorHashCodeArgs] = descriptor
this.descriptorsArgs[descriptorHashCode] = descriptorArgs
descriptorsArgs.add(descriptorArgs)
}
val characteristicArgs = characteristic.toManufacturerSpecificDataArgs(descriptorsArgs)
val characteristicHashCode = characteristic.hashCode()
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
this.characteristics[characteristicHashCodeArgs] = characteristic
this.characteristicsArgs[characteristicHashCode] = characteristicArgs
characteristicsArgs.add(characteristicArgs)
}
val serviceArgs = service.toManufacturerSpecificDataArgs(characteristicsArgs)
val serviceHashCode = service.hashCode()
val serviceHashCodeArgs = serviceArgs.hashCodeArgs
this.services[serviceHashCodeArgs] = service
this.servicesArgs[serviceHashCode] = serviceArgs
servicesArgs.add(serviceArgs)
}
servicesArgsOfPeripherals[peripheralHashCodeArgs] = servicesArgs
callback(Result.success(servicesArgs)) callback(Result.success(servicesArgs))
} else { } else {
val error = IllegalStateException("Discover GATT failed with status: $status") val error = IllegalStateException("Discover GATT failed with status: $status")
@ -584,11 +468,17 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
} }
} }
fun onCharacteristicRead(characteristic: BluetoothGattCharacteristic, status: Int, value: ByteArray) { fun onCharacteristicRead(
val hashCode = characteristic.hashCode() gatt: BluetoothGatt,
val characteristicArgs = characteristicsArgs[hashCode] as MyGattCharacteristicArgs characteristic: BluetoothGattCharacteristic,
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs status: Int,
val callback = readCharacteristicCallbacks.remove(characteristicHashCodeArgs) ?: return value: ByteArray
) {
val device = gatt.device
val addressArgs = device.address
val hashCodeArgs = characteristic.hashCode().toLong()
val callbacks = mReadCharacteristicCallbacks[addressArgs] ?: return
val callback = callbacks.remove(hashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(value)) callback(Result.success(value))
} else { } else {
@ -597,11 +487,14 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
} }
} }
fun onCharacteristicWrite(characteristic: BluetoothGattCharacteristic, status: Int) { fun onCharacteristicWrite(
val hashCode = characteristic.hashCode() gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int
val characteristicArgs = characteristicsArgs[hashCode] as MyGattCharacteristicArgs ) {
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs val device = gatt.device
val callback = writeCharacteristicCallbacks.remove(characteristicHashCodeArgs) ?: return val addressArgs = device.address
val hashCodeArgs = characteristic.hashCode().toLong()
val callbacks = mWriteCharacteristicCallbacks[addressArgs] ?: return
val callback = callbacks.remove(hashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(Unit)) callback(Result.success(Unit))
} else { } else {
@ -610,17 +503,23 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
} }
} }
fun onCharacteristicChanged(characteristic: BluetoothGattCharacteristic, value: ByteArray) { fun onCharacteristicChanged(
val hashCode = characteristic.hashCode() gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray
val characteristicArgs = characteristicsArgs[hashCode] as MyGattCharacteristicArgs ) {
api.onCharacteristicValueChanged(characteristicArgs, value) {} val device = gatt.device
val addressArgs = device.address
val hashCodeArgs = characteristic.hashCode().toLong()
mApi.onCharacteristicNotified(addressArgs, hashCodeArgs, value) {}
} }
fun onDescriptorRead(descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) { fun onDescriptorRead(
val hashCode = descriptor.hashCode() gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray
val descriptorArgs = descriptorsArgs[hashCode] as MyGattDescriptorArgs ) {
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs val device = gatt.device
val callback = readDescriptorCallbacks.remove(descriptorHashCodeArgs) ?: return val addressArgs = device.address
val hashCodeArgs = descriptor.hashCode().toLong()
val callbacks = mReadDescriptorCallbacks[addressArgs] ?: return
val callback = callbacks.remove(hashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(value)) callback(Result.success(value))
} else { } else {
@ -629,11 +528,12 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
} }
} }
fun onDescriptorWrite(descriptor: BluetoothGattDescriptor, status: Int) { fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
val hashCode = descriptor.hashCode() val device = gatt.device
val descriptorArgs = descriptorsArgs[hashCode] as MyGattDescriptorArgs val addressArgs = device.address
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs val hashCodeArgs = descriptor.hashCode().toLong()
val callback = writeDescriptorCallbacks.remove(descriptorHashCodeArgs) ?: return val callbacks = mWriteDescriptorCallbacks[addressArgs] ?: return
val callback = callbacks.remove(hashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(Unit)) callback(Result.success(Unit))
} else { } else {
@ -641,4 +541,42 @@ class MyCentralManager(private val context: Context, binaryMessenger: BinaryMess
callback(Result.failure(error)) callback(Result.failure(error))
} }
} }
private fun mClearState() {
if (mDiscovering) {
stopDiscovery()
}
for (gatt in mGATTs.values) {
gatt.disconnect()
}
mDevices.clear()
mGATTs.clear()
mCharacteristics.clear()
mDescriptors.clear()
mStartDiscoveryCallback = null
mConnectCallbacks.clear()
mDisconnectCallbacks.clear()
mRequestMtuCallbacks.clear()
mReadRssiCallbacks.clear()
mDiscoverServicesCallbacks.clear()
mReadCharacteristicCallbacks.clear()
mWriteCharacteristicCallbacks.clear()
mReadDescriptorCallbacks.clear()
mWriteDescriptorCallbacks.clear()
}
private fun mRetrieveCharacteristic(
addressArgs: String, hashCodeArgs: Long
): BluetoothGattCharacteristic? {
val characteristics = mCharacteristics[addressArgs] ?: return null
return characteristics[hashCodeArgs]
}
private fun mRetrieveDescriptor(
addressArgs: String, hashCodeArgs: Long
): BluetoothGattDescriptor? {
val descriptors = mDescriptors[addressArgs] ?: return null
return descriptors[hashCodeArgs]
}
} }

View File

@ -1,123 +1,109 @@
package dev.yanshouwang.bluetooth_low_energy_android package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattServer import android.bluetooth.BluetoothGattServer
import android.bluetooth.BluetoothGattServerCallback
import android.bluetooth.BluetoothGattService import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothProfile
import android.bluetooth.BluetoothStatusCodes import android.bluetooth.BluetoothStatusCodes
import android.bluetooth.le.AdvertiseCallback
import android.bluetooth.le.AdvertiseSettings import android.bluetooth.le.AdvertiseSettings
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.BinaryMessenger
class MyPeripheralManager(private val context: Context, binaryMessenger: BinaryMessenger) : MyBluetoothLowEnergyManager(context), MyPeripheralManagerHostApi { class MyPeripheralManager(context: Context, binaryMessenger: BinaryMessenger) :
MyBluetoothLowEnergyManager(context), MyPeripheralManagerHostApi {
companion object {
const val REQUEST_CODE = 444
}
private val advertiser get() = adapter.bluetoothLeAdvertiser private val advertiser get() = adapter.bluetoothLeAdvertiser
private val api = MyPeripheralManagerFlutterApi(binaryMessenger) private val mContext: Context
private val bluetoothGattServerCallback = MyBluetoothGattServerCallback(this, executor) private val mApi: MyPeripheralManagerFlutterApi
private val advertiseCallback = MyAdvertiseCallback(this)
private val devices = mutableMapOf<Long, BluetoothDevice>() private val bluetoothGattServerCallback: BluetoothGattServerCallback by lazy {
private val services = mutableMapOf<Long, BluetoothGattService>() MyBluetoothGattServerCallback(this, executor)
private val characteristics = mutableMapOf<Long, BluetoothGattCharacteristic>()
private val descriptors = mutableMapOf<Long, BluetoothGattDescriptor>()
private val mtus = mutableMapOf<Long, Int>()
private val confirms = mutableMapOf<Long, Boolean>()
private val preparedCharacteristics = mutableMapOf<Int, BluetoothGattCharacteristic>()
private val preparedValues = mutableMapOf<Int, ByteArray>()
private val values = mutableMapOf<Long, ByteArray>()
private val centralsArgs = mutableMapOf<Int, MyCentralArgs>()
private val servicesArgs = mutableMapOf<Int, MyGattServiceArgs>()
private val characteristicsArgs = mutableMapOf<Int, MyGattCharacteristicArgs>()
private val descriptorsArgs = mutableMapOf<Int, MyGattDescriptorArgs>()
private lateinit var server: BluetoothGattServer
private var registered = false
private var advertising = false
private var setUpCallback: ((Result<MyPeripheralManagerArgs>) -> Unit)? = null
private var addServiceCallback: ((Result<Unit>) -> Unit)? = null
private var startAdvertisingCallback: ((Result<Unit>) -> Unit)? = null
private val notifyCharacteristicValueChangedCallbacks = mutableMapOf<Long, (Result<Unit>) -> Unit>()
override fun setUp(callback: (Result<MyPeripheralManagerArgs>) -> Unit) {
try {
val unfinishedCallback = setUpCallback
if (unfinishedCallback != null) {
throw IllegalStateException()
} }
tearDown() private val advertiseCallback: AdvertiseCallback by lazy {
if (unsupported) { MyAdvertiseCallback(this)
val stateNumberArgs = MyBluetoothLowEnergyStateArgs.UNSUPPORTED.raw.toLong()
val args = MyPeripheralManagerArgs(stateNumberArgs)
callback(Result.success(args))
} }
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT) private lateinit var mServer: BluetoothGattServer
private var mOpening = false
private var mAdvertising = false
private val mServicesArgs: MutableMap<Int, MyGattServiceArgs>
private val mCharacteristicsArgs: MutableMap<Int, MyGattCharacteristicArgs>
private val mDescriptorsArgs: MutableMap<Int, MyGattDescriptorArgs>
private val mDevices: MutableMap<String, BluetoothDevice>
private val mServices: MutableMap<Long, BluetoothGattService>
private val mCharacteristics: MutableMap<Long, BluetoothGattCharacteristic>
private val mDescriptors: MutableMap<Long, BluetoothGattDescriptor>
private var mSetUpCallback: ((Result<Unit>) -> Unit)?
private var mAddServiceCallback: ((Result<Unit>) -> Unit)?
private var mStartAdvertisingCallback: ((Result<Unit>) -> Unit)?
private val mNotifyCharacteristicValueChangedCallbacks: MutableMap<String, (Result<Unit>) -> Unit>
init {
mContext = context
mApi = MyPeripheralManagerFlutterApi(binaryMessenger)
mServicesArgs = mutableMapOf()
mCharacteristicsArgs = mutableMapOf()
mDescriptorsArgs = mutableMapOf()
mDevices = mutableMapOf()
mServices = mutableMapOf()
mCharacteristics = mutableMapOf()
mDescriptors = mutableMapOf()
mSetUpCallback = null
mAddServiceCallback = null
mStartAdvertisingCallback = null
mNotifyCharacteristicValueChangedCallbacks = mutableMapOf()
}
override val permissions: Array<String>
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(
android.Manifest.permission.ACCESS_COARSE_LOCATION,
android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.BLUETOOTH_ADVERTISE,
android.Manifest.permission.BLUETOOTH_CONNECT
)
} else { } else {
arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION) arrayOf(
} android.Manifest.permission.ACCESS_COARSE_LOCATION,
authorize(permissions) android.Manifest.permission.ACCESS_FINE_LOCATION
setUpCallback = callback )
} catch (e: Throwable) {
callback(Result.failure(e))
}
} }
private fun tearDown() { override val requestCode: Int
if (registered) { get() = REQUEST_CODE
unregister()
}
if (advertising) {
stopAdvertising()
}
devices.clear()
services.clear()
characteristics.clear()
descriptors.clear()
mtus.clear()
confirms.clear()
preparedCharacteristics.clear()
preparedValues.clear()
values.clear()
centralsArgs.clear()
servicesArgs.clear()
characteristicsArgs.clear()
descriptorsArgs.clear()
setUpCallback = null
addServiceCallback = null
startAdvertisingCallback = null
notifyCharacteristicValueChangedCallbacks.clear()
}
override fun register() { override fun setUp() {
super.register() mClearState()
registered = true initialize()
}
override fun unregister() {
super.unregister()
registered = false
} }
override fun addService(serviceArgs: MyGattServiceArgs, callback: (Result<Unit>) -> Unit) { override fun addService(serviceArgs: MyGattServiceArgs, callback: (Result<Unit>) -> Unit) {
try { try {
val unfinishedCallback = addServiceCallback
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val service = serviceArgs.toService() val service = serviceArgs.toService()
val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull() val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull()
for (characteristicArgs in characteristicsArgs) { for (characteristicArgs in characteristicsArgs) {
val characteristic = characteristicArgs.toCharacteristic() val characteristic = characteristicArgs.toCharacteristic()
val cccDescriptor = BluetoothGattDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID, BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE) val cccDescriptor = BluetoothGattDescriptor(
CLIENT_CHARACTERISTIC_CONFIG_UUID,
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
)
val cccDescriptorAdded = characteristic.addDescriptor(cccDescriptor) val cccDescriptorAdded = characteristic.addDescriptor(cccDescriptor)
if (!cccDescriptorAdded) { if (!cccDescriptorAdded) {
throw IllegalStateException() throw IllegalStateException()
@ -129,89 +115,68 @@ class MyPeripheralManager(private val context: Context, binaryMessenger: BinaryM
// Already added. // Already added.
continue continue
} }
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
val descriptorHashCode = descriptor.hashCode()
this.mDescriptorsArgs[descriptorHashCode] = descriptorArgs
this.mDescriptors[descriptorHashCodeArgs] = descriptor
val descriptorAdded = characteristic.addDescriptor(descriptor) val descriptorAdded = characteristic.addDescriptor(descriptor)
if (!descriptorAdded) { if (!descriptorAdded) {
throw IllegalStateException() throw IllegalStateException()
} }
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
val descriptorHashCode = descriptor.hashCode()
this.descriptorsArgs[descriptorHashCode] = descriptorArgs
this.descriptors[descriptorHashCodeArgs] = descriptor
} }
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
val characteristicHashCode = characteristic.hashCode()
this.mCharacteristicsArgs[characteristicHashCode] = characteristicArgs
this.mCharacteristics[characteristicHashCodeArgs] = characteristic
val characteristicAdded = service.addCharacteristic(characteristic) val characteristicAdded = service.addCharacteristic(characteristic)
if (!characteristicAdded) { if (!characteristicAdded) {
throw IllegalStateException() throw IllegalStateException()
} }
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
val characteristicHashCode = characteristic.hashCode()
this.characteristicsArgs[characteristicHashCode] = characteristicArgs
this.characteristics[characteristicHashCodeArgs] = characteristic
}
val adding = server.addService(service)
if (!adding) {
throw IllegalStateException()
} }
val serviceHashCodeArgs = serviceArgs.hashCodeArgs val serviceHashCodeArgs = serviceArgs.hashCodeArgs
val serviceHashCode = service.hashCode() val serviceHashCode = service.hashCode()
this.servicesArgs[serviceHashCode] = serviceArgs this.mServicesArgs[serviceHashCode] = serviceArgs
this.services[serviceHashCodeArgs] = service this.mServices[serviceHashCodeArgs] = service
addServiceCallback = callback val adding = mServer.addService(service)
if (!adding) {
throw IllegalStateException()
}
mAddServiceCallback = callback
} catch (e: Throwable) { } catch (e: Throwable) {
freeService(serviceArgs) mClearService(serviceArgs)
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun removeService(serviceHashCodeArgs: Long) { override fun removeService(hashCodeArgs: Long) {
val service = services[serviceHashCodeArgs] as BluetoothGattService val service = mServices[hashCodeArgs] as BluetoothGattService
val serviceHashCode = service.hashCode() val hashCode = service.hashCode()
val serviceArgs = servicesArgs[serviceHashCode] as MyGattServiceArgs val serviceArgs = mServicesArgs[hashCode] as MyGattServiceArgs
val removed = server.removeService(service) val removed = mServer.removeService(service)
if (!removed) { if (!removed) {
throw IllegalStateException() throw IllegalStateException()
} }
freeService(serviceArgs) mClearService(serviceArgs)
}
private fun freeService(serviceArgs: MyGattServiceArgs) {
val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull()
for (characteristicArgs in characteristicsArgs) {
val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull()
for (descriptorArgs in descriptorsArgs) {
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
val descriptor = this.descriptors.remove(descriptorHashCodeArgs) ?: continue
val descriptorHashCode = descriptor.hashCode()
this.descriptorsArgs.remove(descriptorHashCode)
}
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
val characteristic = this.characteristics.remove(characteristicHashCodeArgs) ?: continue
this.confirms.remove(characteristicHashCodeArgs)
val characteristicHashCode = characteristic.hashCode()
this.characteristicsArgs.remove(characteristicHashCode)
}
val serviceHashCodeArgs = serviceArgs.hashCodeArgs
val service = services.remove(serviceHashCodeArgs) ?: return
val serviceHashCode = service.hashCode()
servicesArgs.remove(serviceHashCode)
} }
override fun clearServices() { override fun clearServices() {
server.clearServices() mServer.clearServices()
val servicesArgs = this.servicesArgs.values val servicesArgs = this.mServicesArgs.values
for (serviceArgs in servicesArgs) { for (serviceArgs in servicesArgs) {
freeService(serviceArgs) mClearService(serviceArgs)
} }
} }
override fun startAdvertising(advertisementArgs: MyAdvertisementArgs, callback: (Result<Unit>) -> Unit) { override fun startAdvertising(
advertisementArgs: MyAdvertisementArgs, callback: (Result<Unit>) -> Unit
) {
try { try {
if (startAdvertisingCallback != null) { val settings = AdvertiseSettings.Builder()
throw IllegalStateException() .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED).setConnectable(true)
} .build()
val settings = AdvertiseSettings.Builder().setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED).setConnectable(true).build()
val advertiseData = advertisementArgs.toAdvertiseData(adapter) val advertiseData = advertisementArgs.toAdvertiseData(adapter)
advertiser.startAdvertising(settings, advertiseData, advertiseCallback) advertiser.startAdvertising(settings, advertiseData, advertiseCallback)
startAdvertisingCallback = callback mStartAdvertisingCallback = callback
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
@ -219,264 +184,259 @@ class MyPeripheralManager(private val context: Context, binaryMessenger: BinaryM
override fun stopAdvertising() { override fun stopAdvertising() {
advertiser.stopAdvertising(advertiseCallback) advertiser.stopAdvertising(advertiseCallback)
advertising = false mAdvertising = false
} }
override fun getMaximumWriteLength(centralHashCodeArgs: Long): Long { override fun sendResponse(
val mtu = mtus[centralHashCodeArgs] ?: 23 addressArgs: String,
val maximumWriteLength = (mtu - 3).coerceIn(20, 512) idArgs: Long,
return maximumWriteLength.toLong() statusNumberArgs: Long,
} offsetArgs: Long,
valueArgs: ByteArray?
override fun sendReadCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean, valueArgs: ByteArray) { ) {
val device = devices[centralHashCodeArgs] as BluetoothDevice val device = mDevices[addressArgs] as BluetoothDevice
val requestId = idArgs.toInt() val requestId = idArgs.toInt()
val status = if (statusArgs) BluetoothGatt.GATT_SUCCESS val statusRawArgs = statusNumberArgs.toInt()
else BluetoothGatt.GATT_FAILURE val statusArgs = MyGattStatusArgs.ofRaw(statusRawArgs) ?: throw IllegalArgumentException()
val status = statusArgs.toStatus()
val offset = offsetArgs.toInt() val offset = offsetArgs.toInt()
val sent = server.sendResponse(device, requestId, status, offset, valueArgs) val sent = mServer.sendResponse(device, requestId, status, offset, valueArgs)
if (!sent) { if (!sent) {
throw IllegalStateException("Send read characteristic reply failed.") throw IllegalStateException("Send read characteristic reply failed.")
} }
} }
override fun sendWriteCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean) { override fun notifyCharacteristicChanged(
val device = devices[centralHashCodeArgs] as BluetoothDevice hashCodeArgs: Long,
val requestId = idArgs.toInt() valueArgs: ByteArray,
val status = if (statusArgs) BluetoothGatt.GATT_SUCCESS confirmArgs: Boolean,
else BluetoothGatt.GATT_FAILURE addressArgs: String,
val offset = offsetArgs.toInt() callback: (Result<Unit>) -> Unit
val value = values.remove(idArgs) as ByteArray ) {
val sent = server.sendResponse(device, requestId, status, offset, value)
if (!sent) {
throw IllegalStateException("Send write characteristic reply failed.")
}
}
override fun notifyCharacteristicValueChanged(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result<Unit>) -> Unit) {
try { try {
val unfinishedCallback = notifyCharacteristicValueChangedCallbacks[centralHashCodeArgs] val device = mDevices[addressArgs] as BluetoothDevice
if (unfinishedCallback != null) { val characteristic = mCharacteristics[hashCodeArgs] as BluetoothGattCharacteristic
throw IllegalStateException()
}
val device = devices[centralHashCodeArgs] as BluetoothDevice
val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic
val confirm = confirms[characteristicHashCodeArgs]
?: throw IllegalStateException("The characteristic is not subscribed.")
val notifying = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val notifying = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val statusCode = server.notifyCharacteristicChanged(device, characteristic, confirm, valueArgs) val statusCode = mServer.notifyCharacteristicChanged(
device, characteristic, confirmArgs, valueArgs
)
statusCode == BluetoothStatusCodes.SUCCESS statusCode == BluetoothStatusCodes.SUCCESS
} else { } else {
// TODO: remove this when minSdkVersion >= 33
characteristic.value = valueArgs characteristic.value = valueArgs
server.notifyCharacteristicChanged(device, characteristic, confirm) mServer.notifyCharacteristicChanged(device, characteristic, confirmArgs)
} }
if (!notifying) { if (!notifying) {
throw IllegalStateException() throw IllegalStateException()
} }
notifyCharacteristicValueChangedCallbacks[centralHashCodeArgs] = callback mNotifyCharacteristicValueChangedCallbacks[addressArgs] = callback
} catch (e: Throwable) { } catch (e: Throwable) {
callback(Result.failure(e)) callback(Result.failure(e))
} }
} }
override fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean { override fun onStateChanged(state: MyBluetoothLowEnergyState) {
if (requestCode != REQUEST_CODE) { when (state) {
return false MyBluetoothLowEnergyStateArgs.POWEREDOFF -> mCloseGattServer()
MyBluetoothLowEnergyStateArgs.POWEREDON -> mOpenGattServer()
else -> {}
} }
val authorized = results.all { r -> r == PackageManager.PERMISSION_GRANTED } val stateNumberArgs = state.raw.toLong()
val callback = setUpCallback ?: return false mApi.onStateChanged(stateNumberArgs) {}
setUpCallback = null
val stateArgs = if (authorized) adapter.stateArgs
else MyBluetoothLowEnergyStateArgs.UNAUTHORIZED
val stateNumberArgs = stateArgs.raw.toLong()
val args = MyPeripheralManagerArgs(stateNumberArgs)
if (stateArgs == MyBluetoothLowEnergyStateArgs.POWEREDON) {
server = manager.openGattServer(context, bluetoothGattServerCallback)
}
callback(Result.success(args))
if (authorized) {
register()
}
return true
}
override fun onReceive(intent: Intent) {
val action = intent.action
if (action != BluetoothAdapter.ACTION_STATE_CHANGED) {
return
}
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
val stateArgs = state.toBluetoothLowEnergyStateArgs()
if (stateArgs == MyBluetoothLowEnergyStateArgs.POWEREDON) {
server = manager.openGattServer(context, bluetoothGattServerCallback)
}
val stateNumberArgs = stateArgs.raw.toLong()
api.onStateChanged(stateNumberArgs) {}
} }
fun onServiceAdded(status: Int, service: BluetoothGattService) { fun onServiceAdded(status: Int, service: BluetoothGattService) {
val callback = addServiceCallback ?: return val callback = mAddServiceCallback ?: return
addServiceCallback = null mAddServiceCallback = null
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(Unit)) callback(Result.success(Unit))
} else { } else {
val hashCode = service.hashCode()
val serviceArgs = servicesArgs[hashCode] as MyGattServiceArgs
freeService(serviceArgs)
val error = IllegalStateException("Read rssi failed with status: $status") val error = IllegalStateException("Read rssi failed with status: $status")
callback(Result.failure(error)) callback(Result.failure(error))
val hashCode = service.hashCode()
val serviceArgs = mServicesArgs[hashCode] ?: return
mClearService(serviceArgs)
} }
} }
fun onStartSuccess(settingsInEffect: AdvertiseSettings) { fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
advertising = true mAdvertising = true
val callback = startAdvertisingCallback ?: return val callback = mStartAdvertisingCallback ?: return
startAdvertisingCallback = null mStartAdvertisingCallback = null
callback(Result.success(Unit)) callback(Result.success(Unit))
} }
fun onStartFailure(errorCode: Int) { fun onStartFailure(errorCode: Int) {
val callback = startAdvertisingCallback ?: return val callback = mStartAdvertisingCallback ?: return
startAdvertisingCallback = null mStartAdvertisingCallback = null
val error = IllegalStateException("Start advertising failed with error code: $errorCode") val error = IllegalStateException("Start advertising failed with error code: $errorCode")
callback(Result.failure(error)) callback(Result.failure(error))
} }
fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) { fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) {
val hashCode = device.hashCode() val centralArgs = device.toCentralArgs()
val centralArgs = centralsArgs.getOrPut(hashCode) { device.toCentralArgs() } val addressArgs = centralArgs.addressArgs
val centralHashCodeArgs = centralArgs.hashCodeArgs val stateArgs = newState == BluetoothProfile.STATE_CONNECTED
devices[centralHashCodeArgs] = device mDevices[addressArgs] = device
mApi.onConnectionStateChanged(centralArgs, stateArgs) {}
} }
fun onMtuChanged(device: BluetoothDevice, mtu: Int) { fun onMtuChanged(device: BluetoothDevice, mtu: Int) {
val hashCode = device.hashCode() val addressArgs = device.address
val centralArgs = centralsArgs.getOrPut(hashCode) { device.toCentralArgs() } val mtuArgs = mtu.toLong()
val centralHashCodeArgs = centralArgs.hashCodeArgs mApi.onMtuChanged(addressArgs, mtuArgs) {}
devices[centralHashCodeArgs] = device
mtus[centralHashCodeArgs] = mtu
} }
fun onCharacteristicReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, characteristic: BluetoothGattCharacteristic) { fun onCharacteristicReadRequest(
val deviceHashCode = device.hashCode() device: BluetoothDevice,
val centralArgs = centralsArgs.getOrPut(deviceHashCode) { device.toCentralArgs() } requestId: Int,
val centralHashCodeArgs = centralArgs.hashCodeArgs offset: Int,
devices[centralHashCodeArgs] = device characteristic: BluetoothGattCharacteristic
val characteristicHashCode = characteristic.hashCode() ) {
val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs val addressArgs = device.address
val hashCode = characteristic.hashCode()
val characteristicArgs = mCharacteristicsArgs[hashCode] ?: return
val hashCodeArgs = characteristicArgs.hashCodeArgs
val idArgs = requestId.toLong() val idArgs = requestId.toLong()
val offsetArgs = offset.toLong() val offsetArgs = offset.toLong()
api.onReadCharacteristicCommandReceived(centralArgs, characteristicArgs, idArgs, offsetArgs) {} mApi.onCharacteristicReadRequest(addressArgs, hashCodeArgs, idArgs, offsetArgs) {}
} }
fun onCharacteristicWriteRequest(device: BluetoothDevice, requestId: Int, characteristic: BluetoothGattCharacteristic, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) { fun onCharacteristicWriteRequest(
val deviceHashCode = device.hashCode() device: BluetoothDevice,
val centralArgs = centralsArgs.getOrPut(deviceHashCode) { device.toCentralArgs() } requestId: Int,
val centralHashCodeArgs = centralArgs.hashCodeArgs characteristic: BluetoothGattCharacteristic,
devices[centralHashCodeArgs] = device preparedWrite: Boolean,
if (preparedWrite) { responseNeeded: Boolean,
val preparedCharacteristic = preparedCharacteristics[deviceHashCode] offset: Int,
if (preparedCharacteristic != null && preparedCharacteristic != characteristic) { value: ByteArray
val status = BluetoothGatt.GATT_CONNECTION_CONGESTED ) {
server.sendResponse(device, requestId, status, offset, value) val addressArgs = device.address
return val hashCode = characteristic.hashCode()
} val characteristicArgs = mCharacteristicsArgs[hashCode] ?: return
val preparedValue = preparedValues[deviceHashCode] val hashCodeArgs = characteristicArgs.hashCodeArgs
if (preparedValue == null) {
preparedCharacteristics[deviceHashCode] = characteristic
preparedValues[deviceHashCode] = value
} else {
preparedValues[deviceHashCode] = preparedValue.plus(value)
}
val status = BluetoothGatt.GATT_SUCCESS
server.sendResponse(device, requestId, status, offset, value)
} else {
val characteristicHashCode = characteristic.hashCode()
val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs
val idArgs = requestId.toLong() val idArgs = requestId.toLong()
val offsetArgs = offset.toLong() val offsetArgs = offset.toLong()
values[idArgs] = value mApi.onCharacteristicWriteRequest(
api.onWriteCharacteristicCommandReceived(centralArgs, characteristicArgs, idArgs, offsetArgs, value) {} addressArgs, hashCodeArgs, idArgs, offsetArgs, value, preparedWrite, responseNeeded
} ) {}
} }
fun onExecuteWrite(device: BluetoothDevice, requestId: Int, execute: Boolean) { fun onExecuteWrite(device: BluetoothDevice, requestId: Int, execute: Boolean) {
val deviceHashCode = device.hashCode() val addressArgs = device.address
val centralArgs = centralsArgs[deviceHashCode] as MyCentralArgs
val characteristic = preparedCharacteristics.remove(deviceHashCode) as BluetoothGattCharacteristic
val characteristicHashCode = characteristic.hashCode()
val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs
val value = preparedValues.remove(deviceHashCode) as ByteArray
if (execute) {
val idArgs = requestId.toLong() val idArgs = requestId.toLong()
val offsetArgs = 0L mApi.onExecuteWrite(addressArgs, idArgs, execute) {}
values[idArgs] = value
api.onWriteCharacteristicCommandReceived(centralArgs, characteristicArgs, idArgs, offsetArgs, value) {}
} else {
val status = BluetoothGatt.GATT_SUCCESS
val offset = 0
server.sendResponse(device, requestId, status, offset, value)
}
} }
fun onDescriptorReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor) { fun onDescriptorReadRequest(
device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor
) {
val status = BluetoothGatt.GATT_SUCCESS val status = BluetoothGatt.GATT_SUCCESS
val descriptorHashCode = descriptor.hashCode() val hashCode = descriptor.hashCode()
val descriptorArgs = descriptorsArgs[descriptorHashCode] as MyGattDescriptorArgs val descriptorArgs = mDescriptorsArgs[hashCode] as MyGattDescriptorArgs
val value = descriptorArgs.valueArgs val value = descriptorArgs.valueArgs
val sent = server.sendResponse(device, requestId, status, offset, value) val sent = mServer.sendResponse(device, requestId, status, offset, value)
if (!sent) { if (!sent) {
Log.e(TAG, "onDescriptorReadRequest: send response failed.") Log.e(TAG, "onDescriptorReadRequest: send response failed.")
} }
} }
fun onDescriptorWriteRequest(device: BluetoothDevice, requestId: Int, descriptor: BluetoothGattDescriptor, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) { fun onDescriptorWriteRequest(
val status = if (descriptor.uuid == CLIENT_CHARACTERISTIC_CONFIG_UUID) { device: BluetoothDevice,
val deviceHashCode = device.hashCode() requestId: Int,
val centralArgs = centralsArgs.getOrPut(deviceHashCode) { device.toCentralArgs() } descriptor: BluetoothGattDescriptor,
val centralHashCodeArgs = centralArgs.hashCodeArgs preparedWrite: Boolean,
devices[centralHashCodeArgs] = device responseNeeded: Boolean,
offset: Int,
value: ByteArray
) {
if (descriptor.uuid == CLIENT_CHARACTERISTIC_CONFIG_UUID) {
val addressArgs = device.address
val characteristic = descriptor.characteristic val characteristic = descriptor.characteristic
val characteristicHashCode = characteristic.hashCode() val hashCode = characteristic.hashCode()
val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs val characteristicArgs = mCharacteristicsArgs[hashCode] ?: return
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs val hashCodeArgs = characteristicArgs.hashCodeArgs
// TODO: what is 中缀? val stateArgs = value.toNotifyStateArgs()
if (value contentEquals BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) { val stateNumberArgs = stateArgs.raw.toLong()
confirms[characteristicHashCodeArgs] = false mApi.onCharacteristicNotifyStateChanged(
val stateArgs = true addressArgs, hashCodeArgs, stateNumberArgs
api.onNotifyCharacteristicCommandReceived(centralArgs, characteristicArgs, stateArgs) {} ) {}
BluetoothGatt.GATT_SUCCESS
} else if (value contentEquals BluetoothGattDescriptor.ENABLE_INDICATION_VALUE) {
confirms[characteristicHashCodeArgs] = true
val stateArgs = true
api.onNotifyCharacteristicCommandReceived(centralArgs, characteristicArgs, stateArgs) {}
BluetoothGatt.GATT_SUCCESS
} else if (value contentEquals BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE) {
confirms.remove(characteristicHashCodeArgs)
val stateArgs = false
api.onNotifyCharacteristicCommandReceived(centralArgs, characteristicArgs, stateArgs) {}
BluetoothGatt.GATT_SUCCESS
} else {
BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED
} }
} else BluetoothGatt.GATT_SUCCESS if (responseNeeded) {
val sent = server.sendResponse(device, requestId, status, offset, value) val status = BluetoothGatt.GATT_SUCCESS
val sent = mServer.sendResponse(device, requestId, status, offset, value)
if (!sent) { if (!sent) {
Log.e(TAG, "onDescriptorReadRequest: send response failed.") Log.e(TAG, "onDescriptorReadRequest: send response failed.")
} }
} }
}
fun onNotificationSent(device: BluetoothDevice, status: Int) { fun onNotificationSent(device: BluetoothDevice, status: Int) {
val deviceHashCode = device.hashCode() val addressArgs = device.address
val centralArgs = centralsArgs[deviceHashCode] as MyCentralArgs val callback = mNotifyCharacteristicValueChangedCallbacks.remove(addressArgs) ?: return
val centralHashCodeArgs = centralArgs.hashCodeArgs
val callback = notifyCharacteristicValueChangedCallbacks.remove(centralHashCodeArgs)
?: return
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(Unit)) callback(Result.success(Unit))
} else { } else {
val error = IllegalStateException("Notify characteristic value changed failed with status: $status") val error =
IllegalStateException("Notify characteristic value changed failed with status: $status")
callback(Result.failure(error)) callback(Result.failure(error))
} }
} }
private fun mClearState() {
if (mAdvertising) {
stopAdvertising()
}
mServicesArgs.clear()
mCharacteristicsArgs.clear()
mDescriptorsArgs.clear()
mDevices.clear()
mServices.clear()
mCharacteristics.clear()
mDescriptors.clear()
mSetUpCallback = null
mAddServiceCallback = null
mStartAdvertisingCallback = null
mNotifyCharacteristicValueChangedCallbacks.clear()
}
private fun mOpenGattServer() {
if (mOpening) {
return
}
mServer = manager.openGattServer(mContext, bluetoothGattServerCallback)
mOpening = true
}
private fun mCloseGattServer() {
if (!mOpening) {
return
}
mServer.close()
mOpening = false
}
private fun mClearService(serviceArgs: MyGattServiceArgs) {
val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull()
for (characteristicArgs in characteristicsArgs) {
val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull()
for (descriptorArgs in descriptorsArgs) {
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
val descriptor = mDescriptors.remove(descriptorHashCodeArgs) ?: continue
val descriptorHashCode = descriptor.hashCode()
mDescriptorsArgs.remove(descriptorHashCode)
}
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
val characteristic = mCharacteristics.remove(characteristicHashCodeArgs) ?: continue
val characteristicHashCode = characteristic.hashCode()
mCharacteristicsArgs.remove(characteristicHashCode)
}
val serviceHashCodeArgs = serviceArgs.hashCodeArgs
val service = mServices.remove(serviceHashCodeArgs) ?: return
val serviceHashCode = service.hashCode()
mServicesArgs.remove(serviceHashCode)
}
} }

View File

@ -2,8 +2,17 @@ package dev.yanshouwang.bluetooth_low_energy_android
import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener
class MyRequestPermissionResultListener(private val bluetoothLowEnergyManager: MyBluetoothLowEnergyManager) : RequestPermissionsResultListener { class MyRequestPermissionResultListener(manager: MyBluetoothLowEnergyManager) :
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, results: IntArray): Boolean { RequestPermissionsResultListener {
return bluetoothLowEnergyManager.onRequestPermissionsResult(requestCode, results) private val mManager: MyBluetoothLowEnergyManager
init {
mManager = manager
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<out String>, results: IntArray
): Boolean {
return mManager.onRequestPermissionsResult(requestCode, permissions, results)
} }
} }

View File

@ -3,14 +3,20 @@ package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.le.ScanCallback import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult import android.bluetooth.le.ScanResult
class MyScanCallback(private val centralManager: MyCentralManager) : ScanCallback() { class MyScanCallback(manager: MyCentralManager) : ScanCallback() {
private val mManager: MyCentralManager
init {
mManager = manager
}
override fun onScanFailed(errorCode: Int) { override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode) super.onScanFailed(errorCode)
centralManager.onScanFailed(errorCode) mManager.onScanFailed(errorCode)
} }
override fun onScanResult(callbackType: Int, result: ScanResult) { override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result) super.onScanResult(callbackType, result)
centralManager.onScanResult(result) mManager.onScanResult(result)
} }
} }

View File

@ -8,18 +8,17 @@ import 'package:convert/convert.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
CentralManager get centralManager => CentralManager.instance;
PeripheralManager get peripheralManager => PeripheralManager.instance;
void main() { void main() {
runZonedGuarded(onStartUp, onCrashed); runZonedGuarded(onStartUp, onCrashed);
} }
void onStartUp() async { void onStartUp() async {
Logger.root.onRecord.listen(onLogRecord); Logger.root.onRecord.listen(onLogRecord);
// hierarchicalLoggingEnabled = true;
// CentralManager.instance.logLevel = Level.WARNING;
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await centralManager.setUp(); await CentralManager.instance.setUp();
await peripheralManager.setUp(); await PeripheralManager.instance.setUp();
runApp(const MyApp()); runApp(const MyApp());
} }
@ -58,6 +57,8 @@ class _MyAppState extends State<MyApp> {
return MaterialApp( return MaterialApp(
theme: ThemeData.light( theme: ThemeData.light(
useMaterial3: true, useMaterial3: true,
).copyWith(
materialTapTargetSize: MaterialTapTargetSize.padded,
), ),
home: const HomeView(), home: const HomeView(),
routes: { routes: {
@ -168,15 +169,15 @@ class _ScannerViewState extends State<ScannerView> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
state = ValueNotifier(centralManager.state); state = ValueNotifier(BluetoothLowEnergyState.unknown);
discovering = ValueNotifier(false); discovering = ValueNotifier(false);
discoveredEventArgs = ValueNotifier([]); discoveredEventArgs = ValueNotifier([]);
stateChangedSubscription = centralManager.stateChanged.listen( stateChangedSubscription = CentralManager.instance.stateChanged.listen(
(eventArgs) { (eventArgs) {
state.value = eventArgs.state; state.value = eventArgs.state;
}, },
); );
discoveredSubscription = centralManager.discovered.listen( discoveredSubscription = CentralManager.instance.discovered.listen(
(eventArgs) { (eventArgs) {
final items = discoveredEventArgs.value; final items = discoveredEventArgs.value;
final i = items.indexWhere( final i = items.indexWhere(
@ -190,6 +191,11 @@ class _ScannerViewState extends State<ScannerView> {
} }
}, },
); );
_initialize();
}
void _initialize() async {
state.value = await CentralManager.instance.getState();
} }
@override @override
@ -233,12 +239,13 @@ class _ScannerViewState extends State<ScannerView> {
} }
Future<void> startDiscovery() async { Future<void> startDiscovery() async {
await centralManager.startDiscovery(); discoveredEventArgs.value = [];
await CentralManager.instance.startDiscovery();
discovering.value = true; discovering.value = true;
} }
Future<void> stopDiscovery() async { Future<void> stopDiscovery() async {
await centralManager.stopDiscovery(); await CentralManager.instance.stopDiscovery();
discovering.value = false; discovering.value = false;
} }
@ -340,7 +347,13 @@ class _ScannerViewState extends State<ScannerView> {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
trailing: RssiWidget(rssi), trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
RssiWidget(rssi),
Text('$rssi'),
],
),
); );
}, },
separatorBuilder: (context, i) { separatorBuilder: (context, i) {
@ -378,44 +391,39 @@ class PeripheralView extends StatefulWidget {
} }
class _PeripheralViewState extends State<PeripheralView> { class _PeripheralViewState extends State<PeripheralView> {
late final ValueNotifier<bool> state; late final ValueNotifier<bool> connectionState;
late final DiscoveredEventArgs eventArgs; late final DiscoveredEventArgs eventArgs;
late final ValueNotifier<List<GattService>> services; late final ValueNotifier<List<GattService>> services;
late final ValueNotifier<List<GattCharacteristic>> characteristics; late final ValueNotifier<List<GattCharacteristic>> characteristics;
late final ValueNotifier<GattService?> service; late final ValueNotifier<GattService?> service;
late final ValueNotifier<GattCharacteristic?> characteristic; late final ValueNotifier<GattCharacteristic?> characteristic;
late final ValueNotifier<GattCharacteristicWriteType> writeType; late final ValueNotifier<GattCharacteristicWriteType> writeType;
late final ValueNotifier<int> maximumWriteLength;
late final ValueNotifier<int> rssi;
late final ValueNotifier<List<Log>> logs; late final ValueNotifier<List<Log>> logs;
late final TextEditingController writeController; late final TextEditingController writeController;
late final StreamSubscription stateChangedSubscription; late final StreamSubscription connectionStateChangedSubscription;
late final StreamSubscription valueChangedSubscription; late final StreamSubscription characteristicNotifiedSubscription;
late final StreamSubscription rssiChangedSubscription;
late final Timer rssiTimer;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
eventArgs = widget.eventArgs; eventArgs = widget.eventArgs;
state = ValueNotifier(false); connectionState = ValueNotifier(false);
services = ValueNotifier([]); services = ValueNotifier([]);
characteristics = ValueNotifier([]); characteristics = ValueNotifier([]);
service = ValueNotifier(null); service = ValueNotifier(null);
characteristic = ValueNotifier(null); characteristic = ValueNotifier(null);
writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); writeType = ValueNotifier(GattCharacteristicWriteType.withResponse);
maximumWriteLength = ValueNotifier(0);
rssi = ValueNotifier(-100);
logs = ValueNotifier([]); logs = ValueNotifier([]);
writeController = TextEditingController(); writeController = TextEditingController();
stateChangedSubscription = centralManager.peripheralStateChanged.listen( connectionStateChangedSubscription =
CentralManager.instance.connectionStateChanged.listen(
(eventArgs) { (eventArgs) {
if (eventArgs.peripheral != this.eventArgs.peripheral) { if (eventArgs.peripheral != this.eventArgs.peripheral) {
return; return;
} }
final state = eventArgs.state; final connectionState = eventArgs.connectionState;
this.state.value = state; this.connectionState.value = connectionState;
if (!state) { if (!connectionState) {
services.value = []; services.value = [];
characteristics.value = []; characteristics.value = [];
service.value = null; service.value = null;
@ -424,12 +432,13 @@ class _PeripheralViewState extends State<PeripheralView> {
} }
}, },
); );
valueChangedSubscription = centralManager.characteristicValueChanged.listen( characteristicNotifiedSubscription =
CentralManager.instance.characteristicNotified.listen(
(eventArgs) { (eventArgs) {
final characteristic = this.characteristic.value; // final characteristic = this.characteristic.value;
if (eventArgs.characteristic != characteristic) { // if (eventArgs.characteristic != characteristic) {
return; // return;
} // }
const type = LogType.notify; const type = LogType.notify;
final log = Log(type, eventArgs.value); final log = Log(type, eventArgs.value);
logs.value = [ logs.value = [
@ -438,28 +447,16 @@ class _PeripheralViewState extends State<PeripheralView> {
]; ];
}, },
); );
rssiTimer = Timer.periodic(
const Duration(seconds: 5),
(timer) async {
final state = this.state.value;
if (state) {
rssi.value = await centralManager.readRSSI(eventArgs.peripheral);
} else {
rssi.value = -100;
}
},
);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return WillPopScope( return PopScope(
onWillPop: () async { onPopInvoked: (didPop) async {
if (state.value) { if (connectionState.value) {
final peripheral = eventArgs.peripheral; final peripheral = eventArgs.peripheral;
await centralManager.disconnect(peripheral); await CentralManager.instance.disconnect(peripheral);
} }
return true;
}, },
child: Scaffold( child: Scaffold(
appBar: buildAppBar(context), appBar: buildAppBar(context),
@ -474,25 +471,17 @@ class _PeripheralViewState extends State<PeripheralView> {
title: Text(title), title: Text(title),
actions: [ actions: [
ValueListenableBuilder( ValueListenableBuilder(
valueListenable: state, valueListenable: connectionState,
builder: (context, state, child) { builder: (context, state, child) {
return TextButton( return TextButton(
onPressed: () async { onPressed: () async {
final peripheral = eventArgs.peripheral; final peripheral = eventArgs.peripheral;
if (state) { if (state) {
await centralManager.disconnect(peripheral); await CentralManager.instance.disconnect(peripheral);
maximumWriteLength.value = 0;
rssi.value = 0;
} else { } else {
await centralManager.connect(peripheral); await CentralManager.instance.connect(peripheral);
services.value = services.value =
await centralManager.discoverGATT(peripheral); await CentralManager.instance.discoverGATT(peripheral);
maximumWriteLength.value =
await centralManager.getMaximumWriteLength(
peripheral,
type: writeType.value,
);
rssi.value = await centralManager.readRSSI(peripheral);
} }
}, },
child: Text(state ? 'DISCONNECT' : 'CONNECT'), child: Text(state ? 'DISCONNECT' : 'CONNECT'),
@ -566,7 +555,32 @@ class _PeripheralViewState extends State<PeripheralView> {
hint: const Text('CHOOSE A CHARACTERISTIC'), hint: const Text('CHOOSE A CHARACTERISTIC'),
value: characteristic, value: characteristic,
onChanged: (characteristic) { onChanged: (characteristic) {
if (characteristic == null) {
return;
}
this.characteristic.value = characteristic; this.characteristic.value = characteristic;
final writeType = this.writeType.value;
final canWrite = characteristic.properties.contains(
GattCharacteristicProperty.write,
);
final canWriteWithoutResponse =
characteristic.properties.contains(
GattCharacteristicProperty.writeWithoutResponse,
);
if (writeType ==
GattCharacteristicWriteType.withResponse &&
!canWrite &&
canWriteWithoutResponse) {
this.writeType.value =
GattCharacteristicWriteType.withoutResponse;
}
if (writeType ==
GattCharacteristicWriteType.withoutResponse &&
!canWriteWithoutResponse &&
canWrite) {
this.writeType.value =
GattCharacteristicWriteType.withResponse;
}
}, },
); );
}, },
@ -624,21 +638,101 @@ class _PeripheralViewState extends State<PeripheralView> {
}, },
), ),
), ),
ValueListenableBuilder(
valueListenable: characteristic,
builder: (context, characteristic, chld) {
final bool canNotify, canRead, canWrite, canWriteWithoutResponse;
if (characteristic == null) {
canNotify =
canRead = canWrite = canWriteWithoutResponse = false;
} else {
final properties = characteristic.properties;
canNotify = properties.contains(
GattCharacteristicProperty.notify,
) ||
properties.contains(
GattCharacteristicProperty.indicate,
);
canRead = properties.contains(
GattCharacteristicProperty.read,
);
canWrite = properties.contains(
GattCharacteristicProperty.write,
);
canWriteWithoutResponse = properties.contains(
GattCharacteristicProperty.writeWithoutResponse,
);
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
margin: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: characteristic != null && canNotify
? () async {
await CentralManager.instance
.setCharacteristicNotifyState(
characteristic,
state: true,
);
}
: null,
child: const Text('NOTIFY'),
),
const SizedBox(width: 8.0),
ElevatedButton(
onPressed: characteristic != null && canRead
? () async {
final value = await CentralManager.instance
.readCharacteristic(characteristic);
const type = LogType.read;
final log = Log(type, value);
logs.value = [...logs.value, log];
}
: null,
child: const Text('READ'),
)
],
),
),
SizedBox(
height: 160.0,
child: TextField(
controller: writeController,
enabled: canWrite || canWriteWithoutResponse,
expands: true,
maxLines: null,
textAlignVertical: TextAlignVertical.top,
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
),
),
Row( Row(
children: [ children: [
ValueListenableBuilder( ValueListenableBuilder(
valueListenable: writeType, valueListenable: writeType,
builder: (context, writeType, child) { builder: (context, writeType, child) {
return ToggleButtons( return ToggleButtons(
onPressed: (i) async { onPressed: canWrite || canWriteWithoutResponse
final type = GattCharacteristicWriteType.values[i]; ? (i) {
if (!canWrite || !canWriteWithoutResponse) {
return;
}
final type =
GattCharacteristicWriteType.values[i];
this.writeType.value = type; this.writeType.value = type;
maximumWriteLength.value = }
await centralManager.getMaximumWriteLength( : null,
eventArgs.peripheral,
type: type,
);
},
constraints: const BoxConstraints( constraints: const BoxConstraints(
minWidth: 0.0, minWidth: 0.0,
minHeight: 0.0, minHeight: 0.0,
@ -647,7 +741,8 @@ class _PeripheralViewState extends State<PeripheralView> {
isSelected: GattCharacteristicWriteType.values isSelected: GattCharacteristicWriteType.values
.map((type) => type == writeType) .map((type) => type == writeType)
.toList(), .toList(),
children: GattCharacteristicWriteType.values.map((type) { children: GattCharacteristicWriteType.values.map(
(type) {
return Container( return Container(
margin: const EdgeInsets.symmetric( margin: const EdgeInsets.symmetric(
horizontal: 8.0, horizontal: 8.0,
@ -655,7 +750,8 @@ class _PeripheralViewState extends State<PeripheralView> {
), ),
child: Text(type.name), child: Text(type.name),
); );
}).toList(), },
).toList(),
); );
// final segments = // final segments =
// GattCharacteristicWriteType.values.map((type) { // GattCharacteristicWriteType.values.map((type) {
@ -679,118 +775,46 @@ class _PeripheralViewState extends State<PeripheralView> {
// ); // );
}, },
), ),
const SizedBox(width: 8.0),
ValueListenableBuilder(
valueListenable: state,
builder: (context, state, child) {
return ValueListenableBuilder(
valueListenable: maximumWriteLength,
builder: (context, maximumWriteLength, child) {
return Text('$maximumWriteLength');
},
);
},
),
const Spacer(), const Spacer(),
ValueListenableBuilder( ElevatedButton(
valueListenable: rssi,
builder: (context, rssi, child) {
return RssiWidget(rssi);
},
),
],
),
Container(
margin: const EdgeInsets.only(bottom: 16.0),
height: 160.0,
child: ValueListenableBuilder(
valueListenable: characteristic,
builder: (context, characteristic, child) {
final bool canNotify, canRead, canWrite;
if (characteristic == null) {
canNotify = canRead = canWrite = false;
} else {
final properties = characteristic.properties;
canNotify = properties.contains(
GattCharacteristicProperty.notify,
);
canRead = properties.contains(
GattCharacteristicProperty.read,
);
canWrite = properties.contains(
GattCharacteristicProperty.write,
);
}
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: TextField(
controller: writeController,
enabled: canWrite,
expands: true,
maxLines: null,
textAlignVertical: TextAlignVertical.top,
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
),
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: characteristic != null && canNotify
? () async {
await centralManager.notifyCharacteristic(
characteristic,
state: true,
);
}
: null,
child: const Text('NOTIFY'),
),
TextButton(
onPressed: characteristic != null && canRead
? () async {
final value = await centralManager
.readCharacteristic(characteristic);
const type = LogType.read;
final log = Log(type, value);
logs.value = [...logs.value, log];
}
: null,
child: const Text('READ'),
),
TextButton(
onPressed: characteristic != null && canWrite onPressed: characteristic != null && canWrite
? () async { ? () async {
final text = writeController.text; final text = writeController.text;
final elements = utf8.encode(text); final elements = utf8.encode(text);
final value = Uint8List.fromList(elements); final value = Uint8List.fromList(elements);
final type = writeType.value; final type = writeType.value;
await centralManager.writeCharacteristic( // Fragments the value by 512 bytes.
const fragmentSize = 512;
var start = 0;
while (start < value.length) {
final end = start + fragmentSize;
final fragmentedValue = end < value.length
? value.sublist(start, end)
: value.sublist(start);
await CentralManager.instance
.writeCharacteristic(
characteristic, characteristic,
value: value, value: fragmentedValue,
type: type, type: type,
); );
final log = Log(LogType.write, value); final log = Log(
LogType.write,
fragmentedValue,
);
logs.value = [...logs.value, log]; logs.value = [...logs.value, log];
start = end;
}
} }
: null, : null,
child: const Text('WRITE'), child: const Text('WRITE'),
), ),
], ],
), ),
const SizedBox(height: 16.0),
], ],
); );
}, },
), ),
),
], ],
), ),
); );
@ -799,17 +823,14 @@ class _PeripheralViewState extends State<PeripheralView> {
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
rssiTimer.cancel(); connectionStateChangedSubscription.cancel();
stateChangedSubscription.cancel(); characteristicNotifiedSubscription.cancel();
valueChangedSubscription.cancel(); connectionState.dispose();
state.dispose();
services.dispose(); services.dispose();
characteristics.dispose(); characteristics.dispose();
service.dispose(); service.dispose();
characteristic.dispose(); characteristic.dispose();
writeType.dispose(); writeType.dispose();
maximumWriteLength.dispose();
rssi.dispose();
logs.dispose(); logs.dispose();
writeController.dispose(); writeController.dispose();
} }
@ -828,87 +849,63 @@ class _AdvertiserViewState extends State<AdvertiserView>
late final ValueNotifier<bool> advertising; late final ValueNotifier<bool> advertising;
late final ValueNotifier<List<Log>> logs; late final ValueNotifier<List<Log>> logs;
late final StreamSubscription stateChangedSubscription; late final StreamSubscription stateChangedSubscription;
late final StreamSubscription readCharacteristicCommandReceivedSubscription; late final StreamSubscription characteristicReadSubscription;
late final StreamSubscription writeCharacteristicCommandReceivedSubscription; late final StreamSubscription characteristicWrittenSubscription;
late final StreamSubscription notifyCharacteristicCommandReceivedSubscription; late final StreamSubscription characteristicNotifyStateChangedSubscription;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
state = ValueNotifier(peripheralManager.state); state = ValueNotifier(BluetoothLowEnergyState.unknown);
advertising = ValueNotifier(false); advertising = ValueNotifier(false);
logs = ValueNotifier([]); logs = ValueNotifier([]);
stateChangedSubscription = peripheralManager.stateChanged.listen( stateChangedSubscription = PeripheralManager.instance.stateChanged.listen(
(eventArgs) { (eventArgs) {
state.value = eventArgs.state; state.value = eventArgs.state;
}, },
); );
readCharacteristicCommandReceivedSubscription = characteristicReadSubscription =
peripheralManager.readCharacteristicCommandReceived.listen( PeripheralManager.instance.characteristicRead.listen(
(eventArgs) async { (eventArgs) async {
final central = eventArgs.central; final central = eventArgs.central;
final characteristic = eventArgs.characteristic; final characteristic = eventArgs.characteristic;
final id = eventArgs.id; final value = eventArgs.value;
final offset = eventArgs.offset;
final log = Log( final log = Log(
LogType.read, LogType.read,
Uint8List.fromList([]), value,
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', 'central: ${central.uuid}; characteristic: ${characteristic.uuid}',
); );
logs.value = [ logs.value = [
...logs.value, ...logs.value,
log, log,
]; ];
// final maximumWriteLength = peripheralManager.getMaximumWriteLength(
// central,
// );
const status = true;
final value = Uint8List.fromList([0x01, 0x02, 0x03]);
await peripheralManager.sendReadCharacteristicReply(
central,
characteristic: characteristic,
id: id,
offset: offset,
status: status,
value: value,
);
}, },
); );
writeCharacteristicCommandReceivedSubscription = characteristicWrittenSubscription =
peripheralManager.writeCharacteristicCommandReceived.listen( PeripheralManager.instance.characteristicWritten.listen(
(eventArgs) async { (eventArgs) async {
final central = eventArgs.central; final central = eventArgs.central;
final characteristic = eventArgs.characteristic; final characteristic = eventArgs.characteristic;
final id = eventArgs.id;
final offset = eventArgs.offset;
final value = eventArgs.value; final value = eventArgs.value;
final log = Log( final log = Log(
LogType.write, LogType.write,
value, value,
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', 'central: ${central.uuid}; characteristic: ${characteristic.uuid}',
); );
logs.value = [ logs.value = [
...logs.value, ...logs.value,
log, log,
]; ];
const status = true;
await peripheralManager.sendWriteCharacteristicReply(
central,
characteristic: characteristic,
id: id,
offset: offset,
status: status,
);
}, },
); );
notifyCharacteristicCommandReceivedSubscription = characteristicNotifyStateChangedSubscription =
peripheralManager.notifyCharacteristicCommandReceived.listen( PeripheralManager.instance.characteristicNotifyStateChanged.listen(
(eventArgs) async { (eventArgs) async {
final central = eventArgs.central; final central = eventArgs.central;
final characteristic = eventArgs.characteristic; final characteristic = eventArgs.characteristic;
final state = eventArgs.state; final state = eventArgs.state;
final log = Log( final log = Log(
LogType.write, LogType.notify,
Uint8List.fromList([]), Uint8List.fromList([]),
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state', 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state',
); );
@ -918,15 +915,21 @@ class _AdvertiserViewState extends State<AdvertiserView>
]; ];
// Write someting to the central when notify started. // Write someting to the central when notify started.
if (state) { if (state) {
final value = Uint8List.fromList([0x03, 0x02, 0x01]); final elements = List.generate(2000, (i) => i % 256);
await peripheralManager.notifyCharacteristicValueChanged( final value = Uint8List.fromList(elements);
central, await PeripheralManager.instance.writeCharacteristic(
characteristic: characteristic, characteristic,
value: value, value: value,
central: central,
); );
} }
}, },
); );
_initialize();
}
void _initialize() async {
state.value = await PeripheralManager.instance.getState();
} }
@override @override
@ -970,7 +973,9 @@ class _AdvertiserViewState extends State<AdvertiserView>
} }
Future<void> startAdvertising() async { Future<void> startAdvertising() async {
await peripheralManager.clearServices(); await PeripheralManager.instance.clearServices();
final elements = List.generate(1000, (i) => i % 256);
final value = Uint8List.fromList(elements);
final service = GattService( final service = GattService(
uuid: UUID.short(100), uuid: UUID.short(100),
characteristics: [ characteristics: [
@ -979,15 +984,16 @@ class _AdvertiserViewState extends State<AdvertiserView>
properties: [ properties: [
GattCharacteristicProperty.read, GattCharacteristicProperty.read,
], ],
value: value,
descriptors: [], descriptors: [],
), ),
GattCharacteristic( GattCharacteristic(
uuid: UUID.short(201), uuid: UUID.short(201),
properties: [ properties: [
GattCharacteristicProperty.read,
GattCharacteristicProperty.write, GattCharacteristicProperty.write,
GattCharacteristicProperty.writeWithoutResponse, GattCharacteristicProperty.writeWithoutResponse,
], ],
value: Uint8List.fromList([]),
descriptors: [], descriptors: [],
), ),
GattCharacteristic( GattCharacteristic(
@ -996,24 +1002,41 @@ class _AdvertiserViewState extends State<AdvertiserView>
GattCharacteristicProperty.notify, GattCharacteristicProperty.notify,
GattCharacteristicProperty.indicate, GattCharacteristicProperty.indicate,
], ],
value: Uint8List.fromList([]),
descriptors: [],
),
GattCharacteristic(
uuid: UUID.short(203),
properties: [
GattCharacteristicProperty.notify,
],
value: Uint8List.fromList([]),
descriptors: [],
),
GattCharacteristic(
uuid: UUID.short(204),
properties: [
GattCharacteristicProperty.indicate,
],
value: Uint8List.fromList([]),
descriptors: [], descriptors: [],
), ),
], ],
); );
await peripheralManager.addService(service); await PeripheralManager.instance.addService(service);
final advertisement = Advertisement( final advertisement = Advertisement(
name: 'flutter', name: 'le12138',
manufacturerSpecificData: ManufacturerSpecificData( manufacturerSpecificData: ManufacturerSpecificData(
id: 0x2e19, id: 0x2e19,
data: Uint8List.fromList([0x01, 0x02, 0x03]), data: Uint8List.fromList([0x01, 0x02, 0x03]),
), ),
); );
await peripheralManager.startAdvertising(advertisement); await PeripheralManager.instance.startAdvertising(advertisement);
advertising.value = true; advertising.value = true;
} }
Future<void> stopAdvertising() async { Future<void> stopAdvertising() async {
await peripheralManager.stopAdvertising(); await PeripheralManager.instance.stopAdvertising();
advertising.value = false; advertising.value = false;
} }
@ -1074,9 +1097,9 @@ class _AdvertiserViewState extends State<AdvertiserView>
void dispose() { void dispose() {
super.dispose(); super.dispose();
stateChangedSubscription.cancel(); stateChangedSubscription.cancel();
readCharacteristicCommandReceivedSubscription.cancel(); characteristicReadSubscription.cancel();
writeCharacteristicCommandReceivedSubscription.cancel(); characteristicWrittenSubscription.cancel();
notifyCharacteristicCommandReceivedSubscription.cancel(); characteristicNotifyStateChangedSubscription.cancel();
state.dispose(); state.dispose();
advertising.dispose(); advertising.dispose();
logs.dispose(); logs.dispose();

View File

@ -15,15 +15,15 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "4.0.0" version: "5.0.0"
bluetooth_low_energy_platform_interface: bluetooth_low_energy_platform_interface:
dependency: "direct main" dependency: "direct main"
description: description:
name: bluetooth_low_energy_platform_interface name: bluetooth_low_energy_platform_interface
sha256: a01819f4ef89d033edaa979465ec8c3af13b2618dc718d90fe681be91b6c4356 sha256: "54f92ab2d7746fb6f2b4a40a48cd7eb8e3bf772f3ee89e1979d4d7b741fb2a05"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "5.0.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -52,10 +52,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.2" version: "1.18.0"
convert: convert:
dependency: "direct main" dependency: "direct main"
description: description:
@ -102,10 +102,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.1"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -125,10 +125,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: intl name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.18.1" version: "0.19.0"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -173,10 +173,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.10.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -189,18 +189,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.1.2"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.6" version: "2.1.7"
process: process:
dependency: transitive dependency: transitive
description: description:
@ -226,18 +226,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.11.1"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
@ -266,10 +266,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.6.1"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -290,18 +290,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.7.1" version: "11.10.0"
web: web:
dependency: transitive dependency: transitive
description: description:
name: web name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.4-beta" version: "0.3.0"
webdriver: webdriver:
dependency: transitive dependency: transitive
description: description:
@ -311,5 +311,5 @@ packages:
source: hosted source: hosted
version: "3.0.2" version: "3.0.2"
sdks: sdks:
dart: ">=3.1.0-185.0.dev <4.0.0" dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.3.0" flutter: ">=3.3.0"

View File

@ -17,7 +17,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
bluetooth_low_energy_platform_interface: ^4.0.0 bluetooth_low_energy_platform_interface: ^5.0.0
bluetooth_low_energy_android: bluetooth_low_energy_android:
# When depending on this package from a real application you should use: # When depending on this package from a real application you should use:
# bluetooth_low_energy: ^x.y.z # bluetooth_low_energy: ^x.y.z
@ -30,7 +30,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
convert: ^3.1.1 convert: ^3.1.1
intl: ^0.18.1 intl: ^0.19.0
dev_dependencies: dev_dependencies:
integration_test: integration_test:

View File

@ -1,11 +1,11 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'src/my_central_manager2.dart'; import 'src/my_central_manager.dart';
import 'src/my_peripheral_manager2.dart'; import 'src/my_peripheral_manager.dart';
abstract class BluetoothLowEnergyAndroid { abstract class BluetoothLowEnergyAndroid {
static void registerWith() { static void registerWith() {
MyCentralManager.instance = MyCentralManager2(); CentralManager.instance = MyCentralManager();
MyPeripheralManager.instance = MyPeripheralManager2(); PeripheralManager.instance = MyPeripheralManager();
} }
} }

View File

@ -3,28 +3,47 @@ import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_api.g.dart'; import 'my_api.g.dart';
import 'my_central2.dart';
import 'my_gatt_characteristic2.dart'; import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart'; import 'my_gatt_descriptor2.dart';
import 'my_gatt_service2.dart'; import 'my_gatt_service2.dart';
import 'my_peripheral2.dart';
export 'my_api.g.dart'; export 'my_api.g.dart';
// ToObject
extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs {
BluetoothLowEnergyState toState() { BluetoothLowEnergyState toState() {
return BluetoothLowEnergyState.values[index]; return BluetoothLowEnergyState.values[index];
} }
} }
extension MyGattCharacteristicPropertyArgsX
on MyGattCharacteristicPropertyArgs {
GattCharacteristicProperty toProperty() {
return GattCharacteristicProperty.values[index];
}
}
extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs {
ManufacturerSpecificData toManufacturerSpecificData() {
final id = idArgs;
final data = dataArgs;
return ManufacturerSpecificData(
id: id,
data: data,
);
}
}
extension MyAdvertisementArgsX on MyAdvertisementArgs { extension MyAdvertisementArgsX on MyAdvertisementArgs {
Advertisement toAdvertisement() { Advertisement toAdvertisement() {
final name = nameArgs; final name = nameArgs;
final serviceUUIDs = serviceUUIDsArgs final serviceUUIDs =
.cast<String>() serviceUUIDsArgs.cast<String>().map((args) => args.toUUID()).toList();
.map((args) => UUID.fromString(args))
.toList();
final serviceData = serviceDataArgs.cast<String, Uint8List>().map( final serviceData = serviceDataArgs.cast<String, Uint8List>().map(
(uuidArgs, dataArgs) { (uuidArgs, dataArgs) {
final uuid = UUID.fromString(uuidArgs); final uuid = uuidArgs.toUUID();
final data = dataArgs; final data = dataArgs;
return MapEntry(uuid, data); return MapEntry(uuid, data);
}, },
@ -40,61 +59,38 @@ extension MyAdvertisementArgsX on MyAdvertisementArgs {
} }
} }
extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs { extension MyCentralArgsX on MyCentralArgs {
ManufacturerSpecificData toManufacturerSpecificData() { MyCentral2 toCentral() {
final id = idArgs; return MyCentral2(
final data = dataArgs; address: addressArgs,
return ManufacturerSpecificData(
id: id,
data: data,
); );
} }
} }
extension MyGattCharacteristicPropertyArgsX
on MyGattCharacteristicPropertyArgs {
GattCharacteristicProperty toProperty() {
return GattCharacteristicProperty.values[index];
}
}
extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType {
MyGattCharacteristicWriteTypeArgs toArgs() {
return MyGattCharacteristicWriteTypeArgs.values[index];
}
}
extension MyPeripheralArgsX on MyPeripheralArgs { extension MyPeripheralArgsX on MyPeripheralArgs {
MyPeripheral toPeripheral() { MyPeripheral2 toPeripheral() {
final hashCode = hashCodeArgs; return MyPeripheral2(
final uuid = UUID.fromString(uuidArgs); address: addressArgs,
return MyPeripheral(
hashCode: hashCode,
uuid: uuid,
); );
} }
} }
extension MyGattServiceArgsX on MyGattServiceArgs { extension MyGattDescriptorArgsX on MyGattDescriptorArgs {
MyGattService2 toService2() { MyGattDescriptor2 toDescriptor2(MyPeripheral2 peripheral) {
final hashCode = hashCodeArgs; final hashCode = hashCodeArgs;
final uuid = UUID.fromString(uuidArgs); final uuid = uuidArgs.toUUID();
final characteristics = characteristicsArgs return MyGattDescriptor2(
.cast<MyGattCharacteristicArgs>() peripheral: peripheral,
.map((args) => args.toCharacteristic2())
.toList();
return MyGattService2(
hashCode: hashCode, hashCode: hashCode,
uuid: uuid, uuid: uuid,
characteristics: characteristics,
); );
} }
} }
extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs { extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs {
MyGattCharacteristic2 toCharacteristic2() { MyGattCharacteristic2 toCharacteristic2(MyPeripheral2 peripheral) {
final hashCode = hashCodeArgs; final hashCode = hashCodeArgs;
final uuid = UUID.fromString(uuidArgs); final uuid = uuidArgs.toUUID();
final properties = propertyNumbersArgs.cast<int>().map( final properties = propertyNumbersArgs.cast<int>().map(
(args) { (args) {
final propertyArgs = MyGattCharacteristicPropertyArgs.values[args]; final propertyArgs = MyGattCharacteristicPropertyArgs.values[args];
@ -103,9 +99,10 @@ extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs {
).toList(); ).toList();
final descriptors = descriptorsArgs final descriptors = descriptorsArgs
.cast<MyGattDescriptorArgs>() .cast<MyGattDescriptorArgs>()
.map((args) => args.toDescriptor2()) .map((args) => args.toDescriptor2(peripheral))
.toList(); .toList();
return MyGattCharacteristic2( return MyGattCharacteristic2(
peripheral: peripheral,
hashCode: hashCode, hashCode: hashCode,
uuid: uuid, uuid: uuid,
properties: properties, properties: properties,
@ -114,45 +111,39 @@ extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs {
} }
} }
extension MyGattDescriptorArgsX on MyGattDescriptorArgs { extension MyGattServiceArgsX on MyGattServiceArgs {
MyGattDescriptor2 toDescriptor2() { MyGattService2 toService2(MyPeripheral2 peripheral) {
final hashCode = hashCodeArgs; final hashCode = hashCodeArgs;
final uuid = UUID.fromString(uuidArgs); final uuid = uuidArgs.toUUID();
return MyGattDescriptor2( final characteristics = characteristicsArgs
.cast<MyGattCharacteristicArgs>()
.map((args) => args.toCharacteristic2(peripheral))
.toList();
return MyGattService2(
peripheral: peripheral,
hashCode: hashCode, hashCode: hashCode,
uuid: uuid, uuid: uuid,
characteristics: characteristics,
); );
} }
} }
extension MyCentralArgsX on MyCentralArgs { extension MyUuidArgsX on String {
MyCentral toCentral() { UUID toUUID() {
final hashCode = hashCodeArgs; return UUID.fromString(this);
final uuid = UUID.fromString(uuidArgs);
return MyCentral(
hashCode: hashCode,
uuid: uuid,
);
} }
} }
extension AdvertisementX on Advertisement { // ToArgs
MyAdvertisementArgs toArgs() { extension GattCharacteristicPropertyX on GattCharacteristicProperty {
final nameArgs = name; MyGattCharacteristicPropertyArgs toArgs() {
final serviceUUIDsArgs = return MyGattCharacteristicPropertyArgs.values[index];
serviceUUIDs.map((uuid) => uuid.toString()).toList(); }
final serviceDataArgs = serviceData.map((uuid, data) { }
final uuidArgs = uuid.toString();
final dataArgs = data; extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType {
return MapEntry(uuidArgs, dataArgs); MyGattCharacteristicWriteTypeArgs toArgs() {
}); return MyGattCharacteristicWriteTypeArgs.values[index];
final manufacturerSpecificDataArgs = manufacturerSpecificData?.toArgs();
return MyAdvertisementArgs(
nameArgs: nameArgs,
serviceUUIDsArgs: serviceUUIDsArgs,
serviceDataArgs: serviceDataArgs,
manufacturerSpecificDataArgs: manufacturerSpecificDataArgs,
);
} }
} }
@ -167,18 +158,34 @@ extension ManufacturerSpecificDataX on ManufacturerSpecificData {
} }
} }
extension MyGattServiceX on MyGattService { extension AdvertisementX on Advertisement {
MyGattServiceArgs toArgs() { MyAdvertisementArgs toArgs() {
final nameArgs = name;
final serviceUUIDsArgs = serviceUUIDs.map((uuid) => uuid.toArgs()).toList();
final serviceDataArgs = serviceData.map((uuid, data) {
final uuidArgs = uuid.toArgs();
final dataArgs = data;
return MapEntry(uuidArgs, dataArgs);
});
final manufacturerSpecificDataArgs = manufacturerSpecificData?.toArgs();
return MyAdvertisementArgs(
nameArgs: nameArgs,
serviceUUIDsArgs: serviceUUIDsArgs,
serviceDataArgs: serviceDataArgs,
manufacturerSpecificDataArgs: manufacturerSpecificDataArgs,
);
}
}
extension MyGattDescriptorX on MyGattDescriptor {
MyGattDescriptorArgs toArgs() {
final hashCodeArgs = hashCode; final hashCodeArgs = hashCode;
final uuidArgs = uuid.toString(); final uuidArgs = uuid.toArgs();
final characteristicsArgs = characteristics final valueArgs = value;
.cast<MyGattCharacteristic>() return MyGattDescriptorArgs(
.map((characteristic) => characteristic.toArgs())
.toList();
return MyGattServiceArgs(
hashCodeArgs: hashCodeArgs, hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs, uuidArgs: uuidArgs,
characteristicsArgs: characteristicsArgs, valueArgs: valueArgs,
); );
} }
} }
@ -186,7 +193,7 @@ extension MyGattServiceX on MyGattService {
extension MyGattCharacteristicX on MyGattCharacteristic { extension MyGattCharacteristicX on MyGattCharacteristic {
MyGattCharacteristicArgs toArgs() { MyGattCharacteristicArgs toArgs() {
final hashCodeArgs = hashCode; final hashCodeArgs = hashCode;
final uuidArgs = uuid.toString(); final uuidArgs = uuid.toArgs();
final propertyNumbersArgs = properties.map((property) { final propertyNumbersArgs = properties.map((property) {
final propertyArgs = property.toArgs(); final propertyArgs = property.toArgs();
return propertyArgs.index; return propertyArgs.index;
@ -204,21 +211,24 @@ extension MyGattCharacteristicX on MyGattCharacteristic {
} }
} }
extension MyGattDescriptorX on MyGattDescriptor { extension MyGattServiceX on MyGattService {
MyGattDescriptorArgs toArgs() { MyGattServiceArgs toArgs() {
final hashCodeArgs = hashCode; final hashCodeArgs = hashCode;
final uuidArgs = uuid.toString(); final uuidArgs = uuid.toArgs();
final valueArgs = value; final characteristicsArgs = characteristics
return MyGattDescriptorArgs( .cast<MyGattCharacteristic>()
.map((characteristic) => characteristic.toArgs())
.toList();
return MyGattServiceArgs(
hashCodeArgs: hashCodeArgs, hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs, uuidArgs: uuidArgs,
valueArgs: valueArgs, characteristicsArgs: characteristicsArgs,
); );
} }
} }
extension GattCharacteristicPropertyX on GattCharacteristicProperty { extension UuidX on UUID {
MyGattCharacteristicPropertyArgs toArgs() { String toArgs() {
return MyGattCharacteristicPropertyArgs.values[index]; return toString();
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
class MyCentral2 extends MyCentral {
final String address;
MyCentral2({
required this.address,
}) : super(
uuid: UUID.fromAddress(address),
);
}

View File

@ -0,0 +1,341 @@
import 'dart:async';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'my_api.dart';
import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart';
import 'my_peripheral2.dart';
class MyCentralManager extends CentralManager
implements MyCentralManagerFlutterApi {
final MyCentralManagerHostApi _api;
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
final StreamController<DiscoveredEventArgs> _discoveredController;
final StreamController<ConnectionStateChangedEventArgs>
_connectionStateChangedController;
final StreamController<GattCharacteristicNotifiedEventArgs>
_characteristicNotifiedController;
final Map<String, MyPeripheral2> _peripherals;
final Map<String, Map<int, MyGattCharacteristic2>> _characteristics;
final Map<String, int> _mtus;
BluetoothLowEnergyState _state;
MyCentralManager()
: _api = MyCentralManagerHostApi(),
_stateChangedController = StreamController.broadcast(),
_discoveredController = StreamController.broadcast(),
_connectionStateChangedController = StreamController.broadcast(),
_characteristicNotifiedController = StreamController.broadcast(),
_peripherals = {},
_characteristics = {},
_mtus = {},
_state = BluetoothLowEnergyState.unknown;
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<DiscoveredEventArgs> get discovered => _discoveredController.stream;
@override
Stream<ConnectionStateChangedEventArgs> get connectionStateChanged =>
_connectionStateChangedController.stream;
@override
Stream<GattCharacteristicNotifiedEventArgs> get characteristicNotified =>
_characteristicNotifiedController.stream;
@override
Future<void> setUp() async {
logger.info('setUp');
await _api.setUp();
MyCentralManagerFlutterApi.setup(this);
}
@override
Future<BluetoothLowEnergyState> getState() {
logger.info('getState');
return Future.value(_state);
}
@override
Future<void> startDiscovery() async {
logger.info('startDiscovery');
await _api.startDiscovery();
}
@override
Future<void> stopDiscovery() async {
logger.info('stopDiscovery');
await _api.stopDiscovery();
}
@override
Future<void> connect(Peripheral peripheral) async {
if (peripheral is! MyPeripheral2) {
throw TypeError();
}
final addressArgs = peripheral.address;
logger.info('connect: $addressArgs');
await _api.connect(addressArgs);
try {
await _api.requestMTU(addressArgs, 517);
} catch (error, stackTrace) {
// 忽略协商 MTU 错误
logger.warning('requstMTU failed.', error, stackTrace);
}
}
@override
Future<void> disconnect(Peripheral peripheral) async {
if (peripheral is! MyPeripheral2) {
throw TypeError();
}
final addressArgs = peripheral.address;
logger.info('disconnect: $addressArgs');
await _api.disconnect(addressArgs);
}
@override
Future<int> readRSSI(Peripheral peripheral) async {
if (peripheral is! MyPeripheral2) {
throw TypeError();
}
final addressArgs = peripheral.address;
logger.info('readRSSI: $addressArgs');
final rssi = await _api.readRSSI(addressArgs);
return rssi;
}
@override
Future<List<GattService>> discoverGATT(Peripheral peripheral) async {
if (peripheral is! MyPeripheral2) {
throw TypeError();
}
final addressArgs = peripheral.address;
logger.info('discoverServices: $addressArgs');
final servicesArgs = await _api.discoverServices(addressArgs);
final services = servicesArgs
.cast<MyGattServiceArgs>()
.map((args) => args.toService2(peripheral))
.toList();
final characteristics =
services.expand((service) => service.characteristics).toList();
_characteristics[addressArgs] = {
for (var characteristic in characteristics)
characteristic.hashCode: characteristic
};
return services;
}
@override
Future<Uint8List> readCharacteristic(
GattCharacteristic characteristic,
) async {
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final peripheral = characteristic.peripheral;
final addressArgs = peripheral.address;
final hashCodeArgs = characteristic.hashCode;
logger.info('readCharacteristic: $addressArgs.$hashCodeArgs');
final value = await _api.readCharacteristic(addressArgs, hashCodeArgs);
return value;
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
required GattCharacteristicWriteType type,
}) async {
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final peripheral = characteristic.peripheral;
final addressArgs = peripheral.address;
final hashCodeArgs = characteristic.hashCode;
final trimmedValueArgs = value.trimGATT();
final typeArgs = type.toArgs();
final typeNumberArgs = typeArgs.index;
// When write without response, fragments the value by MTU - 3 size.
// If mtu is null, use 23 as default MTU size.
if (type == GattCharacteristicWriteType.withResponse) {
logger.info(
'writeCharacteristic: $addressArgs.$hashCodeArgs - $trimmedValueArgs, $typeArgs');
await _api.writeCharacteristic(
addressArgs,
hashCodeArgs,
trimmedValueArgs,
typeNumberArgs,
);
} else {
final mtu = _mtus[addressArgs] ?? 23;
final fragmentSize = (mtu - 3).clamp(20, 512);
var start = 0;
while (start < trimmedValueArgs.length) {
final end = start + fragmentSize;
final fragmentedValueArgs = end < trimmedValueArgs.length
? trimmedValueArgs.sublist(start, end)
: trimmedValueArgs.sublist(start);
logger.info(
'writeCharacteristic: $addressArgs.$hashCodeArgs - $fragmentedValueArgs, $typeArgs');
await _api.writeCharacteristic(
addressArgs,
hashCodeArgs,
fragmentedValueArgs,
typeNumberArgs,
);
start = end;
}
}
}
@override
Future<void> setCharacteristicNotifyState(
GattCharacteristic characteristic, {
required bool state,
}) async {
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final peripheral = characteristic.peripheral;
final addressArgs = peripheral.address;
final hashCodeArgs = characteristic.hashCode;
final stateArgs = state
? characteristic.properties.contains(GattCharacteristicProperty.notify)
? MyGattCharacteristicNotifyStateArgs.notify
: MyGattCharacteristicNotifyStateArgs.indicate
: MyGattCharacteristicNotifyStateArgs.none;
final stateNumberArgs = stateArgs.index;
logger.info(
'setCharacteristicNotifyState: $addressArgs.$hashCodeArgs - $stateArgs');
await _api.setCharacteristicNotifyState(
addressArgs,
hashCodeArgs,
stateNumberArgs,
);
}
@override
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final peripheral = descriptor.peripheral;
final addressArgs = peripheral.address;
final hashCodeArgs = descriptor.hashCode;
logger.info('readDescriptor: $addressArgs.$hashCodeArgs');
final value = await _api.readDescriptor(addressArgs, hashCodeArgs);
return value;
}
@override
Future<void> writeDescriptor(
GattDescriptor descriptor, {
required Uint8List value,
}) async {
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final peripheral = descriptor.peripheral;
final addressArgs = peripheral.address;
final hashCodeArgs = descriptor.hashCode;
final trimmedValueArgs = value.trimGATT();
logger.info(
'writeDescriptor: $addressArgs.$hashCodeArgs - $trimmedValueArgs');
await _api.writeDescriptor(addressArgs, hashCodeArgs, trimmedValueArgs);
}
@override
void onStateChanged(int stateNumberArgs) {
final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs];
logger.info('onStateChanged: $stateArgs');
final state = stateArgs.toState();
if (_state == state) {
return;
}
_state = state;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
}
@override
void onDiscovered(
MyPeripheralArgs peripheralArgs,
int rssiArgs,
MyAdvertisementArgs advertisementArgs,
) {
final addressArgs = peripheralArgs.addressArgs;
logger.info('onDiscovered: $addressArgs - $rssiArgs, $advertisementArgs');
final peripheral = _peripherals.putIfAbsent(
peripheralArgs.addressArgs,
() => peripheralArgs.toPeripheral(),
);
final rssi = rssiArgs;
final advertisement = advertisementArgs.toAdvertisement();
final eventArgs = DiscoveredEventArgs(
peripheral,
rssi,
advertisement,
);
_discoveredController.add(eventArgs);
}
@override
void onConnectionStateChanged(String addressArgs, bool stateArgs) {
logger.info('onConnectionStateChanged: $addressArgs - $stateArgs');
final peripheral = _peripherals[addressArgs];
if (peripheral == null) {
return;
}
final state = stateArgs;
final eventArgs = ConnectionStateChangedEventArgs(peripheral, state);
_connectionStateChangedController.add(eventArgs);
if (!state) {
_characteristics.remove(addressArgs);
_mtus.remove(addressArgs);
}
}
@override
void onMtuChanged(String addressArgs, int mtuArgs) {
logger.info('onMtuChanged: $addressArgs - $mtuArgs');
final mtu = mtuArgs;
_mtus[addressArgs] = mtu;
}
@override
void onCharacteristicNotified(
String addressArgs,
int hashCodeArgs,
Uint8List valueArgs,
) {
logger.info(
'onCharacteristicNotified: $addressArgs.$hashCodeArgs - $valueArgs');
final characteristic = _retrieveCharacteristic(addressArgs, hashCodeArgs);
if (characteristic == null) {
return;
}
final value = valueArgs;
final eventArgs = GattCharacteristicNotifiedEventArgs(
characteristic,
value,
);
_characteristicNotifiedController.add(eventArgs);
}
MyGattCharacteristic2? _retrieveCharacteristic(
String addressArgs,
int hashCodeArgs,
) {
final characteristics = _characteristics[addressArgs];
if (characteristics == null) {
return null;
}
return characteristics[hashCodeArgs];
}
}

View File

@ -1,308 +0,0 @@
import 'dart:async';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'my_api.dart';
import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart';
class MyCentralManager2 extends MyCentralManager
implements MyCentralManagerFlutterApi {
final MyCentralManagerHostApi _api;
BluetoothLowEnergyState _state;
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
final StreamController<DiscoveredEventArgs> _discoveredController;
final StreamController<PeripheralStateChangedEventArgs>
_peripheralStateChangedController;
final StreamController<GattCharacteristicValueChangedEventArgs>
_characteristicValueChangedController;
MyCentralManager2()
: _api = MyCentralManagerHostApi(),
_state = BluetoothLowEnergyState.unknown,
_stateChangedController = StreamController.broadcast(),
_discoveredController = StreamController.broadcast(),
_peripheralStateChangedController = StreamController.broadcast(),
_characteristicValueChangedController = StreamController.broadcast();
@override
BluetoothLowEnergyState get state => _state;
@protected
set state(BluetoothLowEnergyState value) {
if (_state == value) {
return;
}
_state = value;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
}
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<DiscoveredEventArgs> get discovered => _discoveredController.stream;
@override
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
_peripheralStateChangedController.stream;
@override
Stream<GattCharacteristicValueChangedEventArgs>
get characteristicValueChanged =>
_characteristicValueChangedController.stream;
Future<void> _throwWithoutState(BluetoothLowEnergyState state) async {
if (this.state != state) {
throw StateError(
'$state is expected, but current state is ${this.state}.');
}
}
@override
Future<void> setUp() async {
final args = await _api.setUp();
final stateArgs =
MyBluetoothLowEnergyStateArgs.values[args.stateNumberArgs];
state = stateArgs.toState();
MyCentralManagerFlutterApi.setup(this);
}
@override
Future<void> startDiscovery() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.startDiscovery();
}
@override
Future<void> stopDiscovery() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.stopDiscovery();
}
@override
Future<void> connect(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
await _api.connect(peripheralHashCodeArgs);
}
@override
Future<void> disconnect(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
await _api.disconnect(peripheralHashCodeArgs);
}
@override
Future<int> getMaximumWriteLength(
Peripheral peripheral, {
required GattCharacteristicWriteType type,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
final typeArgs = type.toArgs();
final typeNumberArgs = typeArgs.index;
final maximumWriteLength = await _api.getMaximumWriteLength(
peripheralHashCodeArgs,
typeNumberArgs,
);
return maximumWriteLength;
}
@override
Future<int> readRSSI(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
final rssi = await _api.readRSSI(peripheralHashCodeArgs);
return rssi;
}
@override
Future<List<GattService>> discoverGATT(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (peripheral is! MyPeripheral) {
throw TypeError();
}
final peripheralHashCodeArgs = peripheral.hashCode;
final servicesArgs = await _api.discoverGATT(peripheralHashCodeArgs);
final services = servicesArgs
.cast<MyGattServiceArgs>()
.map((args) => args.toService2())
.toList();
for (var service in services) {
for (var charactersitic in service.characteristics) {
for (var descriptor in charactersitic.descriptors) {
descriptor.characteristic = charactersitic;
}
charactersitic.service = service;
}
service.peripheral = peripheral;
}
try {
// 部分外围设备连接后会触发 onMtuChanged 回调,若在此之前调用协商 MTU 的方法,会在协商完成前返回,
// 此时如果继续调用其他方法(如发现服务)会导致回调无法触发,
// 因此为避免此情况发生,需要延迟到发现服务完成后再协商 MTU。
// TODO: 思考更好的解决方式,可以在连接后立即协商 MTU。
const mtuArgs = 517;
await _api.requestMTU(peripheralHashCodeArgs, mtuArgs);
} catch (error, stackTrace) {
// 忽略协商 MTU 错误
logger.warning('requst MTU failed.', error, stackTrace);
}
return services;
}
@override
Future<Uint8List> readCharacteristic(
GattCharacteristic characteristic,
) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final characteristcHashCodeArgs = characteristic.hashCode;
final value = await _api.readCharacteristic(
peripheralHashCodeArgs,
characteristcHashCodeArgs,
);
return value;
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
required GattCharacteristicWriteType type,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final characteristcHashCodeArgs = characteristic.hashCode;
final valueArgs = value;
final typeArgs = type.toArgs();
final typeNumberArgs = typeArgs.index;
await _api.writeCharacteristic(
peripheralHashCodeArgs,
characteristcHashCodeArgs,
valueArgs,
typeNumberArgs,
);
}
@override
Future<void> notifyCharacteristic(
GattCharacteristic characteristic, {
required bool state,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final characteristcHashCodeArgs = characteristic.hashCode;
final stateArgs = state;
await _api.notifyCharacteristic(
peripheralHashCodeArgs,
characteristcHashCodeArgs,
stateArgs,
);
}
@override
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final characteristic = descriptor.characteristic;
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final descriptorHashCodeArgs = descriptor.hashCode;
final value = await _api.readDescriptor(
peripheralHashCodeArgs,
descriptorHashCodeArgs,
);
return value;
}
@override
Future<void> writeDescriptor(
GattDescriptor descriptor, {
required Uint8List value,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final characteristic = descriptor.characteristic;
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final descriptorHashCodeArgs = descriptor.hashCode;
final valueArgs = value;
await _api.writeDescriptor(
peripheralHashCodeArgs,
descriptorHashCodeArgs,
valueArgs,
);
}
@override
void onStateChanged(int stateNumberArgs) {
final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs];
state = stateArgs.toState();
}
@override
void onDiscovered(
MyPeripheralArgs peripheralArgs,
int rssiArgs,
MyAdvertisementArgs advertisementArgs,
) {
final peripheral = peripheralArgs.toPeripheral();
final rssi = rssiArgs;
final advertisement = advertisementArgs.toAdvertisement();
final eventArgs = DiscoveredEventArgs(
peripheral,
rssi,
advertisement,
);
_discoveredController.add(eventArgs);
}
@override
void onPeripheralStateChanged(
MyPeripheralArgs peripheralArgs,
bool stateArgs,
) {
final peripheral = peripheralArgs.toPeripheral();
final state = stateArgs;
final eventArgs = PeripheralStateChangedEventArgs(peripheral, state);
_peripheralStateChangedController.add(eventArgs);
}
@override
void onCharacteristicValueChanged(
MyGattCharacteristicArgs characteristicArgs,
Uint8List valueArgs,
) {
final characteristic = characteristicArgs.toCharacteristic2();
final value = valueArgs;
final eventArgs = GattCharacteristicValueChangedEventArgs(
characteristic,
value,
);
_characteristicValueChangedController.add(eventArgs);
}
}

View File

@ -1,13 +1,16 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_gatt_descriptor2.dart'; import 'my_gatt_descriptor2.dart';
import 'my_gatt_service2.dart'; import 'my_peripheral2.dart';
class MyGattCharacteristic2 extends MyGattCharacteristic { class MyGattCharacteristic2 extends MyGattCharacteristic {
late final MyGattService2 service; final MyPeripheral2 peripheral;
@override
final int hashCode;
MyGattCharacteristic2({ MyGattCharacteristic2({
super.hashCode, required this.peripheral,
required this.hashCode,
required super.uuid, required super.uuid,
required super.properties, required super.properties,
required List<MyGattDescriptor2> descriptors, required List<MyGattDescriptor2> descriptors,
@ -16,4 +19,11 @@ class MyGattCharacteristic2 extends MyGattCharacteristic {
@override @override
List<MyGattDescriptor2> get descriptors => List<MyGattDescriptor2> get descriptors =>
super.descriptors.cast<MyGattDescriptor2>(); super.descriptors.cast<MyGattDescriptor2>();
@override
bool operator ==(Object other) {
return other is MyGattCharacteristic2 &&
other.peripheral == peripheral &&
other.hashCode == hashCode;
}
} }

View File

@ -1,12 +1,22 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_gatt_characteristic2.dart'; import 'my_peripheral2.dart';
class MyGattDescriptor2 extends MyGattDescriptor { class MyGattDescriptor2 extends MyGattDescriptor {
late final MyGattCharacteristic2 characteristic; final MyPeripheral2 peripheral;
@override
final int hashCode;
MyGattDescriptor2({ MyGattDescriptor2({
super.hashCode, required this.peripheral,
required this.hashCode,
required super.uuid, required super.uuid,
}); });
@override
bool operator ==(Object other) {
return other is MyGattDescriptor2 &&
other.peripheral == peripheral &&
other.hashCode == hashCode;
}
} }

View File

@ -1,12 +1,16 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_gatt_characteristic2.dart'; import 'my_gatt_characteristic2.dart';
import 'my_peripheral2.dart';
class MyGattService2 extends MyGattService { class MyGattService2 extends MyGattService {
late final MyPeripheral peripheral; final MyPeripheral2 peripheral;
@override
final int hashCode;
MyGattService2({ MyGattService2({
super.hashCode, required this.peripheral,
required this.hashCode,
required super.uuid, required super.uuid,
required List<MyGattCharacteristic2> characteristics, required List<MyGattCharacteristic2> characteristics,
}) : super(characteristics: characteristics); }) : super(characteristics: characteristics);
@ -14,4 +18,11 @@ class MyGattService2 extends MyGattService {
@override @override
List<MyGattCharacteristic2> get characteristics => List<MyGattCharacteristic2> get characteristics =>
super.characteristics.cast<MyGattCharacteristic2>(); super.characteristics.cast<MyGattCharacteristic2>();
@override
bool operator ==(Object other) {
return other is MyGattService2 &&
other.peripheral == peripheral &&
other.hashCode == hashCode;
}
} }

View File

@ -0,0 +1,11 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
class MyPeripheral2 extends MyPeripheral {
final String address;
MyPeripheral2({
required this.address,
}) : super(
uuid: UUID.fromAddress(address),
);
}

View File

@ -0,0 +1,413 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'my_api.dart';
import 'my_central2.dart';
class MyPeripheralManager extends PeripheralManager
implements MyPeripheralManagerFlutterApi {
final MyPeripheralManagerHostApi _api;
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
final StreamController<GattCharacteristicReadEventArgs>
_characteristicReadController;
final StreamController<GattCharacteristicWrittenEventArgs>
_characteristicWrittenController;
final StreamController<GattCharacteristicNotifyStateChangedEventArgs>
_characteristicNotifyStateChangedController;
final Map<String, MyCentral2> _centrals;
final Map<int, Map<int, MyGattCharacteristic>> _characteristics;
final Map<String, int> _mtus;
final Map<String, Map<int, bool>> _confirms;
final Map<String, MyGattCharacteristic> _preparedCharacteristics;
final Map<String, List<int>> _preparedValue;
BluetoothLowEnergyState _state;
MyPeripheralManager()
: _api = MyPeripheralManagerHostApi(),
_stateChangedController = StreamController.broadcast(),
_characteristicReadController = StreamController.broadcast(),
_characteristicWrittenController = StreamController.broadcast(),
_characteristicNotifyStateChangedController =
StreamController.broadcast(),
_centrals = {},
_characteristics = {},
_mtus = {},
_confirms = {},
_preparedCharacteristics = {},
_preparedValue = {},
_state = BluetoothLowEnergyState.unknown;
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<GattCharacteristicReadEventArgs> get characteristicRead =>
_characteristicReadController.stream;
@override
Stream<GattCharacteristicWrittenEventArgs> get characteristicWritten =>
_characteristicWrittenController.stream;
@override
Stream<GattCharacteristicNotifyStateChangedEventArgs>
get characteristicNotifyStateChanged =>
_characteristicNotifyStateChangedController.stream;
@override
Future<void> setUp() async {
logger.info('setUp');
await _api.setUp();
MyPeripheralManagerFlutterApi.setup(this);
}
@override
Future<BluetoothLowEnergyState> getState() {
logger.info('getState');
return Future.value(_state);
}
@override
Future<void> addService(GattService service) async {
if (service is! MyGattService) {
throw TypeError();
}
final serviceArgs = service.toArgs();
final hashCodeArgs = serviceArgs.hashCodeArgs;
logger.info('addService: $hashCodeArgs');
await _api.addService(serviceArgs);
_characteristics[hashCodeArgs] = {
for (var characteristics in service.characteristics)
characteristics.hashCode: characteristics
};
}
@override
Future<void> removeService(GattService service) async {
final hashCodeArgs = service.hashCode;
logger.info('removeService: $hashCodeArgs');
await _api.removeService(hashCodeArgs);
_characteristics.remove(hashCodeArgs);
}
@override
Future<void> clearServices() async {
logger.info('clearServices');
await _api.clearServices();
_characteristics.clear();
}
@override
Future<void> startAdvertising(Advertisement advertisement) async {
final advertisementArgs = advertisement.toArgs();
logger.info('startAdvertising: $advertisementArgs');
await _api.startAdvertising(advertisementArgs);
}
@override
Future<void> stopAdvertising() async {
logger.info('stopAdvertising');
await _api.stopAdvertising();
}
@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);
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
Central? central,
}) async {
if (characteristic is! MyGattCharacteristic) {
throw TypeError();
}
characteristic.value = value;
if (central == null) {
return;
}
if (central is! MyCentral2) {
throw TypeError();
}
final addressArgs = central.address;
final hashCodeArgs = characteristic.hashCode;
final confirm = _retrieveConfirm(addressArgs, hashCodeArgs);
if (confirm == null) {
logger.warning('The central is not listening.');
return;
}
final trimmedValueArgs = characteristic.value;
// Fragments the value by MTU - 3 size.
// If mtu is null, use 23 as default MTU size.
final mtu = _mtus[addressArgs] ?? 23;
final fragmentSize = (mtu - 3).clamp(20, 512);
var start = 0;
while (start < trimmedValueArgs.length) {
final end = start + fragmentSize;
final fragmentedValueArgs = end < trimmedValueArgs.length
? trimmedValueArgs.sublist(start, end)
: trimmedValueArgs.sublist(start);
logger.info(
'notifyCharacteristicChanged: $hashCodeArgs - $fragmentedValueArgs, $confirm, $addressArgs');
await _api.notifyCharacteristicChanged(
hashCodeArgs,
fragmentedValueArgs,
confirm,
addressArgs,
);
start = end;
}
}
@override
void onStateChanged(int stateNumberArgs) {
final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs];
logger.info('onStateChanged: $stateArgs');
final state = stateArgs.toState();
if (_state == state) {
return;
}
_state = state;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
}
@override
void onConnectionStateChanged(MyCentralArgs centralArgs, bool stateArgs) {
final addressArgs = centralArgs.addressArgs;
logger.info('onConnectionStateChanged: $addressArgs - $stateArgs');
final central = centralArgs.toCentral();
final state = stateArgs;
if (state) {
_centrals[addressArgs] = central;
} else {
_centrals.remove(addressArgs);
_mtus.remove(addressArgs);
_confirms.remove(addressArgs);
}
}
@override
void onMtuChanged(String addressArgs, int mtuArgs) {
logger.info('onMtuChanged: $addressArgs - $mtuArgs');
final mtu = mtuArgs;
_mtus[addressArgs] = mtu;
}
@override
void onCharacteristicReadRequest(
String addressArgs,
int hashCodeArgs,
int idArgs,
int offsetArgs,
) async {
logger.info(
'onCharacteristicReadRequest: $addressArgs.$hashCodeArgs - $idArgs, $offsetArgs');
final central = _centrals[addressArgs];
if (central == null) {
return;
}
final characteristic = _retrieveCharacteristic(hashCodeArgs);
if (characteristic == null) {
return;
}
const statusArgs = MyGattStatusArgs.success;
final offset = offsetArgs;
final valueArgs = _onCharacteristicRead(central, characteristic, offset);
await _trySendResponse(
addressArgs,
idArgs,
statusArgs,
offsetArgs,
valueArgs,
);
}
@override
void onCharacteristicWriteRequest(
String addressArgs,
int hashCodeArgs,
int idArgs,
int offsetArgs,
Uint8List valueArgs,
bool preparedWrite,
bool responseNeeded,
) async {
logger.info(
'onCharacteristicWriteRequest: $addressArgs.$hashCodeArgs - $idArgs, $offsetArgs, $valueArgs, $preparedWrite, $responseNeeded');
final central = _centrals[addressArgs];
if (central == null) {
return;
}
final characteristic = _retrieveCharacteristic(hashCodeArgs);
if (characteristic == null) {
return;
}
final MyGattStatusArgs statusArgs;
if (preparedWrite) {
final preparedCharacteristic = _preparedCharacteristics[addressArgs];
if (preparedCharacteristic != null &&
preparedCharacteristic != characteristic) {
statusArgs = MyGattStatusArgs.connectionCongested;
} else {
final preparedValueArgs = _preparedValue[addressArgs];
if (preparedValueArgs == null) {
_preparedCharacteristics[addressArgs] = characteristic;
// Change the immutable Uint8List to mutable.
_preparedValue[addressArgs] = [...valueArgs];
} else {
preparedValueArgs.insertAll(offsetArgs, valueArgs);
}
statusArgs = MyGattStatusArgs.success;
}
} else {
final value = valueArgs;
_onCharacteristicWritten(central, characteristic, value);
statusArgs = MyGattStatusArgs.success;
}
if (responseNeeded) {
await _trySendResponse(
addressArgs,
idArgs,
statusArgs,
offsetArgs,
null,
);
}
}
@override
void onExecuteWrite(String addressArgs, int idArgs, bool executeArgs) async {
logger.info('onExecuteWrite: $addressArgs - $idArgs, $executeArgs');
final central = _centrals[addressArgs];
final characteristic = _preparedCharacteristics.remove(addressArgs);
final elements = _preparedValue.remove(addressArgs);
if (central == null || characteristic == null || elements == null) {
return;
}
final value = Uint8List.fromList(elements);
final execute = executeArgs;
if (execute) {
_onCharacteristicWritten(central, characteristic, value);
}
await _trySendResponse(
addressArgs,
idArgs,
MyGattStatusArgs.success,
0,
null,
);
}
@override
void onCharacteristicNotifyStateChanged(
String addressArgs,
int hashCodeArgs,
int stateNumberArgs,
) {
final stateArgs =
MyGattCharacteristicNotifyStateArgs.values[stateNumberArgs];
logger.info(
'onCharacteristicNotifyStateChanged: $addressArgs.$hashCodeArgs - $stateArgs');
final central = _centrals[addressArgs];
if (central == null) {
return;
}
final characteristic = _retrieveCharacteristic(hashCodeArgs);
if (characteristic == null) {
return;
}
final state = stateArgs != MyGattCharacteristicNotifyStateArgs.none;
final confirms = _confirms.putIfAbsent(addressArgs, () => {});
if (state) {
confirms[hashCodeArgs] =
stateArgs == MyGattCharacteristicNotifyStateArgs.indicate;
} else {
confirms.remove(hashCodeArgs);
}
final eventArgs = GattCharacteristicNotifyStateChangedEventArgs(
central,
characteristic,
state,
);
_characteristicNotifyStateChangedController.add(eventArgs);
}
MyGattCharacteristic? _retrieveCharacteristic(int hashCodeArgs) {
final characteristics = _characteristics.values
.reduce((value, element) => value..addAll(element));
return characteristics[hashCodeArgs];
}
bool? _retrieveConfirm(String addressArgs, int hashCodeArgs) {
final confirms = _confirms[addressArgs];
if (confirms == null) {
return null;
}
return confirms[hashCodeArgs];
}
Future<void> _trySendResponse(
String addressArgs,
int idArgs,
MyGattStatusArgs statusArgs,
int offsetArgs,
Uint8List? valueArgs,
) async {
final statusNumberArgs = statusArgs.index;
try {
_api.sendResponse(
addressArgs,
idArgs,
statusNumberArgs,
offsetArgs,
valueArgs,
);
} catch (e, stack) {
logger.shout('Send response failed.', e, stack);
}
}
Uint8List _onCharacteristicRead(
MyCentral central,
MyGattCharacteristic characteristic,
int offset,
) {
final value = characteristic.value;
final trimmedValue = value.sublist(offset);
if (offset == 0) {
final eventArgs = GattCharacteristicReadEventArgs(
central,
characteristic,
value,
);
_characteristicReadController.add(eventArgs);
}
return trimmedValue;
}
void _onCharacteristicWritten(
MyCentral central,
MyGattCharacteristic characteristic,
Uint8List value,
) async {
final trimmedValue = value.trimGATT();
final eventArgs = GattCharacteristicWrittenEventArgs(
central,
characteristic,
trimmedValue,
);
_characteristicWrittenController.add(eventArgs);
}
}

View File

@ -1,252 +0,0 @@
import 'dart:async';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'my_api.dart';
class MyPeripheralManager2 extends MyPeripheralManager
implements MyPeripheralManagerFlutterApi {
final MyPeripheralManagerHostApi _api;
BluetoothLowEnergyState _state;
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
final StreamController<ReadGattCharacteristicCommandEventArgs>
_readCharacteristicCommandReceivedController;
final StreamController<WriteGattCharacteristicCommandEventArgs>
_writeCharacteristicCommandReceivedController;
final StreamController<NotifyGattCharacteristicCommandEventArgs>
_notifyCharacteristicCommandReceivedController;
MyPeripheralManager2()
: _api = MyPeripheralManagerHostApi(),
_state = BluetoothLowEnergyState.unknown,
_stateChangedController = StreamController.broadcast(),
_readCharacteristicCommandReceivedController =
StreamController.broadcast(),
_writeCharacteristicCommandReceivedController =
StreamController.broadcast(),
_notifyCharacteristicCommandReceivedController =
StreamController.broadcast();
@override
BluetoothLowEnergyState get state => _state;
@protected
set state(BluetoothLowEnergyState value) {
if (_state == value) {
return;
}
_state = value;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
}
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<ReadGattCharacteristicCommandEventArgs>
get readCharacteristicCommandReceived =>
_readCharacteristicCommandReceivedController.stream;
@override
Stream<WriteGattCharacteristicCommandEventArgs>
get writeCharacteristicCommandReceived =>
_writeCharacteristicCommandReceivedController.stream;
@override
Stream<NotifyGattCharacteristicCommandEventArgs>
get notifyCharacteristicCommandReceived =>
_notifyCharacteristicCommandReceivedController.stream;
Future<void> _throwWithoutState(BluetoothLowEnergyState state) async {
if (this.state != state) {
throw StateError(
'$state is expected, but current state is ${this.state}.');
}
}
@override
Future<void> setUp() async {
final args = await _api.setUp();
final stateArgs =
MyBluetoothLowEnergyStateArgs.values[args.stateNumberArgs];
state = stateArgs.toState();
MyPeripheralManagerFlutterApi.setup(this);
}
@override
Future<void> addService(GattService service) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (service is! MyGattService) {
throw TypeError();
}
final serviceArgs = service.toArgs();
await _api.addService(serviceArgs);
}
@override
Future<void> removeService(GattService service) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final serviceHashCodeArgs = service.hashCode;
await _api.removeService(serviceHashCodeArgs);
}
@override
Future<void> clearServices() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.clearServices();
}
@override
Future<void> startAdvertising(Advertisement advertisement) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final advertisementArgs = advertisement.toArgs();
await _api.startAdvertising(advertisementArgs);
}
@override
Future<void> stopAdvertising() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.stopAdvertising();
}
@override
Future<int> getMaximumWriteLength(Central central) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final maximumWriteLength =
await _api.getMaximumWriteLength(centralHashCodeArgs);
return maximumWriteLength;
}
@override
Future<void> sendReadCharacteristicReply(
Central central, {
required GattCharacteristic characteristic,
required int id,
required int offset,
required bool status,
required Uint8List value,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final idArgs = id;
final offsetArgs = offset;
final statusArgs = status;
final valueArgs = value;
await _api.sendReadCharacteristicReply(
centralHashCodeArgs,
characteristicHashCodeArgs,
idArgs,
offsetArgs,
statusArgs,
valueArgs,
);
}
@override
Future<void> sendWriteCharacteristicReply(
Central central, {
required GattCharacteristic characteristic,
required int id,
required int offset,
required bool status,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final idArgs = id;
final offsetArgs = offset;
final statusArgs = status;
await _api.sendWriteCharacteristicReply(
centralHashCodeArgs,
characteristicHashCodeArgs,
idArgs,
offsetArgs,
statusArgs,
);
}
@override
Future<void> notifyCharacteristicValueChanged(
Central central, {
required GattCharacteristic characteristic,
required Uint8List value,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final valueArgs = value;
await _api.notifyCharacteristicValueChanged(
centralHashCodeArgs,
characteristicHashCodeArgs,
valueArgs,
);
}
@override
void onStateChanged(int stateNumberArgs) {
final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs];
state = stateArgs.toState();
}
@override
void onReadCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
) {
final central = centralArgs.toCentral();
final characteristic = characteristicArgs.toCharacteristic2();
final id = idArgs;
final offset = offsetArgs;
final eventArgs = ReadGattCharacteristicCommandEventArgs(
central,
characteristic,
id,
offset,
);
_readCharacteristicCommandReceivedController.add(eventArgs);
}
@override
void onWriteCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
Uint8List valueArgs,
) {
final central = centralArgs.toCentral();
final characteristic = characteristicArgs.toCharacteristic2();
final id = idArgs;
final offset = offsetArgs;
final value = valueArgs;
final eventArgs = WriteGattCharacteristicCommandEventArgs(
central,
characteristic,
id,
offset,
value,
);
_writeCharacteristicCommandReceivedController.add(eventArgs);
}
@override
void onNotifyCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
bool stateArgs,
) {
final central = centralArgs.toCentral();
final characteristic = characteristicArgs.toCharacteristic2();
final state = stateArgs;
final eventArgs = NotifyGattCharacteristicCommandEventArgs(
central,
characteristic,
state,
);
_notifyCharacteristicCommandReceivedController.add(eventArgs);
}
}

View File

@ -11,216 +11,6 @@ import 'package:pigeon/pigeon.dart';
), ),
), ),
) )
@HostApi()
abstract class MyCentralManagerHostApi {
@async
MyCentralManagerArgs setUp();
@async
void startDiscovery();
void stopDiscovery();
@async
void connect(int peripheralHashCodeArgs);
@async
void disconnect(int peripheralHashCodeArgs);
int getMaximumWriteLength(int peripheralHashCodeArgs, int typeNumberArgs);
@async
int readRSSI(int peripheralHashCodeArgs);
@async
List<MyGattServiceArgs> discoverGATT(int peripheralHashCodeArgs);
@async
int requestMTU(int peripheralHashCodeArgs, int mtuArgs);
@async
Uint8List readCharacteristic(
int peripheralHashCodeArgs,
int characteristicHashCodeArgs,
);
@async
void writeCharacteristic(
int peripheralHashCodeArgs,
int characteristicHashCodeArgs,
Uint8List valueArgs,
int typeNumberArgs,
);
@async
void notifyCharacteristic(
int peripheralHashCodeArgs,
int characteristicHashCodeArgs,
bool stateArgs,
);
@async
Uint8List readDescriptor(
int peripheralHashCodeArgs,
int descriptorHashCodeArgs,
);
@async
void writeDescriptor(
int peripheralHashCodeArgs,
int descriptorHashCodeArgs,
Uint8List valueArgs,
);
}
@FlutterApi()
abstract class MyCentralManagerFlutterApi {
void onStateChanged(int stateNumberArgs);
void onDiscovered(
MyPeripheralArgs peripheralArgs,
int rssiArgs,
MyAdvertisementArgs advertisementArgs,
);
void onPeripheralStateChanged(
MyPeripheralArgs peripheralArgs,
bool stateArgs,
);
void onCharacteristicValueChanged(
MyGattCharacteristicArgs characteristicArgs,
Uint8List valueArgs,
);
}
@HostApi()
abstract class MyPeripheralManagerHostApi {
@async
MyPeripheralManagerArgs setUp();
@async
void addService(MyGattServiceArgs serviceArgs);
void removeService(int serviceHashCodeArgs);
void clearServices();
@async
void startAdvertising(MyAdvertisementArgs advertisementArgs);
void stopAdvertising();
int getMaximumWriteLength(int centralHashCodeArgs);
void sendReadCharacteristicReply(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
int idArgs,
int offsetArgs,
bool statusArgs,
Uint8List valueArgs,
);
void sendWriteCharacteristicReply(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
int idArgs,
int offsetArgs,
bool statusArgs,
);
@async
void notifyCharacteristicValueChanged(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
Uint8List valueArgs,
);
}
@FlutterApi()
abstract class MyPeripheralManagerFlutterApi {
void onStateChanged(int stateNumberArgs);
void onReadCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
);
void onWriteCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
Uint8List valueArgs,
);
void onNotifyCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
bool stateArgs,
);
}
class MyCentralManagerArgs {
final int stateNumberArgs;
MyCentralManagerArgs(this.stateNumberArgs);
}
class MyPeripheralManagerArgs {
final int stateNumberArgs;
MyPeripheralManagerArgs(this.stateNumberArgs);
}
class MyCentralArgs {
final int hashCodeArgs;
final String uuidArgs;
MyCentralArgs(this.hashCodeArgs, this.uuidArgs);
}
class MyPeripheralArgs {
final int hashCodeArgs;
final String uuidArgs;
MyPeripheralArgs(this.hashCodeArgs, this.uuidArgs);
}
class MyAdvertisementArgs {
final String? nameArgs;
final List<String?> serviceUUIDsArgs;
final Map<String?, Uint8List?> serviceDataArgs;
final MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs;
MyAdvertisementArgs(
this.nameArgs,
this.serviceUUIDsArgs,
this.serviceDataArgs,
this.manufacturerSpecificDataArgs,
);
}
class MyManufacturerSpecificDataArgs {
final int idArgs;
final Uint8List dataArgs;
MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs);
}
class MyGattServiceArgs {
final int hashCodeArgs;
final String uuidArgs;
final List<MyGattCharacteristicArgs?> characteristicsArgs;
MyGattServiceArgs(
this.hashCodeArgs,
this.uuidArgs,
this.characteristicsArgs,
);
}
class MyGattCharacteristicArgs {
final int hashCodeArgs;
final String uuidArgs;
final List<int?> propertyNumbersArgs;
final List<MyGattDescriptorArgs?> descriptorsArgs;
MyGattCharacteristicArgs(
this.hashCodeArgs,
this.uuidArgs,
this.propertyNumbersArgs,
this.descriptorsArgs,
);
}
class MyGattDescriptorArgs {
final int hashCodeArgs;
final String uuidArgs;
final Uint8List? valueArgs;
MyGattDescriptorArgs(
this.hashCodeArgs,
this.uuidArgs,
this.valueArgs,
);
}
enum MyBluetoothLowEnergyStateArgs { enum MyBluetoothLowEnergyStateArgs {
unknown, unknown,
unsupported, unsupported,
@ -238,10 +28,212 @@ enum MyGattCharacteristicPropertyArgs {
} }
enum MyGattCharacteristicWriteTypeArgs { enum MyGattCharacteristicWriteTypeArgs {
// Write with response
withResponse, withResponse,
// Write without response
withoutResponse, withoutResponse,
// Write with response and waiting for confirmation }
// reliable,
enum MyGattCharacteristicNotifyStateArgs {
none,
notify,
indicate,
}
enum MyGattStatusArgs {
success,
readNotPermitted,
writeNotPermitted,
requestNotSupported,
invalidOffset,
insufficientAuthentication,
insufficientEncryption,
invalidAttributeLength,
connectionCongested,
failure,
}
class MyManufacturerSpecificDataArgs {
final int idArgs;
final Uint8List dataArgs;
MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs);
}
class MyAdvertisementArgs {
final String? nameArgs;
final List<String?> serviceUUIDsArgs;
final Map<String?, Uint8List?> serviceDataArgs;
final MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs;
MyAdvertisementArgs(
this.nameArgs,
this.serviceUUIDsArgs,
this.serviceDataArgs,
this.manufacturerSpecificDataArgs,
);
}
class MyCentralArgs {
final String addressArgs;
MyCentralArgs(this.addressArgs);
}
class MyPeripheralArgs {
final String addressArgs;
MyPeripheralArgs(this.addressArgs);
}
class MyGattDescriptorArgs {
final int hashCodeArgs;
final String uuidArgs;
final Uint8List? valueArgs;
MyGattDescriptorArgs(
this.hashCodeArgs,
this.uuidArgs,
this.valueArgs,
);
}
class MyGattCharacteristicArgs {
final int hashCodeArgs;
final String uuidArgs;
final List<int?> propertyNumbersArgs;
final List<MyGattDescriptorArgs?> descriptorsArgs;
MyGattCharacteristicArgs(
this.hashCodeArgs,
this.uuidArgs,
this.propertyNumbersArgs,
this.descriptorsArgs,
);
}
class MyGattServiceArgs {
final int hashCodeArgs;
final String uuidArgs;
final List<MyGattCharacteristicArgs?> characteristicsArgs;
MyGattServiceArgs(
this.hashCodeArgs,
this.uuidArgs,
this.characteristicsArgs,
);
}
@HostApi()
abstract class MyCentralManagerHostApi {
void setUp();
@async
void startDiscovery();
void stopDiscovery();
@async
void connect(String addressArgs);
@async
void disconnect(String addressArgs);
@async
int requestMTU(String addressArgs, int mtuArgs);
@async
int readRSSI(String addressArgs);
@async
List<MyGattServiceArgs> discoverServices(String addressArgs);
@async
Uint8List readCharacteristic(String addressArgs, int hashCodeArgs);
@async
void writeCharacteristic(
String addressArgs,
int hashCodeArgs,
Uint8List valueArgs,
int typeNumberArgs,
);
@async
void setCharacteristicNotifyState(
String addressArgs,
int hashCodeArgs,
int stateNumberArgs,
);
@async
Uint8List readDescriptor(String addressArgs, int hashCodeArgs);
@async
void writeDescriptor(
String addressArgs,
int hashCodeArgs,
Uint8List valueArgs,
);
}
@FlutterApi()
abstract class MyCentralManagerFlutterApi {
void onStateChanged(int stateNumberArgs);
void onDiscovered(
MyPeripheralArgs peripheralArgs,
int rssiArgs,
MyAdvertisementArgs advertisementArgs,
);
void onConnectionStateChanged(String addressArgs, bool stateArgs);
void onMtuChanged(String addressArgs, int mtuArgs);
void onCharacteristicNotified(
String addressArgs,
int hashCodeArgs,
Uint8List valueArgs,
);
}
@HostApi()
abstract class MyPeripheralManagerHostApi {
void setUp();
@async
void addService(MyGattServiceArgs serviceArgs);
void removeService(int hashCodeArgs);
void clearServices();
@async
void startAdvertising(MyAdvertisementArgs advertisementArgs);
void stopAdvertising();
void sendResponse(
String addressArgs,
int idArgs,
int statusNumberArgs,
int offsetArgs,
Uint8List? valueArgs,
);
@async
void notifyCharacteristicChanged(
int hashCodeArgs,
Uint8List valueArgs,
bool confirmArgs,
String addressArgs,
);
}
@FlutterApi()
abstract class MyPeripheralManagerFlutterApi {
void onStateChanged(int stateNumberArgs);
void onConnectionStateChanged(MyCentralArgs centralArgs, bool stateArgs);
void onMtuChanged(String addressArgs, int mtuArgs);
void onCharacteristicReadRequest(
String addressArgs,
int hashCodeArgs,
int idArgs,
int offsetArgs,
);
void onCharacteristicWriteRequest(
String addressArgs,
int hashCodeArgs,
int idArgs,
int offsetArgs,
Uint8List valueArgs,
bool preparedWriteArgs,
bool responseNeededArgs,
);
void onExecuteWrite(
String addressArgs,
int idArgs,
bool executeArgs,
);
void onCharacteristicNotifyStateChanged(
String addressArgs,
int hashCodeArgs,
int stateNumberArgs,
);
} }

View File

@ -1,6 +1,6 @@
name: bluetooth_low_energy_android name: bluetooth_low_energy_android
description: Android implementation of the bluetooth_low_energy plugin. description: Android implementation of the bluetooth_low_energy plugin.
version: 4.0.0 version: 5.0.0
homepage: https://github.com/yanshouwang/bluetooth_low_energy homepage: https://github.com/yanshouwang/bluetooth_low_energy
environment: environment:
@ -10,13 +10,13 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
bluetooth_low_energy_platform_interface: ^4.0.0 bluetooth_low_energy_platform_interface: ^5.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^3.0.0 flutter_lints: ^3.0.0
pigeon: ^12.0.1 pigeon: ^15.0.2
flutter: flutter:
plugin: plugin:

View File

@ -1,3 +1,29 @@
## 5.0.0
* Now `CentralManager#writeCharacteristic` and `PeripheralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes.
* Add `UUID#fromAddress` constructor.
* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`.
* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`.
* Add `PeripheralManager#readCharacteristic`.
* Remove `CentralManager#getMaximumWriteLength` method.
* Remove `PeripheralManager#getMaximumWriteLength` method.
* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`.
* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`.
* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`.
* Move `CentralManager#state` to `CentralManager#getState()`.
* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`.
* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`.
* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`.
* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`.
* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`.
* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#writeCharacteristic`.
* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`.
* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`.
## 5.0.0-dev.1
* Implements new Api.
## 4.0.0 ## 4.0.0
* Remove `BluetoothLowEnergy` class. * Remove `BluetoothLowEnergy` class.

View File

@ -11,15 +11,15 @@ import FlutterMacOS
public class BluetoothLowEnergyDarwin: NSObject, FlutterPlugin { public class BluetoothLowEnergyDarwin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) { public static func register(with registrar: FlutterPluginRegistrar) {
#if os(iOS) #if os(iOS)
let binaryMessenger = registrar.messenger() let messenger = registrar.messenger()
#elseif os(macOS) #elseif os(macOS)
let binaryMessenger = registrar.messenger let messenger = registrar.messenger
#else #else
#error("Unsupported platform.") #error("Unsupported platform.")
#endif #endif
let centralManager = MyCentralManager(binaryMessenger) let centralManager = MyCentralManager(messenger: messenger)
let peripheralManager = MyPeripheralManager(binaryMessenger) let peripheralManager = MyPeripheralManager(messenger: messenger)
MyCentralManagerHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: centralManager) MyCentralManagerHostApiSetup.setUp(binaryMessenger: messenger, api: centralManager)
MyPeripheralManagerHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: peripheralManager) MyPeripheralManagerHostApiSetup.setUp(binaryMessenger: messenger, api: peripheralManager)
} }
} }

View File

@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v12.0.1), do not edit directly. // Autogenerated from Pigeon (v15.0.2), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
import Foundation import Foundation
@ -10,10 +10,6 @@ import FlutterMacOS
#error("Unsupported platform.") #error("Unsupported platform.")
#endif #endif
private func isNullish(_ value: Any?) -> Bool {
return value is NSNull || value == nil
}
private func wrapResult(_ result: Any?) -> [Any?] { private func wrapResult(_ result: Any?) -> [Any?] {
return [result] return [result]
} }
@ -33,6 +29,14 @@ private func wrapError(_ error: Any) -> [Any?] {
] ]
} }
private func createConnectionError(withChannelName channelName: String) -> FlutterError {
return FlutterError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "")
}
private func isNullish(_ value: Any?) -> Bool {
return value is NSNull || value == nil
}
private func nilOrValue<T>(_ value: Any?) -> T? { private func nilOrValue<T>(_ value: Any?) -> T? {
if value is NSNull { return nil } if value is NSNull { return nil }
return value as! T? return value as! T?
@ -40,10 +44,11 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
enum MyBluetoothLowEnergyStateArgs: Int { enum MyBluetoothLowEnergyStateArgs: Int {
case unknown = 0 case unknown = 0
case unsupported = 1 case resetting = 1
case unauthorized = 2 case unsupported = 2
case poweredOff = 3 case unauthorized = 3
case poweredOn = 4 case poweredOff = 4
case poweredOn = 5
} }
enum MyGattCharacteristicPropertyArgs: Int { enum MyGattCharacteristicPropertyArgs: Int {
@ -59,82 +64,45 @@ enum MyGattCharacteristicWriteTypeArgs: Int {
case withoutResponse = 1 case withoutResponse = 1
} }
enum MyGattErrorArgs: Int {
case success = 0
case invalidHandle = 1
case readNotPermitted = 2
case writeNotPermitted = 3
case invalidPDU = 4
case insufficientAuthentication = 5
case requestNotSupported = 6
case invalidOffset = 7
case insufficientAuthorization = 8
case prepareQueueFull = 9
case attributeNotFound = 10
case attributeNotLong = 11
case insufficientEncryptionKeySize = 12
case invalidAttributeValueLength = 13
case unlikelyError = 14
case insufficientEncryption = 15
case unsupportedGroupType = 16
case insufficientResources = 17
}
/// Generated class from Pigeon that represents data sent in messages. /// Generated class from Pigeon that represents data sent in messages.
struct MyCentralManagerArgs { struct MyManufacturerSpecificDataArgs {
var stateNumberArgs: Int64 var idArgs: Int64
var dataArgs: FlutterStandardTypedData
static func fromList(_ list: [Any?]) -> MyCentralManagerArgs? { static func fromList(_ list: [Any?]) -> MyManufacturerSpecificDataArgs? {
let stateNumberArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) let idArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
let dataArgs = list[1] as! FlutterStandardTypedData
return MyCentralManagerArgs( return MyManufacturerSpecificDataArgs(
stateNumberArgs: stateNumberArgs idArgs: idArgs,
dataArgs: dataArgs
) )
} }
func toList() -> [Any?] { func toList() -> [Any?] {
return [ return [
stateNumberArgs, idArgs,
] dataArgs,
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyPeripheralManagerArgs {
var stateNumberArgs: Int64
static func fromList(_ list: [Any?]) -> MyPeripheralManagerArgs? {
let stateNumberArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
return MyPeripheralManagerArgs(
stateNumberArgs: stateNumberArgs
)
}
func toList() -> [Any?] {
return [
stateNumberArgs,
]
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyCentralArgs {
var hashCodeArgs: Int64
var uuidArgs: String
static func fromList(_ list: [Any?]) -> MyCentralArgs? {
let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
let uuidArgs = list[1] as! String
return MyCentralArgs(
hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs
)
}
func toList() -> [Any?] {
return [
hashCodeArgs,
uuidArgs,
]
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyPeripheralArgs {
var hashCodeArgs: Int64
var uuidArgs: String
static func fromList(_ list: [Any?]) -> MyPeripheralArgs? {
let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
let uuidArgs = list[1] as! String
return MyPeripheralArgs(
hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs
)
}
func toList() -> [Any?] {
return [
hashCodeArgs,
uuidArgs,
] ]
} }
} }
@ -173,49 +141,63 @@ struct MyAdvertisementArgs {
} }
/// Generated class from Pigeon that represents data sent in messages. /// Generated class from Pigeon that represents data sent in messages.
struct MyManufacturerSpecificDataArgs { struct MyCentralArgs {
var idArgs: Int64 var uuidArgs: String
var dataArgs: FlutterStandardTypedData
static func fromList(_ list: [Any?]) -> MyManufacturerSpecificDataArgs? { static func fromList(_ list: [Any?]) -> MyCentralArgs? {
let idArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) let uuidArgs = list[0] as! String
let dataArgs = list[1] as! FlutterStandardTypedData
return MyManufacturerSpecificDataArgs( return MyCentralArgs(
idArgs: idArgs, uuidArgs: uuidArgs
dataArgs: dataArgs
) )
} }
func toList() -> [Any?] { func toList() -> [Any?] {
return [ return [
idArgs, uuidArgs,
dataArgs,
] ]
} }
} }
/// Generated class from Pigeon that represents data sent in messages. /// Generated class from Pigeon that represents data sent in messages.
struct MyGattServiceArgs { struct MyPeripheralArgs {
var uuidArgs: String
static func fromList(_ list: [Any?]) -> MyPeripheralArgs? {
let uuidArgs = list[0] as! String
return MyPeripheralArgs(
uuidArgs: uuidArgs
)
}
func toList() -> [Any?] {
return [
uuidArgs,
]
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyGattDescriptorArgs {
var hashCodeArgs: Int64 var hashCodeArgs: Int64
var uuidArgs: String var uuidArgs: String
var characteristicsArgs: [MyGattCharacteristicArgs?] var valueArgs: FlutterStandardTypedData? = nil
static func fromList(_ list: [Any?]) -> MyGattServiceArgs? { static func fromList(_ list: [Any?]) -> MyGattDescriptorArgs? {
let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
let uuidArgs = list[1] as! String let uuidArgs = list[1] as! String
let characteristicsArgs = list[2] as! [MyGattCharacteristicArgs?] let valueArgs: FlutterStandardTypedData? = nilOrValue(list[2])
return MyGattServiceArgs( return MyGattDescriptorArgs(
hashCodeArgs: hashCodeArgs, hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs, uuidArgs: uuidArgs,
characteristicsArgs: characteristicsArgs valueArgs: valueArgs
) )
} }
func toList() -> [Any?] { func toList() -> [Any?] {
return [ return [
hashCodeArgs, hashCodeArgs,
uuidArgs, uuidArgs,
characteristicsArgs, valueArgs,
] ]
} }
} }
@ -251,27 +233,27 @@ struct MyGattCharacteristicArgs {
} }
/// Generated class from Pigeon that represents data sent in messages. /// Generated class from Pigeon that represents data sent in messages.
struct MyGattDescriptorArgs { struct MyGattServiceArgs {
var hashCodeArgs: Int64 var hashCodeArgs: Int64
var uuidArgs: String var uuidArgs: String
var valueArgs: FlutterStandardTypedData? = nil var characteristicsArgs: [MyGattCharacteristicArgs?]
static func fromList(_ list: [Any?]) -> MyGattDescriptorArgs? { static func fromList(_ list: [Any?]) -> MyGattServiceArgs? {
let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32) let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
let uuidArgs = list[1] as! String let uuidArgs = list[1] as! String
let valueArgs: FlutterStandardTypedData? = nilOrValue(list[2]) let characteristicsArgs = list[2] as! [MyGattCharacteristicArgs?]
return MyGattDescriptorArgs( return MyGattServiceArgs(
hashCodeArgs: hashCodeArgs, hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs, uuidArgs: uuidArgs,
valueArgs: valueArgs characteristicsArgs: characteristicsArgs
) )
} }
func toList() -> [Any?] { func toList() -> [Any?] {
return [ return [
hashCodeArgs, hashCodeArgs,
uuidArgs, uuidArgs,
valueArgs, characteristicsArgs,
] ]
} }
} }
@ -280,12 +262,10 @@ private class MyCentralManagerHostApiCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? { override func readValue(ofType type: UInt8) -> Any? {
switch type { switch type {
case 128: case 128:
return MyCentralManagerArgs.fromList(self.readValue() as! [Any?])
case 129:
return MyGattCharacteristicArgs.fromList(self.readValue() as! [Any?]) return MyGattCharacteristicArgs.fromList(self.readValue() as! [Any?])
case 130: case 129:
return MyGattDescriptorArgs.fromList(self.readValue() as! [Any?]) return MyGattDescriptorArgs.fromList(self.readValue() as! [Any?])
case 131: case 130:
return MyGattServiceArgs.fromList(self.readValue() as! [Any?]) return MyGattServiceArgs.fromList(self.readValue() as! [Any?])
default: default:
return super.readValue(ofType: type) return super.readValue(ofType: type)
@ -295,17 +275,14 @@ private class MyCentralManagerHostApiCodecReader: FlutterStandardReader {
private class MyCentralManagerHostApiCodecWriter: FlutterStandardWriter { private class MyCentralManagerHostApiCodecWriter: FlutterStandardWriter {
override func writeValue(_ value: Any) { override func writeValue(_ value: Any) {
if let value = value as? MyCentralManagerArgs { if let value = value as? MyGattCharacteristicArgs {
super.writeByte(128) super.writeByte(128)
super.writeValue(value.toList()) super.writeValue(value.toList())
} else if let value = value as? MyGattCharacteristicArgs { } else if let value = value as? MyGattDescriptorArgs {
super.writeByte(129) super.writeByte(129)
super.writeValue(value.toList()) super.writeValue(value.toList())
} else if let value = value as? MyGattDescriptorArgs {
super.writeByte(130)
super.writeValue(value.toList())
} else if let value = value as? MyGattServiceArgs { } else if let value = value as? MyGattServiceArgs {
super.writeByte(131) super.writeByte(130)
super.writeValue(value.toList()) super.writeValue(value.toList())
} else { } else {
super.writeValue(value) super.writeValue(value)
@ -329,19 +306,21 @@ class MyCentralManagerHostApiCodec: FlutterStandardMessageCodec {
/// Generated protocol from Pigeon that represents a handler of messages from Flutter. /// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol MyCentralManagerHostApi { protocol MyCentralManagerHostApi {
func setUp(completion: @escaping (Result<MyCentralManagerArgs, Error>) -> Void) func setUp() throws
func startDiscovery() throws func startDiscovery() throws
func stopDiscovery() throws func stopDiscovery() throws
func connect(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) func connect(uuidArgs: String, completion: @escaping (Result<Void, Error>) -> Void)
func disconnect(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) func disconnect(uuidArgs: String, completion: @escaping (Result<Void, Error>) -> Void)
func getMaximumWriteLength(peripheralHashCodeArgs: Int64, typeNumberArgs: Int64) throws -> Int64 func getMaximumWriteValueLength(uuidArgs: String, typeNumberArgs: Int64) throws -> Int64
func readRSSI(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Int64, Error>) -> Void) func readRSSI(uuidArgs: String, completion: @escaping (Result<Int64, Error>) -> Void)
func discoverGATT(peripheralHashCodeArgs: Int64, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void) func discoverServices(uuidArgs: String, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void)
func readCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) func discoverCharacteristics(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattCharacteristicArgs], Error>) -> Void)
func writeCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) func discoverDescriptors(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattDescriptorArgs], Error>) -> Void)
func notifyCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, stateArgs: Bool, completion: @escaping (Result<Void, Error>) -> Void) func readCharacteristic(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void)
func readDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) func writeCharacteristic(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void)
func writeDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void) func setCharacteristicNotifyState(uuidArgs: String, hashCodeArgs: Int64, stateArgs: Bool, completion: @escaping (Result<Void, Error>) -> Void)
func readDescriptor(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void)
func writeDescriptor(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void)
} }
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
@ -353,15 +332,13 @@ class MyCentralManagerHostApiSetup {
let setUpChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setUp", binaryMessenger: binaryMessenger, codec: codec) let setUpChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setUp", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
setUpChannel.setMessageHandler { _, reply in setUpChannel.setMessageHandler { _, reply in
api.setUp() { result in do {
switch result { try api.setUp()
case .success(let res): reply(wrapResult(nil))
reply(wrapResult(res)) } catch {
case .failure(let error):
reply(wrapError(error)) reply(wrapError(error))
} }
} }
}
} else { } else {
setUpChannel.setMessageHandler(nil) setUpChannel.setMessageHandler(nil)
} }
@ -395,8 +372,8 @@ class MyCentralManagerHostApiSetup {
if let api = api { if let api = api {
connectChannel.setMessageHandler { message, reply in connectChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let uuidArgsArg = args[0] as! String
api.connect(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in api.connect(uuidArgs: uuidArgsArg) { result in
switch result { switch result {
case .success: case .success:
reply(wrapResult(nil)) reply(wrapResult(nil))
@ -412,8 +389,8 @@ class MyCentralManagerHostApiSetup {
if let api = api { if let api = api {
disconnectChannel.setMessageHandler { message, reply in disconnectChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let uuidArgsArg = args[0] as! String
api.disconnect(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in api.disconnect(uuidArgs: uuidArgsArg) { result in
switch result { switch result {
case .success: case .success:
reply(wrapResult(nil)) reply(wrapResult(nil))
@ -425,28 +402,28 @@ class MyCentralManagerHostApiSetup {
} else { } else {
disconnectChannel.setMessageHandler(nil) disconnectChannel.setMessageHandler(nil)
} }
let getMaximumWriteLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.getMaximumWriteLength", binaryMessenger: binaryMessenger, codec: codec) let getMaximumWriteValueLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.getMaximumWriteValueLength", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
getMaximumWriteLengthChannel.setMessageHandler { message, reply in getMaximumWriteValueLengthChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let uuidArgsArg = args[0] as! String
let typeNumberArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) let typeNumberArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
do { do {
let result = try api.getMaximumWriteLength(peripheralHashCodeArgs: peripheralHashCodeArgsArg, typeNumberArgs: typeNumberArgsArg) let result = try api.getMaximumWriteValueLength(uuidArgs: uuidArgsArg, typeNumberArgs: typeNumberArgsArg)
reply(wrapResult(result)) reply(wrapResult(result))
} catch { } catch {
reply(wrapError(error)) reply(wrapError(error))
} }
} }
} else { } else {
getMaximumWriteLengthChannel.setMessageHandler(nil) getMaximumWriteValueLengthChannel.setMessageHandler(nil)
} }
let readRSSIChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readRSSI", binaryMessenger: binaryMessenger, codec: codec) let readRSSIChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readRSSI", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
readRSSIChannel.setMessageHandler { message, reply in readRSSIChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let uuidArgsArg = args[0] as! String
api.readRSSI(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in api.readRSSI(uuidArgs: uuidArgsArg) { result in
switch result { switch result {
case .success(let res): case .success(let res):
reply(wrapResult(res)) reply(wrapResult(res))
@ -458,12 +435,12 @@ class MyCentralManagerHostApiSetup {
} else { } else {
readRSSIChannel.setMessageHandler(nil) readRSSIChannel.setMessageHandler(nil)
} }
let discoverGATTChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverGATT", binaryMessenger: binaryMessenger, codec: codec) let discoverServicesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverServices", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
discoverGATTChannel.setMessageHandler { message, reply in discoverServicesChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let uuidArgsArg = args[0] as! String
api.discoverGATT(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in api.discoverServices(uuidArgs: uuidArgsArg) { result in
switch result { switch result {
case .success(let res): case .success(let res):
reply(wrapResult(res)) reply(wrapResult(res))
@ -473,15 +450,51 @@ class MyCentralManagerHostApiSetup {
} }
} }
} else { } else {
discoverGATTChannel.setMessageHandler(nil) discoverServicesChannel.setMessageHandler(nil)
}
let discoverCharacteristicsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverCharacteristics", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
discoverCharacteristicsChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let uuidArgsArg = args[0] as! String
let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
api.discoverCharacteristics(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
discoverCharacteristicsChannel.setMessageHandler(nil)
}
let discoverDescriptorsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverDescriptors", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
discoverDescriptorsChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let uuidArgsArg = args[0] as! String
let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
api.discoverDescriptors(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
discoverDescriptorsChannel.setMessageHandler(nil)
} }
let readCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readCharacteristic", binaryMessenger: binaryMessenger, codec: codec) let readCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readCharacteristic", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
readCharacteristicChannel.setMessageHandler { message, reply in readCharacteristicChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let uuidArgsArg = args[0] as! String
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
api.readCharacteristic(peripheralHashCodeArgs: peripheralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg) { result in api.readCharacteristic(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in
switch result { switch result {
case .success(let res): case .success(let res):
reply(wrapResult(res)) reply(wrapResult(res))
@ -497,11 +510,11 @@ class MyCentralManagerHostApiSetup {
if let api = api { if let api = api {
writeCharacteristicChannel.setMessageHandler { message, reply in writeCharacteristicChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let uuidArgsArg = args[0] as! String
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let valueArgsArg = args[2] as! FlutterStandardTypedData let valueArgsArg = args[2] as! FlutterStandardTypedData
let typeNumberArgsArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32) let typeNumberArgsArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32)
api.writeCharacteristic(peripheralHashCodeArgs: peripheralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, valueArgs: valueArgsArg, typeNumberArgs: typeNumberArgsArg) { result in api.writeCharacteristic(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg, typeNumberArgs: typeNumberArgsArg) { result in
switch result { switch result {
case .success: case .success:
reply(wrapResult(nil)) reply(wrapResult(nil))
@ -513,14 +526,14 @@ class MyCentralManagerHostApiSetup {
} else { } else {
writeCharacteristicChannel.setMessageHandler(nil) writeCharacteristicChannel.setMessageHandler(nil)
} }
let notifyCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.notifyCharacteristic", binaryMessenger: binaryMessenger, codec: codec) let setCharacteristicNotifyStateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setCharacteristicNotifyState", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
notifyCharacteristicChannel.setMessageHandler { message, reply in setCharacteristicNotifyStateChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let uuidArgsArg = args[0] as! String
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let stateArgsArg = args[2] as! Bool let stateArgsArg = args[2] as! Bool
api.notifyCharacteristic(peripheralHashCodeArgs: peripheralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, stateArgs: stateArgsArg) { result in api.setCharacteristicNotifyState(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, stateArgs: stateArgsArg) { result in
switch result { switch result {
case .success: case .success:
reply(wrapResult(nil)) reply(wrapResult(nil))
@ -530,15 +543,15 @@ class MyCentralManagerHostApiSetup {
} }
} }
} else { } else {
notifyCharacteristicChannel.setMessageHandler(nil) setCharacteristicNotifyStateChannel.setMessageHandler(nil)
} }
let readDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readDescriptor", binaryMessenger: binaryMessenger, codec: codec) let readDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readDescriptor", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
readDescriptorChannel.setMessageHandler { message, reply in readDescriptorChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let uuidArgsArg = args[0] as! String
let descriptorHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
api.readDescriptor(peripheralHashCodeArgs: peripheralHashCodeArgsArg, descriptorHashCodeArgs: descriptorHashCodeArgsArg) { result in api.readDescriptor(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in
switch result { switch result {
case .success(let res): case .success(let res):
reply(wrapResult(res)) reply(wrapResult(res))
@ -554,10 +567,10 @@ class MyCentralManagerHostApiSetup {
if let api = api { if let api = api {
writeDescriptorChannel.setMessageHandler { message, reply in writeDescriptorChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let uuidArgsArg = args[0] as! String
let descriptorHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let valueArgsArg = args[2] as! FlutterStandardTypedData let valueArgsArg = args[2] as! FlutterStandardTypedData
api.writeDescriptor(peripheralHashCodeArgs: peripheralHashCodeArgsArg, descriptorHashCodeArgs: descriptorHashCodeArgsArg, valueArgs: valueArgsArg) { result in api.writeDescriptor(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg) { result in
switch result { switch result {
case .success: case .success:
reply(wrapResult(nil)) reply(wrapResult(nil))
@ -577,12 +590,8 @@ private class MyCentralManagerFlutterApiCodecReader: FlutterStandardReader {
case 128: case 128:
return MyAdvertisementArgs.fromList(self.readValue() as! [Any?]) return MyAdvertisementArgs.fromList(self.readValue() as! [Any?])
case 129: case 129:
return MyGattCharacteristicArgs.fromList(self.readValue() as! [Any?])
case 130:
return MyGattDescriptorArgs.fromList(self.readValue() as! [Any?])
case 131:
return MyManufacturerSpecificDataArgs.fromList(self.readValue() as! [Any?]) return MyManufacturerSpecificDataArgs.fromList(self.readValue() as! [Any?])
case 132: case 130:
return MyPeripheralArgs.fromList(self.readValue() as! [Any?]) return MyPeripheralArgs.fromList(self.readValue() as! [Any?])
default: default:
return super.readValue(ofType: type) return super.readValue(ofType: type)
@ -595,17 +604,11 @@ private class MyCentralManagerFlutterApiCodecWriter: FlutterStandardWriter {
if let value = value as? MyAdvertisementArgs { if let value = value as? MyAdvertisementArgs {
super.writeByte(128) super.writeByte(128)
super.writeValue(value.toList()) super.writeValue(value.toList())
} else if let value = value as? MyGattCharacteristicArgs { } else if let value = value as? MyManufacturerSpecificDataArgs {
super.writeByte(129) super.writeByte(129)
super.writeValue(value.toList()) super.writeValue(value.toList())
} else if let value = value as? MyGattDescriptorArgs {
super.writeByte(130)
super.writeValue(value.toList())
} else if let value = value as? MyManufacturerSpecificDataArgs {
super.writeByte(131)
super.writeValue(value.toList())
} else if let value = value as? MyPeripheralArgs { } else if let value = value as? MyPeripheralArgs {
super.writeByte(132) super.writeByte(130)
super.writeValue(value.toList()) super.writeValue(value.toList())
} else { } else {
super.writeValue(value) super.writeValue(value)
@ -631,8 +634,8 @@ class MyCentralManagerFlutterApiCodec: FlutterStandardMessageCodec {
protocol MyCentralManagerFlutterApiProtocol { protocol MyCentralManagerFlutterApiProtocol {
func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void)
func onDiscovered(peripheralArgs peripheralArgsArg: MyPeripheralArgs, rssiArgs rssiArgsArg: Int64, advertisementArgs advertisementArgsArg: MyAdvertisementArgs, completion: @escaping (Result<Void, FlutterError>) -> Void) func onDiscovered(peripheralArgs peripheralArgsArg: MyPeripheralArgs, rssiArgs rssiArgsArg: Int64, advertisementArgs advertisementArgsArg: MyAdvertisementArgs, completion: @escaping (Result<Void, FlutterError>) -> Void)
func onPeripheralStateChanged(peripheralArgs peripheralArgsArg: MyPeripheralArgs, stateArgs stateArgsArg: Bool, completion: @escaping (Result<Void, FlutterError>) -> Void) func onConnectionStateChanged(uuidArgs uuidArgsArg: String, stateArgs stateArgsArg: Bool, completion: @escaping (Result<Void, FlutterError>) -> Void)
func onCharacteristicValueChanged(characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result<Void, FlutterError>) -> Void) func onCharacteristicNotified(uuidArgs uuidArgsArg: String, hashCodeArgs hashCodeArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result<Void, FlutterError>) -> Void)
} }
class MyCentralManagerFlutterApi: MyCentralManagerFlutterApiProtocol { class MyCentralManagerFlutterApi: MyCentralManagerFlutterApiProtocol {
private let binaryMessenger: FlutterBinaryMessenger private let binaryMessenger: FlutterBinaryMessenger
@ -643,30 +646,78 @@ class MyCentralManagerFlutterApi: MyCentralManagerFlutterApiProtocol {
return MyCentralManagerFlutterApiCodec.shared return MyCentralManagerFlutterApiCodec.shared
} }
func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) { func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onStateChanged", binaryMessenger: binaryMessenger, codec: codec) let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onStateChanged"
channel.sendMessage([stateNumberArgsArg] as [Any?]) { _ in let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([stateNumberArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void())) completion(.success(Void()))
} }
} }
}
func onDiscovered(peripheralArgs peripheralArgsArg: MyPeripheralArgs, rssiArgs rssiArgsArg: Int64, advertisementArgs advertisementArgsArg: MyAdvertisementArgs, completion: @escaping (Result<Void, FlutterError>) -> Void) { func onDiscovered(peripheralArgs peripheralArgsArg: MyPeripheralArgs, rssiArgs rssiArgsArg: Int64, advertisementArgs advertisementArgsArg: MyAdvertisementArgs, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onDiscovered", binaryMessenger: binaryMessenger, codec: codec) let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onDiscovered"
channel.sendMessage([peripheralArgsArg, rssiArgsArg, advertisementArgsArg] as [Any?]) { _ in let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([peripheralArgsArg, rssiArgsArg, advertisementArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void())) completion(.success(Void()))
} }
} }
func onPeripheralStateChanged(peripheralArgs peripheralArgsArg: MyPeripheralArgs, stateArgs stateArgsArg: Bool, completion: @escaping (Result<Void, FlutterError>) -> Void) { }
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onPeripheralStateChanged", binaryMessenger: binaryMessenger, codec: codec) func onConnectionStateChanged(uuidArgs uuidArgsArg: String, stateArgs stateArgsArg: Bool, completion: @escaping (Result<Void, FlutterError>) -> Void) {
channel.sendMessage([peripheralArgsArg, stateArgsArg] as [Any?]) { _ in let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onConnectionStateChanged"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([uuidArgsArg, stateArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void())) completion(.success(Void()))
} }
} }
func onCharacteristicValueChanged(characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result<Void, FlutterError>) -> Void) { }
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicValueChanged", binaryMessenger: binaryMessenger, codec: codec) func onCharacteristicNotified(uuidArgs uuidArgsArg: String, hashCodeArgs hashCodeArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result<Void, FlutterError>) -> Void) {
channel.sendMessage([characteristicArgsArg, valueArgsArg] as [Any?]) { _ in let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicNotified"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([uuidArgsArg, hashCodeArgsArg, valueArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void())) completion(.success(Void()))
} }
} }
} }
}
private class MyPeripheralManagerHostApiCodecReader: FlutterStandardReader { private class MyPeripheralManagerHostApiCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? { override func readValue(ofType type: UInt8) -> Any? {
switch type { switch type {
@ -680,8 +731,6 @@ private class MyPeripheralManagerHostApiCodecReader: FlutterStandardReader {
return MyGattServiceArgs.fromList(self.readValue() as! [Any?]) return MyGattServiceArgs.fromList(self.readValue() as! [Any?])
case 132: case 132:
return MyManufacturerSpecificDataArgs.fromList(self.readValue() as! [Any?]) return MyManufacturerSpecificDataArgs.fromList(self.readValue() as! [Any?])
case 133:
return MyPeripheralManagerArgs.fromList(self.readValue() as! [Any?])
default: default:
return super.readValue(ofType: type) return super.readValue(ofType: type)
} }
@ -705,9 +754,6 @@ private class MyPeripheralManagerHostApiCodecWriter: FlutterStandardWriter {
} else if let value = value as? MyManufacturerSpecificDataArgs { } else if let value = value as? MyManufacturerSpecificDataArgs {
super.writeByte(132) super.writeByte(132)
super.writeValue(value.toList()) super.writeValue(value.toList())
} else if let value = value as? MyPeripheralManagerArgs {
super.writeByte(133)
super.writeValue(value.toList())
} else { } else {
super.writeValue(value) super.writeValue(value)
} }
@ -730,16 +776,15 @@ class MyPeripheralManagerHostApiCodec: FlutterStandardMessageCodec {
/// Generated protocol from Pigeon that represents a handler of messages from Flutter. /// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol MyPeripheralManagerHostApi { protocol MyPeripheralManagerHostApi {
func setUp(completion: @escaping (Result<MyPeripheralManagerArgs, Error>) -> Void) func setUp() throws
func addService(serviceArgs: MyGattServiceArgs, completion: @escaping (Result<Void, Error>) -> Void) func addService(serviceArgs: MyGattServiceArgs, completion: @escaping (Result<Void, Error>) -> Void)
func removeService(serviceHashCodeArgs: Int64) throws func removeService(hashCodeArgs: Int64) throws
func clearServices() throws func clearServices() throws
func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result<Void, Error>) -> Void) func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result<Void, Error>) -> Void)
func stopAdvertising() throws func stopAdvertising() throws
func getMaximumWriteLength(centralHashCodeArgs: Int64) throws -> Int64 func getMaximumUpdateValueLength(uuidArgs: String) throws -> Int64
func sendReadCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool, valueArgs: FlutterStandardTypedData) throws func respond(idArgs: Int64, errorNumberArgs: Int64, valueArgs: FlutterStandardTypedData?) throws
func sendWriteCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool) throws func updateCharacteristic(hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, uuidsArgs: [String]?, completion: @escaping (Result<Void, Error>) -> Void)
func notifyCharacteristicValueChanged(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void)
} }
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
@ -751,15 +796,13 @@ class MyPeripheralManagerHostApiSetup {
let setUpChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.setUp", binaryMessenger: binaryMessenger, codec: codec) let setUpChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.setUp", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
setUpChannel.setMessageHandler { _, reply in setUpChannel.setMessageHandler { _, reply in
api.setUp() { result in do {
switch result { try api.setUp()
case .success(let res): reply(wrapResult(nil))
reply(wrapResult(res)) } catch {
case .failure(let error):
reply(wrapError(error)) reply(wrapError(error))
} }
} }
}
} else { } else {
setUpChannel.setMessageHandler(nil) setUpChannel.setMessageHandler(nil)
} }
@ -784,9 +827,9 @@ class MyPeripheralManagerHostApiSetup {
if let api = api { if let api = api {
removeServiceChannel.setMessageHandler { message, reply in removeServiceChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let serviceHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let hashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
do { do {
try api.removeService(serviceHashCodeArgs: serviceHashCodeArgsArg) try api.removeService(hashCodeArgs: hashCodeArgsArg)
reply(wrapResult(nil)) reply(wrapResult(nil))
} catch { } catch {
reply(wrapError(error)) reply(wrapError(error))
@ -838,68 +881,46 @@ class MyPeripheralManagerHostApiSetup {
} else { } else {
stopAdvertisingChannel.setMessageHandler(nil) stopAdvertisingChannel.setMessageHandler(nil)
} }
let getMaximumWriteLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.getMaximumWriteLength", binaryMessenger: binaryMessenger, codec: codec) let getMaximumUpdateValueLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.getMaximumUpdateValueLength", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
getMaximumWriteLengthChannel.setMessageHandler { message, reply in getMaximumUpdateValueLengthChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let uuidArgsArg = args[0] as! String
do { do {
let result = try api.getMaximumWriteLength(centralHashCodeArgs: centralHashCodeArgsArg) let result = try api.getMaximumUpdateValueLength(uuidArgs: uuidArgsArg)
reply(wrapResult(result)) reply(wrapResult(result))
} catch { } catch {
reply(wrapError(error)) reply(wrapError(error))
} }
} }
} else { } else {
getMaximumWriteLengthChannel.setMessageHandler(nil) getMaximumUpdateValueLengthChannel.setMessageHandler(nil)
} }
let sendReadCharacteristicReplyChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.sendReadCharacteristicReply", binaryMessenger: binaryMessenger, codec: codec) let respondChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.respond", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
sendReadCharacteristicReplyChannel.setMessageHandler { message, reply in respondChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let idArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) let errorNumberArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let idArgsArg = args[2] is Int64 ? args[2] as! Int64 : Int64(args[2] as! Int32) let valueArgsArg: FlutterStandardTypedData? = nilOrValue(args[2])
let offsetArgsArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32)
let statusArgsArg = args[4] as! Bool
let valueArgsArg = args[5] as! FlutterStandardTypedData
do { do {
try api.sendReadCharacteristicReply(centralHashCodeArgs: centralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, idArgs: idArgsArg, offsetArgs: offsetArgsArg, statusArgs: statusArgsArg, valueArgs: valueArgsArg) try api.respond(idArgs: idArgsArg, errorNumberArgs: errorNumberArgsArg, valueArgs: valueArgsArg)
reply(wrapResult(nil)) reply(wrapResult(nil))
} catch { } catch {
reply(wrapError(error)) reply(wrapError(error))
} }
} }
} else { } else {
sendReadCharacteristicReplyChannel.setMessageHandler(nil) respondChannel.setMessageHandler(nil)
} }
let sendWriteCharacteristicReplyChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.sendWriteCharacteristicReply", binaryMessenger: binaryMessenger, codec: codec) let updateCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.updateCharacteristic", binaryMessenger: binaryMessenger, codec: codec)
if let api = api { if let api = api {
sendWriteCharacteristicReplyChannel.setMessageHandler { message, reply in updateCharacteristicChannel.setMessageHandler { message, reply in
let args = message as! [Any?] let args = message as! [Any?]
let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32) let hashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32) let valueArgsArg = args[1] as! FlutterStandardTypedData
let idArgsArg = args[2] is Int64 ? args[2] as! Int64 : Int64(args[2] as! Int32) let uuidsArgsArg: [String]? = nilOrValue(args[2])
let offsetArgsArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32) api.updateCharacteristic(hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg, uuidsArgs: uuidsArgsArg) { result in
let statusArgsArg = args[4] as! Bool
do {
try api.sendWriteCharacteristicReply(centralHashCodeArgs: centralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, idArgs: idArgsArg, offsetArgs: offsetArgsArg, statusArgs: statusArgsArg)
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
}
}
} else {
sendWriteCharacteristicReplyChannel.setMessageHandler(nil)
}
let notifyCharacteristicValueChangedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.notifyCharacteristicValueChanged", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
notifyCharacteristicValueChangedChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let valueArgsArg = args[2] as! FlutterStandardTypedData
api.notifyCharacteristicValueChanged(centralHashCodeArgs: centralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, valueArgs: valueArgsArg) { result in
switch result { switch result {
case .success: case .success:
reply(wrapResult(nil)) reply(wrapResult(nil))
@ -909,7 +930,7 @@ class MyPeripheralManagerHostApiSetup {
} }
} }
} else { } else {
notifyCharacteristicValueChangedChannel.setMessageHandler(nil) updateCharacteristicChannel.setMessageHandler(nil)
} }
} }
} }
@ -918,10 +939,6 @@ private class MyPeripheralManagerFlutterApiCodecReader: FlutterStandardReader {
switch type { switch type {
case 128: case 128:
return MyCentralArgs.fromList(self.readValue() as! [Any?]) return MyCentralArgs.fromList(self.readValue() as! [Any?])
case 129:
return MyGattCharacteristicArgs.fromList(self.readValue() as! [Any?])
case 130:
return MyGattDescriptorArgs.fromList(self.readValue() as! [Any?])
default: default:
return super.readValue(ofType: type) return super.readValue(ofType: type)
} }
@ -933,12 +950,6 @@ private class MyPeripheralManagerFlutterApiCodecWriter: FlutterStandardWriter {
if let value = value as? MyCentralArgs { if let value = value as? MyCentralArgs {
super.writeByte(128) super.writeByte(128)
super.writeValue(value.toList()) super.writeValue(value.toList())
} else if let value = value as? MyGattCharacteristicArgs {
super.writeByte(129)
super.writeValue(value.toList())
} else if let value = value as? MyGattDescriptorArgs {
super.writeByte(130)
super.writeValue(value.toList())
} else { } else {
super.writeValue(value) super.writeValue(value)
} }
@ -962,9 +973,9 @@ class MyPeripheralManagerFlutterApiCodec: FlutterStandardMessageCodec {
/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.
protocol MyPeripheralManagerFlutterApiProtocol { protocol MyPeripheralManagerFlutterApiProtocol {
func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void)
func onReadCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) func onCharacteristicReadRequest(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void)
func onWriteCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result<Void, FlutterError>) -> Void) func onCharacteristicWriteRequest(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result<Void, FlutterError>) -> Void)
func onNotifyCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, stateArgs stateArgsArg: Bool, completion: @escaping (Result<Void, FlutterError>) -> Void) func onCharacteristicNotifyStateChanged(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, stateArgs stateArgsArg: Bool, completion: @escaping (Result<Void, FlutterError>) -> Void)
} }
class MyPeripheralManagerFlutterApi: MyPeripheralManagerFlutterApiProtocol { class MyPeripheralManagerFlutterApi: MyPeripheralManagerFlutterApiProtocol {
private let binaryMessenger: FlutterBinaryMessenger private let binaryMessenger: FlutterBinaryMessenger
@ -975,27 +986,75 @@ class MyPeripheralManagerFlutterApi: MyPeripheralManagerFlutterApiProtocol {
return MyPeripheralManagerFlutterApiCodec.shared return MyPeripheralManagerFlutterApiCodec.shared
} }
func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) { func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onStateChanged", binaryMessenger: binaryMessenger, codec: codec) let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onStateChanged"
channel.sendMessage([stateNumberArgsArg] as [Any?]) { _ in let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
completion(.success(Void())) channel.sendMessage([stateNumberArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
} }
} if (listResponse.count > 1) {
func onReadCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) { let code: String = listResponse[0] as! String
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived", binaryMessenger: binaryMessenger, codec: codec) let message: String? = nilOrValue(listResponse[1])
channel.sendMessage([centralArgsArg, characteristicArgsArg, idArgsArg, offsetArgsArg] as [Any?]) { _ in let details: String? = nilOrValue(listResponse[2])
completion(.success(Void())) completion(.failure(FlutterError(code: code, message: message, details: details)));
} } else {
}
func onWriteCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived", binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([centralArgsArg, characteristicArgsArg, idArgsArg, offsetArgsArg, valueArgsArg] as [Any?]) { _ in
completion(.success(Void()))
}
}
func onNotifyCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, stateArgs stateArgsArg: Bool, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived", binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([centralArgsArg, characteristicArgsArg, stateArgsArg] as [Any?]) { _ in
completion(.success(Void())) completion(.success(Void()))
} }
} }
} }
func onCharacteristicReadRequest(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([centralArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void()))
}
}
}
func onCharacteristicWriteRequest(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicWriteRequest"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([centralArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg, valueArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void()))
}
}
}
func onCharacteristicNotifyStateChanged(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, stateArgs stateArgsArg: Bool, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onCharacteristicNotifyStateChanged"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([centralArgsArg, hashCodeArgsArg, stateArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void()))
}
}
}
}

View File

@ -16,85 +16,40 @@ import FlutterMacOS
#error("Unsupported platform.") #error("Unsupported platform.")
#endif #endif
// This extension of Error is required to do use FlutterError in any Swift code. // ToObj
extension FlutterError: Error {} extension [MyGattCharacteristicPropertyArgs] {
func toProperties() -> CBCharacteristicProperties {
var properties: CBCharacteristicProperties = []
for args in self {
switch args {
case .read:
properties.insert(.read)
case .write:
properties.insert(.write)
case .writeWithoutResponse:
properties.insert(.writeWithoutResponse)
case .notify:
properties.insert(.notify)
case .indicate:
properties.insert(.indicate)
}
}
return properties
}
extension CBManagerState { func toPermissions() -> CBAttributePermissions {
func toArgs() -> MyBluetoothLowEnergyStateArgs { var permissions: CBAttributePermissions = []
switch self { for args in self {
case .unauthorized: switch args {
return .unauthorized case .read:
case .poweredOff: permissions.insert(.readable)
return .poweredOff case .write, .writeWithoutResponse:
case .poweredOn: permissions.insert(.writeable)
return .poweredOn
default: default:
return .unsupported continue
} }
} }
} return permissions
extension CBPeer {
var uuidArgs: String { identifier.uuidString }
}
extension CBCentral {
func toArgs() -> MyCentralArgs {
let hashCodeArgs = Int64(hash)
return MyCentralArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs)
}
}
extension CBPeripheral {
func toArgs() -> MyPeripheralArgs {
let hashCodeArgs = Int64(hash)
return MyPeripheralArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs)
}
}
extension CBAttribute {
var uuidArgs: String { uuid.uuidString }
}
extension CBService {
func toArgs(_ characteristicsArgs: [MyGattCharacteristicArgs]) -> MyGattServiceArgs {
let hashCodeArgs = Int64(hash)
return MyGattServiceArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, characteristicsArgs: characteristicsArgs)
}
}
extension CBCharacteristic {
func toArgs(_ descriptorsArgs: [MyGattDescriptorArgs]) -> MyGattCharacteristicArgs {
let hashCodeArgs = Int64(hash)
return MyGattCharacteristicArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, propertyNumbersArgs: propertyNumbersArgs, descriptorsArgs: descriptorsArgs)
}
var propertyNumbersArgs: [Int64] {
var propertiesArgs = [MyGattCharacteristicPropertyArgs]()
let properties = self.properties
if properties.contains(.read) {
propertiesArgs.append(.read)
}
if properties.contains(.write) {
propertiesArgs.append(.write)
}
if properties.contains(.writeWithoutResponse) {
propertiesArgs.append(.writeWithoutResponse)
}
if properties.contains(.notify) {
propertiesArgs.append(.notify)
}
if properties.contains(.indicate) {
propertiesArgs.append(.indicate)
}
return propertiesArgs.map { args in Int64(args.rawValue) }
}
}
extension CBDescriptor {
func toArgs() -> MyGattDescriptorArgs {
let hashCodeArgs = Int64(hash)
return MyGattDescriptorArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs)
} }
} }
@ -109,21 +64,151 @@ extension MyGattCharacteristicWriteTypeArgs {
} }
} }
extension MyGattErrorArgs {
func toError() -> CBATTError.Code {
switch self {
case .success:
return .success
case .invalidHandle:
return .invalidHandle
case .readNotPermitted:
return .readNotPermitted
case .writeNotPermitted:
return .writeNotPermitted
case .invalidPDU:
return .invalidPdu
case .insufficientAuthentication:
return .insufficientAuthentication
case .requestNotSupported:
return .requestNotSupported
case .invalidOffset:
return .invalidOffset
case .insufficientAuthorization:
return .insufficientAuthorization
case .prepareQueueFull:
return .prepareQueueFull
case .attributeNotFound:
return .attributeNotFound
case .attributeNotLong:
return .attributeNotLong
case .insufficientEncryptionKeySize:
return .insufficientEncryptionKeySize
case .invalidAttributeValueLength:
return .invalidAttributeValueLength
case .unlikelyError:
return .unlikelyError
case .insufficientEncryption:
return .insufficientEncryption
case .unsupportedGroupType:
return .unsupportedGroupType
case .insufficientResources:
return .insufficientResources
}
}
}
extension MyAdvertisementArgs {
func toAdvertisement() -> [String : Any] {
// CoreBluetooth only support `CBAdvertisementDataLocalNameKey` and `CBAdvertisementDataServiceUUIDsKey`
// see https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising
var advertisement = [String: Any]()
if nameArgs != nil {
let name = nameArgs!
advertisement[CBAdvertisementDataLocalNameKey] = name
}
if serviceUUIDsArgs.count > 0 {
var serviceUUIDs = [CBUUID]()
for args in serviceUUIDsArgs {
guard let uuidArgs = args else {
continue
}
let uuid = uuidArgs.toCBUUID()
serviceUUIDs.append(uuid)
}
advertisement[CBAdvertisementDataServiceUUIDsKey] = serviceUUIDs
}
return advertisement
}
}
extension MyGattDescriptorArgs {
func toDescriptor() -> CBMutableDescriptor {
let type = uuidArgs.toCBUUID()
let value = valueArgs?.data
return CBMutableDescriptor(type: type, value: value)
}
}
extension MyGattCharacteristicArgs {
func toCharacteristic() -> CBMutableCharacteristic {
let type = uuidArgs.toCBUUID()
var propertiesArgs = [MyGattCharacteristicPropertyArgs]()
for args in propertyNumbersArgs {
guard let propertyNumberArgs = args else {
continue
}
let propertyNumber = propertyNumberArgs.toInt()
guard let propertyArgs = MyGattCharacteristicPropertyArgs(rawValue: propertyNumber) else {
continue
}
propertiesArgs.append(propertyArgs)
}
let properties = propertiesArgs.toProperties()
let permissions = propertiesArgs.toPermissions()
return CBMutableCharacteristic(type: type, properties: properties, value: nil, permissions: permissions)
}
}
extension MyGattServiceArgs {
func toService() -> CBMutableService {
let type = uuidArgs.toCBUUID()
let primary = true
return CBMutableService(type: type, primary: primary)
}
}
extension Int64 {
func toInt() -> Int {
return Int(self)
}
}
extension String { extension String {
var data: Data { data(using: String.Encoding.utf8)! } func toCBUUID() -> CBUUID {
} return CBUUID(string: self)
extension NSNumber {
var data: Data {
var source = self
return Data(bytes: &source, count: MemoryLayout<NSNumber>.size)
} }
} }
extension UInt16 { // ToArgs
var data: Data { extension CBManagerState {
var source = self func toArgs() -> MyBluetoothLowEnergyStateArgs {
return Data(bytes: &source, count: MemoryLayout<UInt16>.size) switch self {
case .resetting:
return .resetting
case .unsupported:
return .unsupported
case .unauthorized:
return .unauthorized
case .poweredOff:
return .poweredOff
case .poweredOn:
return .poweredOn
default:
return .unknown
}
}
}
extension Data {
func toManufacturerSpecificDataArgs() -> MyManufacturerSpecificDataArgs? {
if count > 2 {
let idArgs = Int64(self[0]) | (Int64(self[1]) << 8)
let data = self[2...count - 1]
let dataArgs = FlutterStandardTypedData(bytes: data)
return MyManufacturerSpecificDataArgs(idArgs: idArgs, dataArgs: dataArgs)
} else {
return nil
}
} }
} }
@ -145,160 +230,105 @@ extension [String: Any] {
} }
} }
extension Data { extension CBCentral {
func toManufacturerSpecificDataArgs() -> MyManufacturerSpecificDataArgs? { func toArgs() -> MyCentralArgs {
if count > 2 { let uuidArgs = identifier.toArgs()
let idArgs = Int64(self[0]) | (Int64(self[1]) << 8) return MyCentralArgs(uuidArgs: uuidArgs)
let data = self[2...count - 1]
let dataArgs = FlutterStandardTypedData(bytes: data)
return MyManufacturerSpecificDataArgs(idArgs: idArgs, dataArgs: dataArgs)
} else {
return nil
}
}
}
extension MyAdvertisementArgs {
func toAdvertisement() throws -> [String : Any] {
// CoreBluetooth only support `CBAdvertisementDataLocalNameKey` and `CBAdvertisementDataServiceUUIDsKey`, see https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising
var advertisement = [String: Any]()
if nameArgs != nil {
let name = nameArgs!
advertisement[CBAdvertisementDataLocalNameKey] = name
}
if serviceUUIDsArgs.count > 0 {
var serviceUUIDs = [CBUUID]()
for args in serviceUUIDsArgs {
guard let uuidArgs = args else {
throw MyError.illegalArgument
}
let uuid = CBUUID(string: uuidArgs)
serviceUUIDs.append(uuid)
}
advertisement[CBAdvertisementDataServiceUUIDsKey] = serviceUUIDs
}
// if serviceDataArgs.count > 0 {
// var serviceData = [CBUUID: Data]()
// for args in serviceDataArgs {
// guard let uuidArgs = args.key else {
// throw MyError.illegalArgument
// }
// guard let dataArgs = args.value else {
// throw MyError.illegalArgument
// }
// let uuid = CBUUID(string: uuidArgs)
// let data = dataArgs.data
// serviceData[uuid] = data
// }
// advertisement[CBAdvertisementDataServiceDataKey] = serviceData
// }
// if manufacturerSpecificDataArgs != nil {
// let manufacturerSpecificData = manufacturerSpecificDataArgs!.toManufacturerSpecificData()
// advertisement[CBAdvertisementDataManufacturerDataKey] = manufacturerSpecificData
// }
return advertisement
} }
} }
//extension MyManufacturerSpecificDataArgs { extension CBPeripheral {
// func toManufacturerSpecificData() -> Data { func toArgs() -> MyPeripheralArgs {
// let id = UInt16(idArgs).data let uuidArgs = identifier.toArgs()
// let data = dataArgs.data return MyPeripheralArgs(uuidArgs: uuidArgs)
// return id + data
// }
//}
extension MyGattServiceArgs {
func toService() -> CBMutableService {
let type = CBUUID(string: uuidArgs)
let primary = true
return CBMutableService(type: type, primary: primary)
} }
} }
extension MyGattCharacteristicArgs { extension CBDescriptor {
func toCharacteristic() -> CBMutableCharacteristic { func toArgs() -> MyGattDescriptorArgs {
let type = CBUUID(string: uuidArgs) let hashCodeArgs = hash.toInt64()
return CBMutableCharacteristic(type: type, properties: properties, value: nil, permissions: permissions) let uuidArgs = uuid.toArgs()
} return MyGattDescriptorArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs)
var properties: CBCharacteristicProperties {
var properties: CBCharacteristicProperties = []
for args in propertyNumbersArgs {
guard let rawArgs = args else {
continue
}
let rawValue = Int(rawArgs)
let property = MyGattCharacteristicPropertyArgs(rawValue: rawValue)
switch property {
case .read:
properties.insert(.read)
case .write:
properties.insert(.write)
case .writeWithoutResponse:
properties.insert(.writeWithoutResponse)
case .notify:
properties.insert(.notify)
case .indicate:
properties.insert(.indicate)
default:
continue
}
}
return properties
}
var permissions: CBAttributePermissions {
var permissions: CBAttributePermissions = []
for args in propertyNumbersArgs {
guard let rawArgs = args else {
continue
}
let rawValue = Int(rawArgs)
let property = MyGattCharacteristicPropertyArgs(rawValue: rawValue)
switch property {
case .read:
permissions.insert(.readable)
case .write, .writeWithoutResponse:
permissions.insert(.writeable)
default:
continue
}
}
return permissions
} }
} }
extension MyGattDescriptorArgs { extension CBCharacteristic {
func toDescriptor() -> CBMutableDescriptor { func toArgs() -> MyGattCharacteristicArgs {
let type = CBUUID(string: uuidArgs) let hashCodeArgs = hash.toInt64()
let value = valueArgs?.data let uuidArgs = uuid.toArgs()
return CBMutableDescriptor(type: type, value: value) let propertyNumbersArgs = properties.toArgs().map { args in args.rawValue.toInt64() }
let descriptorsArgs = descriptors?.map { descriptor in descriptor.toArgs() } ?? []
return MyGattCharacteristicArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, propertyNumbersArgs: propertyNumbersArgs, descriptorsArgs: descriptorsArgs)
}
}
extension CBCharacteristicProperties {
func toArgs() -> [MyGattCharacteristicPropertyArgs] {
var args = [MyGattCharacteristicPropertyArgs]()
if contains(.read) {
args.append(.read)
}
if contains(.write) {
args.append(.write)
}
if contains(.writeWithoutResponse) {
args.append(.writeWithoutResponse)
}
if contains(.notify) {
args.append(.notify)
}
if contains(.indicate) {
args.append(.indicate)
}
return args
}
}
extension CBService {
func toArgs() -> MyGattServiceArgs {
let hashCodeArgs = hash.toInt64()
let uuidArgs = uuid.toArgs()
let characteristicsArgs = characteristics?.map { characteristic in characteristic.toArgs() } ?? []
return MyGattServiceArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, characteristicsArgs: characteristicsArgs)
} }
} }
extension Int { extension Int {
func coerceIn(_ minimum: Int, _ maximum: Int) throws -> Int { func toInt64() -> Int64 {
if minimum > maximum { return Int64(self)
throw MyError.illegalArgument
}
if self < minimum {
return minimum
}
if self > maximum {
return maximum
}
return self
} }
} }
extension Dictionary { extension UUID {
mutating func getOrPut(_ key: Key, _ defaultValue: () -> Value) -> Value { func toArgs() -> String {
guard let value = self[key] else { return uuidString.lowercased()
let value1 = defaultValue() }
self[key] = value1 }
return value1
} extension CBUUID {
return value func toArgs() -> String {
return uuidString.lowercased()
}
}
// This extension of Error is required to do use FlutterError in any Swift code.
extension FlutterError: Error {}
extension String {
var data: Data { data(using: String.Encoding.utf8)! }
}
extension NSNumber {
var data: Data {
var source = self
// TODO: resolve warning: Forming 'UnsafeRawPointer' to a variable of type 'NSNumber'; this is likely incorrect because 'NSNumber' may contain an object reference.
return Data(bytes: &source, count: MemoryLayout<NSNumber>.size)
}
}
extension UInt16 {
var data: Data {
var source = self
return Data(bytes: &source, count: MemoryLayout<UInt16>.size)
} }
} }

View File

@ -17,220 +17,192 @@ import FlutterMacOS
#endif #endif
class MyCentralManager: MyCentralManagerHostApi { class MyCentralManager: MyCentralManagerHostApi {
init(_ binaryMessenger: FlutterBinaryMessenger) { private let _api: MyCentralManagerFlutterApi
self.binaryMessenger = binaryMessenger private let _centralManager: CBCentralManager
private lazy var _centralManagerDelegate = MyCentralManagerDelegate(centralManager: self)
private lazy var _peripheralDelegate = MyPeripheralDelegate(centralManager: self)
private var _peripherals: [String: CBPeripheral]
private var _services: [String: [Int64: CBService]]
private var _characteristics: [String: [Int64: CBCharacteristic]]
private var _descriptors: [String: [Int64: CBDescriptor]]
private var _connectCompletions: [String: (Result<Void, 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]]
init(messenger: FlutterBinaryMessenger) {
_api = MyCentralManagerFlutterApi(binaryMessenger: messenger)
_centralManager = CBCentralManager()
_peripherals = [:]
_services = [:]
_characteristics = [:]
_descriptors = [:]
_connectCompletions = [:]
_disconnectCompletions = [:]
_readRssiCompletions = [:]
_discoverServicesCompletions = [:]
_discoverCharacteristicsCompletions = [:]
_discoverDescriptorsCompletions = [:]
_readCharacteristicCompletions = [:]
_writeCharacteristicCompletions = [:]
_setCharacteristicNotifyStateCompletions = [:]
_readDescriptorCompletions = [:]
_writeDescriptorCompletions = [:]
} }
private let binaryMessenger: FlutterBinaryMessenger func setUp() throws {
private let centralManager = CBCentralManager() _clearState()
if _centralManager.delegate == nil {
private lazy var api = MyCentralManagerFlutterApi(binaryMessenger: binaryMessenger) _centralManager.delegate = _centralManagerDelegate
private lazy var centralManagerDelegate = MyCentralManagerDelegate(self)
private lazy var peripheralDelegate = MyPeripheralDelegate(self)
private var peripherals = [Int64: CBPeripheral]()
private var services = [Int64: CBService]()
private var characteristics = [Int64: CBCharacteristic]()
private var descriptors = [Int64: CBDescriptor]()
private var peripheralsArgs = [Int: MyPeripheralArgs]()
private var servicesArgsOfPeripheralsArgs = [Int64: [MyGattServiceArgs]]()
private var servicesArgs = [Int: MyGattServiceArgs]()
private var characteristicsArgs = [Int: MyGattCharacteristicArgs]()
private var descriptorsArgs = [Int: MyGattDescriptorArgs]()
private var setUpCompletion: ((Result<MyCentralManagerArgs, Error>) -> Void)?
private var connectCompletions = [Int64: (Result<Void, Error>) -> Void]()
private var disconnectCompletions = [Int64: (Result<Void, Error>) -> Void]()
private var readRssiCompletions = [Int64: (Result<Int64, Error>) -> Void]()
private var discoverGattCompletions = [Int64: (Result<[MyGattServiceArgs], Error>) -> Void]()
private var unfinishedServices = [Int64: [CBService]]()
private var unfinishedCharacteristics = [Int64: [CBCharacteristic]]()
private var readCharacteristicCompletions = [Int64: (Result<FlutterStandardTypedData, Error>) -> Void]()
private var writeCharacteristicCompletions = [Int64: (Result<Void, Error>) -> Void]()
private var notifyCharacteristicCompletions = [Int64: (Result<Void, Error>) -> Void]()
private var readDescriptorCompletions = [Int64: (Result<FlutterStandardTypedData, Error>) -> Void]()
private var writeDescriptorCompletions = [Int64: (Result<Void, Error>) -> Void]()
func setUp(completion: @escaping (Result<MyCentralManagerArgs, Error>) -> Void) {
do {
if setUpCompletion != nil {
throw MyError.illegalState
} }
try tearDown() _onStateChanged()
centralManager.delegate = centralManagerDelegate
if centralManager.state == .unknown {
setUpCompletion = completion
} else {
let stateArgs = centralManager.state.toArgs()
let stateNumberArgs = Int64(stateArgs.rawValue)
let args = MyCentralManagerArgs(stateNumberArgs: stateNumberArgs)
completion(.success(args))
}
} catch {
completion(.failure(error))
}
}
func tearDown() throws {
if(centralManager.isScanning) {
centralManager.stopScan()
}
for peripheral in peripherals.values {
if peripheral.state != .disconnected {
centralManager.cancelPeripheralConnection(peripheral)
}
}
peripherals.removeAll()
services.removeAll()
characteristics.removeAll()
descriptors.removeAll()
peripheralsArgs.removeAll()
servicesArgsOfPeripheralsArgs.removeAll()
servicesArgs.removeAll()
characteristicsArgs.removeAll()
descriptorsArgs.removeAll()
setUpCompletion = nil
connectCompletions.removeAll()
disconnectCompletions.removeAll()
readRssiCompletions.removeAll()
discoverGattCompletions.removeAll()
unfinishedServices.removeAll()
unfinishedCharacteristics.removeAll()
readCharacteristicCompletions.removeAll()
writeCharacteristicCompletions.removeAll()
notifyCharacteristicCompletions.removeAll()
readDescriptorCompletions.removeAll()
writeDescriptorCompletions.removeAll()
} }
func startDiscovery() throws { func startDiscovery() throws {
let options = [CBCentralManagerScanOptionAllowDuplicatesKey: true] let options = [CBCentralManagerScanOptionAllowDuplicatesKey: true]
centralManager.scanForPeripherals(withServices: nil, options: options) _centralManager.scanForPeripherals(withServices: nil, options: options)
} }
func stopDiscovery() throws { func stopDiscovery() throws {
centralManager.stopScan() _centralManager.stopScan()
} }
func connect(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) { func connect(uuidArgs: String, completion: @escaping (Result<Void, Error>) -> Void) {
do { do {
let unfinishedCompletion = connectCompletions[peripheralHashCodeArgs] guard let peripheral = _peripherals[uuidArgs] else {
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
centralManager.connect(peripheral) _centralManager.connect(peripheral)
connectCompletions[peripheralHashCodeArgs] = completion _connectCompletions[uuidArgs] = completion
} catch { } catch {
completion(.failure(error)) completion(.failure(error))
} }
} }
func disconnect(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) { func disconnect(uuidArgs: String, completion: @escaping (Result<Void, Error>) -> Void) {
do { do {
let unfinishedCompletion = disconnectCompletions[peripheralHashCodeArgs] guard let peripheral = _peripherals[uuidArgs] else {
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
centralManager.cancelPeripheralConnection(peripheral) _centralManager.cancelPeripheralConnection(peripheral)
disconnectCompletions[peripheralHashCodeArgs] = completion _disconnectCompletions[uuidArgs] = completion
} catch { } catch {
completion(.failure(error)) completion(.failure(error))
} }
} }
func getMaximumWriteLength(peripheralHashCodeArgs: Int64, typeNumberArgs: Int64) throws -> Int64 { func getMaximumWriteValueLength(uuidArgs: String, typeNumberArgs: Int64) throws -> Int64 {
guard let peripheral = peripherals[peripheralHashCodeArgs] else { guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
let typeRawValue = Int(typeNumberArgs) let typeNumber = typeNumberArgs.toInt()
guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeRawValue) else { guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeNumber) else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
let type = typeArgs.toWriteType() let type = typeArgs.toWriteType()
let maximumWriteLength = try peripheral.maximumWriteValueLength(for: type).coerceIn(20, 512) let maximumWriteValueLength = peripheral.maximumWriteValueLength(for: type)
let maximumWriteLengthArgs = Int64(maximumWriteLength) let maximumWriteValueLengthArgs = maximumWriteValueLength.toInt64()
return maximumWriteLengthArgs return maximumWriteValueLengthArgs
} }
func readRSSI(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Int64, Error>) -> Void) { func readRSSI(uuidArgs: String, completion: @escaping (Result<Int64, Error>) -> Void) {
do { do {
let unfinishedCompletion = readRssiCompletions[peripheralHashCodeArgs] guard let peripheral = _peripherals[uuidArgs] else {
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
peripheral.readRSSI() peripheral.readRSSI()
readRssiCompletions[peripheralHashCodeArgs] = completion _readRssiCompletions[uuidArgs] = completion
} catch { } catch {
completion(.failure(error)) completion(.failure(error))
} }
} }
func discoverGATT(peripheralHashCodeArgs: Int64, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void) { func discoverServices(uuidArgs: String, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void) {
do { do {
let unfinishedCompletion = discoverGattCompletions[peripheralHashCodeArgs] guard let peripheral = _peripherals[uuidArgs] else {
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
peripheral.discoverServices(nil) peripheral.discoverServices(nil)
discoverGattCompletions[peripheralHashCodeArgs] = completion _discoverServicesCompletions[uuidArgs] = completion
} catch { } catch {
completion(.failure(error)) completion(.failure(error))
} }
} }
func readCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) { func discoverCharacteristics(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattCharacteristicArgs], Error>) -> Void) {
do { do {
let unfinishedCompletion = readCharacteristicCompletions[characteristicHashCodeArgs] guard let peripheral = _peripherals[uuidArgs] else {
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
guard let characteristic = characteristics[characteristicHashCodeArgs] else { guard let service = _retrieveService(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument
}
peripheral.discoverCharacteristics(nil, for: service)
_discoverCharacteristicsCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch {
completion(.failure(error))
}
}
func discoverDescriptors(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattDescriptorArgs], Error>) -> Void){
do {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
guard let characteristic = _retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument
}
peripheral.discoverDescriptors(for: characteristic)
_discoverDescriptorsCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch {
completion(.failure(error))
}
}
func readCharacteristic(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<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 throw MyError.illegalArgument
} }
peripheral.readValue(for: characteristic) peripheral.readValue(for: characteristic)
readCharacteristicCompletions[characteristicHashCodeArgs] = completion _readCharacteristicCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch { } catch {
completion(.failure(error)) completion(.failure(error))
} }
} }
func writeCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) { func writeCharacteristic(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
do { do {
let unfinishedCompletion = writeCharacteristicCompletions[characteristicHashCodeArgs] guard let peripheral = _peripherals[uuidArgs] else {
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
guard let characteristic = characteristics[characteristicHashCodeArgs] else { guard let characteristic = _retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
let data = valueArgs.data let data = valueArgs.data
let typeRawValue = Int(typeNumberArgs) let typeNumber = typeNumberArgs.toInt()
guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeRawValue) else { guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeNumber) else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
let type = typeArgs.toWriteType() let type = typeArgs.toWriteType()
peripheral.writeValue(data, for: characteristic, type: type) peripheral.writeValue(data, for: characteristic, type: type)
if type == .withResponse { if type == .withResponse {
writeCharacteristicCompletions[characteristicHashCodeArgs] = completion _writeCharacteristicCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} else { } else {
completion(.success(())) completion(.success(()))
} }
@ -239,159 +211,149 @@ class MyCentralManager: MyCentralManagerHostApi {
} }
} }
func notifyCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, stateArgs: Bool, completion: @escaping (Result<Void, Error>) -> Void) { func setCharacteristicNotifyState(uuidArgs: String, hashCodeArgs: Int64, stateArgs: Bool, completion: @escaping (Result<Void, Error>) -> Void) {
do { do {
let unfinishedCompletion = notifyCharacteristicCompletions[characteristicHashCodeArgs] guard let peripheral = _peripherals[uuidArgs] else {
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
guard let characteristic = characteristics[characteristicHashCodeArgs] else { guard let characteristic = _retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
let enabled = stateArgs let enabled = stateArgs
peripheral.setNotifyValue(enabled, for: characteristic) peripheral.setNotifyValue(enabled, for: characteristic)
notifyCharacteristicCompletions[characteristicHashCodeArgs] = completion _setCharacteristicNotifyStateCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch { } catch {
completion(.failure(error)) completion(.failure(error))
} }
} }
func readDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) { func readDescriptor(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
do { do {
let unfinishedCompletion = readDescriptorCompletions[descriptorHashCodeArgs] guard let peripheral = _peripherals[uuidArgs] else {
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
guard let descriptor = descriptors[descriptorHashCodeArgs] else { guard let descriptor = _retrieveDescriptor(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
peripheral.readValue(for: descriptor) peripheral.readValue(for: descriptor)
readDescriptorCompletions[descriptorHashCodeArgs] = completion _readDescriptorCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch { } catch {
completion(.failure(error)) completion(.failure(error))
} }
} }
func writeDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void) { func writeDescriptor(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void) {
do { do {
let unfinishedCompletion = writeDescriptorCompletions[descriptorHashCodeArgs] guard let peripheral = _peripherals[uuidArgs] else {
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
guard let descriptor = descriptors[descriptorHashCodeArgs] else { guard let descriptor = _retrieveDescriptor(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument throw MyError.illegalArgument
} }
let data = valueArgs.data let data = valueArgs.data
peripheral.writeValue(data, for: descriptor) peripheral.writeValue(data, for: descriptor)
writeDescriptorCompletions[descriptorHashCodeArgs] = completion _writeDescriptorCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch { } catch {
completion(.failure(error)) completion(.failure(error))
} }
} }
func didUpdateState() { func didUpdateState(central: CBCentralManager) {
let state = centralManager.state _onStateChanged()
let stateArgs = state.toArgs()
let stateNumberArgs = Int64(stateArgs.rawValue)
if state != .unknown && setUpCompletion != nil {
let args = MyCentralManagerArgs(stateNumberArgs: stateNumberArgs)
setUpCompletion!(.success(args))
setUpCompletion = nil
}
api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in }
} }
func didDiscover(_ peripheral: CBPeripheral, _ advertisementData: [String : Any], _ rssi: NSNumber) { func didDiscover(central: CBCentralManager, peripheral: CBPeripheral, advertisementData: [String : Any], rssi: NSNumber) {
let peripheralArgs = peripheral.toArgs() let peripheralArgs = peripheral.toArgs()
let peripheralHashCode = peripheral.hash let uuidArgs = peripheralArgs.uuidArgs
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
peripheral.delegate = peripheralDelegate
peripherals[peripheralHashCodeArgs] = peripheral
peripheralsArgs[peripheralHashCode] = peripheralArgs
let rssiArgs = rssi.int64Value let rssiArgs = rssi.int64Value
let advertisementArgs = advertisementData.toAdvertisementArgs() let advertisementArgs = advertisementData.toAdvertisementArgs()
api.onDiscovered(peripheralArgs: peripheralArgs, rssiArgs: rssiArgs, advertisementArgs: advertisementArgs) {_ in } if peripheral.delegate == nil {
peripheral.delegate = _peripheralDelegate
}
_peripherals[uuidArgs] = peripheral
_api.onDiscovered(peripheralArgs: peripheralArgs, rssiArgs: rssiArgs, advertisementArgs: advertisementArgs) {_ in }
} }
func didConnect(_ peripheral: CBPeripheral) { func didConnect(central: CBCentralManager, peripheral: CBPeripheral) {
let peripheralHashCode = peripheral.hash let uuidArgs = peripheral.identifier.toArgs()
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
let stateArgs = true let stateArgs = true
api.onPeripheralStateChanged(peripheralArgs: peripheralArgs, stateArgs: stateArgs) {_ in } _api.onConnectionStateChanged(uuidArgs: uuidArgs, stateArgs: stateArgs) {_ in }
guard let completion = connectCompletions.removeValue(forKey: peripheralHashCodeArgs) else { guard let completion = _connectCompletions.removeValue(forKey: uuidArgs) else {
return return
} }
completion(.success(())) completion(.success(()))
} }
func didFailToConnect(_ peripheral: CBPeripheral, _ error: Error?) { func didFailToConnect(central: CBCentralManager, peripheral: CBPeripheral, error: Error?) {
let peripheralHashCode = peripheral.hash let uuidArgs = peripheral.identifier.toArgs()
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { guard let completion = _connectCompletions.removeValue(forKey: uuidArgs) else {
return return
} }
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs completion(.failure(error ?? MyError.unknown))
let completion = connectCompletions.removeValue(forKey: peripheralHashCodeArgs)
completion?(.failure(error ?? MyError.unknown))
} }
func didDisconnectPeripheral(_ peripheral: CBPeripheral, _ error: Error?) { func didDisconnectPeripheral(central: CBCentralManager, peripheral: CBPeripheral, error: Error?) {
let peripheralHashCode = peripheral.hash let uuidArgs = peripheral.identifier.toArgs()
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { _services.removeValue(forKey: uuidArgs)
return _characteristics.removeValue(forKey: uuidArgs)
_descriptors.removeValue(forKey: uuidArgs)
let errorNotNil = error ?? MyError.unknown
let readRssiCompletion = _readRssiCompletions.removeValue(forKey: uuidArgs)
readRssiCompletion?(.failure(errorNotNil))
let discoverServicesCompletion = _discoverServicesCompletions.removeValue(forKey: uuidArgs)
discoverServicesCompletion?(.failure(errorNotNil))
let discoverCharacteristicsCompletions = _discoverCharacteristicsCompletions.removeValue(forKey: uuidArgs)
if discoverCharacteristicsCompletions != nil {
let completions = discoverCharacteristicsCompletions!.values
for completion in completions {
completion(.failure(errorNotNil))
} }
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
let readRssiCompletion = readRssiCompletions.removeValue(forKey: peripheralHashCodeArgs)
readRssiCompletion?(.failure(error ?? MyError.unknown))
let discoverGattCompletion = discoverGattCompletions.removeValue(forKey: peripheralHashCodeArgs)
discoverGattCompletion?(.failure(error ?? MyError.unknown))
unfinishedServices.removeValue(forKey: peripheralHashCodeArgs)
unfinishedCharacteristics.removeValue(forKey: peripheralHashCodeArgs)
let servicesArgs = servicesArgsOfPeripheralsArgs.removeValue(forKey: peripheralHashCodeArgs) ?? []
for serviceArgs in servicesArgs {
let serviceHashCodeArgs = serviceArgs.hashCodeArgs
let service = services.removeValue(forKey: serviceHashCodeArgs)!
let serviceHashCode = service.hash
self.servicesArgs.removeValue(forKey: serviceHashCode)
let characteristicsArgs = serviceArgs.characteristicsArgs.map { args in args! }
for characteristicArgs in characteristicsArgs {
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
let characteristic = characteristics.removeValue(forKey: characteristicHashCodeArgs)!
let characteristicHashCode = characteristic.hash
self.characteristicsArgs.removeValue(forKey: characteristicHashCode)
let readCharacteristicCompletion = readCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs)
let writeCharacteristicCompletion = writeCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs)
let notifyCharacteristicCompletion = notifyCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs)
readCharacteristicCompletion?(.failure(error ?? MyError.unknown))
writeCharacteristicCompletion?(.failure(error ?? MyError.unknown))
notifyCharacteristicCompletion?(.failure(error ?? MyError.unknown))
let descriptorsArgs = characteristicArgs.descriptorsArgs.map { args in args! }
for descriptorArgs in descriptorsArgs {
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
let descriptor = descriptors.removeValue(forKey: descriptorHashCodeArgs)!
let descriptorHashCode = descriptor.hash
self.descriptorsArgs.removeValue(forKey: descriptorHashCode)
let readDescriptorCompletion = readDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs)
let writeDescriptorCompletion = writeDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs)
readDescriptorCompletion?(.failure(error ?? MyError.unknown))
writeDescriptorCompletion?(.failure(error ?? MyError.unknown))
} }
let discoverDescriptorsCompletions = _discoverDescriptorsCompletions.removeValue(forKey: uuidArgs)
if discoverDescriptorsCompletions != nil {
let completions = discoverDescriptorsCompletions!.values
for completion in completions {
completion(.failure(errorNotNil))
}
}
let readCharacteristicCompletions = _readCharacteristicCompletions.removeValue(forKey: uuidArgs)
if readCharacteristicCompletions != nil {
let completions = readCharacteristicCompletions!.values
for completion in completions {
completion(.failure(errorNotNil))
}
}
let writeCharacteristicCompletions = _writeCharacteristicCompletions.removeValue(forKey: uuidArgs)
if writeCharacteristicCompletions != nil {
let completions = writeCharacteristicCompletions!.values
for completion in completions {
completion(.failure(errorNotNil))
}
}
let notifyCharacteristicCompletions = _setCharacteristicNotifyStateCompletions.removeValue(forKey: uuidArgs)
if notifyCharacteristicCompletions != nil {
let completions = notifyCharacteristicCompletions!.values
for completioin in completions {
completioin(.failure(errorNotNil))
}
}
let readDescriptorCompletions = _readDescriptorCompletions.removeValue(forKey: uuidArgs)
if readDescriptorCompletions != nil {
let completions = readDescriptorCompletions!.values
for completioin in completions {
completioin(.failure(errorNotNil))
}
}
let writeDescriptorCompletions = _writeDescriptorCompletions.removeValue(forKey: uuidArgs)
if writeDescriptorCompletions != nil {
let completions = writeDescriptorCompletions!.values
for completion in completions {
completion(.failure(errorNotNil))
} }
} }
let stateArgs = false let stateArgs = false
api.onPeripheralStateChanged(peripheralArgs: peripheralArgs, stateArgs: stateArgs) {_ in } _api.onConnectionStateChanged(uuidArgs: uuidArgs, stateArgs: stateArgs) {_ in }
guard let completion = disconnectCompletions.removeValue(forKey: peripheralHashCodeArgs) else { guard let completion = _disconnectCompletions.removeValue(forKey: uuidArgs) else {
return return
} }
if error == nil { if error == nil {
@ -401,13 +363,9 @@ class MyCentralManager: MyCentralManagerHostApi {
} }
} }
func didReadRSSI(_ peripheral: CBPeripheral, _ rssi: NSNumber, _ error: Error?) { func didReadRSSI(peripheral: CBPeripheral, rssi: NSNumber, error: Error?) {
let peripheralHashCode = peripheral.hash let uuidArgs = peripheral.identifier.toArgs()
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { guard let completion = _readRssiCompletions.removeValue(forKey: uuidArgs) else {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
guard let completion = readRssiCompletions.removeValue(forKey: peripheralHashCodeArgs) else {
return return
} }
if error == nil { if error == nil {
@ -418,157 +376,110 @@ class MyCentralManager: MyCentralManagerHostApi {
} }
} }
func didDiscoverServices(_ peripheral: CBPeripheral, _ error: Error?) { func didDiscoverServices(peripheral: CBPeripheral, error: Error?) {
let peripheralHashCode = peripheral.hash let uuidArgs = peripheral.identifier.toArgs()
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else { guard let completion = _discoverServicesCompletions.removeValue(forKey: uuidArgs) else {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
if error == nil {
var services = peripheral.services ?? []
if services.isEmpty {
didDiscoverGATT(peripheral, error)
} else {
let service = services.removeFirst()
unfinishedServices[peripheralHashCodeArgs] = services
peripheral.discoverCharacteristics(nil, for: service)
}
} else {
didDiscoverGATT(peripheral, error)
}
}
func didDiscoverCharacteristics(_ peripheral: CBPeripheral, _ service: CBService, _ error: Error?) {
let peripheralHashCode = peripheral.hash
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
if error == nil {
var characteristics = service.characteristics ?? []
if characteristics.isEmpty {
var services = unfinishedServices.removeValue(forKey: peripheralHashCodeArgs) ?? []
if services.isEmpty {
didDiscoverGATT(peripheral, error)
} else {
let service = services.removeFirst()
unfinishedServices[peripheralHashCodeArgs] = services
peripheral.discoverCharacteristics(nil, for: service)
}
} else {
let characteristic = characteristics.removeFirst()
unfinishedCharacteristics[peripheralHashCodeArgs] = characteristics
peripheral.discoverDescriptors(for: characteristic)
}
} else {
didDiscoverGATT(peripheral, error)
}
}
func didDiscoverDescriptors(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic, _ error: Error?) {
let peripheralHashCode = peripheral.hash
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
if error == nil {
var characteristics = unfinishedCharacteristics.removeValue(forKey: peripheralHashCodeArgs) ?? []
if (characteristics.isEmpty) {
var services = unfinishedServices.removeValue(forKey: peripheralHashCodeArgs) ?? []
if services.isEmpty {
didDiscoverGATT(peripheral, error)
} else {
let service = services.removeFirst()
unfinishedServices[peripheralHashCodeArgs] = services
peripheral.discoverCharacteristics(nil, for: service)
}
} else {
let characteristic = characteristics.removeFirst()
unfinishedCharacteristics[peripheralHashCodeArgs] = characteristics
peripheral.discoverDescriptors(for: characteristic)
}
} else {
didDiscoverGATT(peripheral, error)
}
}
private func didDiscoverGATT(_ peripheral: CBPeripheral, _ error: Error?) {
let peripheralHashCode = peripheral.hash
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
return
}
let peripheralhashCodeArgs = peripheralArgs.hashCodeArgs
guard let completion = discoverGattCompletions.removeValue(forKey: peripheralhashCodeArgs) else {
return return
} }
if error == nil { if error == nil {
let services = peripheral.services ?? [] let services = peripheral.services ?? []
var servicesArgs = [MyGattServiceArgs]() let servicesArgs = services.map { service in service.toArgs() }
for service in services { let elements = services.flatMap { service in
let characteristics = service.characteristics ?? [] let hashCodeArgs = service.hash.toInt64()
var characteristicsArgs = [MyGattCharacteristicArgs]() return [hashCodeArgs: service]
for characteristic in characteristics {
let descriptors = characteristic.descriptors ?? []
var descriptorsArgs = [MyGattDescriptorArgs]()
for descriptor in descriptors {
let descriptorArgs = descriptor.toArgs()
let descriptorHashCode = descriptor.hash
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
self.descriptors[descriptorHashCodeArgs] = descriptor
self.descriptorsArgs[descriptorHashCode] = descriptorArgs
descriptorsArgs.append(descriptorArgs)
} }
let characteristicArgs = characteristic.toArgs(descriptorsArgs) var items = _services[uuidArgs]
let characteristicHashCode = characteristic.hash if items == nil {
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs _services[uuidArgs] = Dictionary(uniqueKeysWithValues: elements)
self.characteristics[characteristicHashCodeArgs] = characteristic } else {
self.characteristicsArgs[characteristicHashCode] = characteristicArgs items!.merge(elements) { service1, service2 in service2 }
characteristicsArgs.append(characteristicArgs)
} }
let serviceArgs = service.toArgs(characteristicsArgs) completion(.success(servicesArgs))
let serviceHashCode = service.hash
let servcieHashCodeArgs = serviceArgs.hashCodeArgs
self.services[servcieHashCodeArgs] = service
self.servicesArgs[serviceHashCode] = serviceArgs
servicesArgs.append(serviceArgs)
}
servicesArgsOfPeripheralsArgs[peripheralhashCodeArgs] = servicesArgs
completion(.success((servicesArgs)))
} else { } else {
completion(.failure(error!)) completion(.failure(error!))
unfinishedServices.removeValue(forKey: peripheralhashCodeArgs)
unfinishedCharacteristics.removeValue(forKey: peripheralhashCodeArgs)
} }
} }
func didUpdateCharacteristicValue(_ characteristic: CBCharacteristic, _ error: Error?) { func didDiscoverCharacteristics(peripheral: CBPeripheral, service: CBService, error: Error?) {
let characteristicHashCode = characteristic.hash let uuidArgs = peripheral.identifier.toArgs()
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else { let hashCodeArgs = service.hash.toInt64()
guard var completions = _discoverCharacteristicsCompletions[uuidArgs] else {
return return
} }
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
guard let completion = readCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) else {
let value = characteristic.value ?? Data()
let valueArgs = FlutterStandardTypedData(bytes: value)
api.onCharacteristicValueChanged(characteristicArgs: characteristicArgs, valueArgs: valueArgs) {_ in }
return return
} }
if error == nil { if error == nil {
let characteristics = service.characteristics ?? []
let characteristicsArgs = characteristics.map { characteristic in characteristic.toArgs() }
let elements = characteristics.flatMap { characteristic in
let hashCodeArgs = characteristic.hash.toInt64()
return [hashCodeArgs: characteristic]
}
var items = _characteristics[uuidArgs]
if items == nil {
_characteristics[uuidArgs] = Dictionary(uniqueKeysWithValues: elements)
} else {
items!.merge(elements) { characteristic1, characteristic2 in characteristic2 }
}
completion(.success(characteristicsArgs))
} else {
completion(.failure(error!))
}
}
func didDiscoverDescriptors(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
let hashCodeArgs = characteristic.hash.toInt64()
guard var completions = _discoverDescriptorsCompletions[uuidArgs] else {
return
}
guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
return
}
if error == nil {
let descriptors = characteristic.descriptors ?? []
let descriptorsArgs = descriptors.map { descriptor in descriptor.toArgs() }
let elements = descriptors.flatMap { descriptor in
let hashCodeArgs = descriptor.hash.toInt64()
return [hashCodeArgs: descriptor]
}
var items = _descriptors[uuidArgs]
if items == nil {
_descriptors[uuidArgs] = Dictionary(uniqueKeysWithValues: elements)
} else {
items!.merge(elements) { descriptor1, descriptor2 in descriptor2 }
}
completion(.success(descriptorsArgs))
} else {
completion(.failure(error!))
}
}
func didUpdateCharacteristicValue(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
let hashCodeArgs = characteristic.hash.toInt64()
let value = characteristic.value ?? Data() let value = characteristic.value ?? Data()
let valueArgs = FlutterStandardTypedData(bytes: value) let valueArgs = FlutterStandardTypedData(bytes: value)
var completions = _readCharacteristicCompletions[uuidArgs]
guard let completion = completions?.removeValue(forKey: hashCodeArgs) else {
_api.onCharacteristicNotified(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs, valueArgs: valueArgs) {_ in }
return
}
if error == nil {
completion(.success(valueArgs)) completion(.success(valueArgs))
} else { } else {
completion(.failure(error!)) completion(.failure(error!))
} }
} }
func didWriteCharacteristicValue(_ characteristic: CBCharacteristic, _ error: Error?) { func didWriteCharacteristicValue(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) {
let characteristicHashCode = characteristic.hash let uuidArgs = peripheral.identifier.toArgs()
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else { let hashCodeArgs = characteristic.hash.toInt64()
guard var completions = _writeCharacteristicCompletions[uuidArgs] else {
return return
} }
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
guard let completion = writeCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) else {
return return
} }
if error == nil { if error == nil {
@ -578,13 +489,13 @@ class MyCentralManager: MyCentralManagerHostApi {
} }
} }
func didUpdateNotificationState(_ characteristic: CBCharacteristic, _ error: Error?) { func didUpdateCharacteristicNotificationState(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) {
let characteristicHashCode = characteristic.hash let uuidArgs = peripheral.identifier.toArgs()
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else { let hashCodeArgs = characteristic.hash.toInt64()
guard var completions = _setCharacteristicNotifyStateCompletions[uuidArgs] else {
return return
} }
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
guard let completion = notifyCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) else {
return return
} }
if error == nil { if error == nil {
@ -594,17 +505,17 @@ class MyCentralManager: MyCentralManagerHostApi {
} }
} }
func didUpdateDescriptorValue(_ descriptor: CBDescriptor, _ error: Error?) { func didUpdateDescriptorValue(peripheral: CBPeripheral, descriptor: CBDescriptor, error: Error?) {
let descriptorHashCode = descriptor.hash let uuidArgs = peripheral.identifier.toArgs()
guard let descriptorArgs = descriptorsArgs[descriptorHashCode] else { let hashCodeArgs = descriptor.hash.toInt64()
guard var completions = _readDescriptorCompletions[uuidArgs] else {
return return
} }
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
guard let completion = readDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs) else {
return return
} }
if error == nil { if error == nil {
// TODO: Need to confirm wheather the corresponding descriptor type and value is correct. // TODO: confirm the corresponding descriptor types and values are correct.
let valueArgs: FlutterStandardTypedData let valueArgs: FlutterStandardTypedData
let value = descriptor.value let value = descriptor.value
do { do {
@ -647,13 +558,13 @@ class MyCentralManager: MyCentralManagerHostApi {
} }
} }
func didWriteDescriptorValue(_ descriptor: CBDescriptor, _ error: Error?) { func didWriteDescriptorValue(peripheral: CBPeripheral, descriptor: CBDescriptor, error: Error?) {
let descriptorHashCode = descriptor.hash let uuidArgs = peripheral.identifier.toArgs()
guard let descriptorArgs = descriptorsArgs[descriptorHashCode] else { let hashCodeArgs = descriptor.hash.toInt64()
guard var completions = _writeDescriptorCompletions[uuidArgs] else {
return return
} }
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
guard let completion = writeDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs) else {
return return
} }
if error == nil { if error == nil {
@ -662,4 +573,60 @@ class MyCentralManager: MyCentralManagerHostApi {
completion(.failure(error!)) completion(.failure(error!))
} }
} }
private func _clearState() {
if(_centralManager.isScanning) {
_centralManager.stopScan()
}
for peripheral in _peripherals.values {
if peripheral.state != .disconnected {
_centralManager.cancelPeripheralConnection(peripheral)
}
}
_peripherals.removeAll()
_services.removeAll()
_characteristics.removeAll()
_descriptors.removeAll()
_connectCompletions.removeAll()
_disconnectCompletions.removeAll()
_readRssiCompletions.removeAll()
_discoverServicesCompletions.removeAll()
_discoverCharacteristicsCompletions.removeAll()
_discoverDescriptorsCompletions.removeAll()
_readCharacteristicCompletions.removeAll()
_writeCharacteristicCompletions.removeAll()
_setCharacteristicNotifyStateCompletions.removeAll()
_readDescriptorCompletions.removeAll()
_writeDescriptorCompletions.removeAll()
}
private func _retrieveService(uuidArgs: String, hashCodeArgs: Int64) -> CBService? {
guard let services = _services[uuidArgs] else {
return nil
}
return services[hashCodeArgs]
}
private func _retrieveCharacteristic(uuidArgs: String, hashCodeArgs: Int64) -> CBCharacteristic? {
guard let characteristics = _characteristics[uuidArgs] else {
return nil
}
return characteristics[hashCodeArgs]
}
private func _retrieveDescriptor(uuidArgs: String, hashCodeArgs: Int64) -> CBDescriptor? {
guard let descriptors = _descriptors[uuidArgs] else {
return nil
}
return descriptors[hashCodeArgs]
}
private func _onStateChanged() {
let state = _centralManager.state
let stateArgs = state.toArgs()
let stateNumberArgs = stateArgs.rawValue.toInt64()
_api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in }
}
} }

View File

@ -9,29 +9,29 @@ import Foundation
import CoreBluetooth import CoreBluetooth
class MyCentralManagerDelegate: NSObject, CBCentralManagerDelegate { class MyCentralManagerDelegate: NSObject, CBCentralManagerDelegate {
init(_ centralManager: MyCentralManager) { private let _centralManager: MyCentralManager
self.centralManager = centralManager
init(centralManager: MyCentralManager) {
_centralManager = centralManager
} }
private let centralManager: MyCentralManager
func centralManagerDidUpdateState(_ central: CBCentralManager) { func centralManagerDidUpdateState(_ central: CBCentralManager) {
centralManager.didUpdateState() _centralManager.didUpdateState(central: central)
} }
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
centralManager.didDiscover(peripheral, advertisementData, RSSI) _centralManager.didDiscover(central: central, peripheral: peripheral, advertisementData: advertisementData, rssi: RSSI)
} }
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
centralManager.didConnect(peripheral) _centralManager.didConnect(central: central, peripheral: peripheral)
} }
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
centralManager.didFailToConnect(peripheral, error) _centralManager.didFailToConnect(central: central, peripheral: peripheral, error: error)
} }
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
centralManager.didDisconnectPeripheral(peripheral, error) _centralManager.didDisconnectPeripheral(central: central, peripheral: peripheral, error: error)
} }
} }

View File

@ -10,6 +10,5 @@ import Foundation
// TODO: // TODO:
enum MyError: Error { enum MyError: Error {
case illegalArgument case illegalArgument
case illegalState
case unknown case unknown
} }

View File

@ -9,45 +9,45 @@ import Foundation
import CoreBluetooth import CoreBluetooth
class MyPeripheralDelegate: NSObject, CBPeripheralDelegate { class MyPeripheralDelegate: NSObject, CBPeripheralDelegate {
private let centralManager: MyCentralManager private let _centralManager: MyCentralManager
init(_ centralManager: MyCentralManager) { init(centralManager: MyCentralManager) {
self.centralManager = centralManager _centralManager = centralManager
} }
func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) { func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) {
centralManager.didReadRSSI(peripheral, RSSI, error) _centralManager.didReadRSSI(peripheral: peripheral, rssi: RSSI, error: error)
} }
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
centralManager.didDiscoverServices(peripheral, error) _centralManager.didDiscoverServices(peripheral: peripheral, error: error)
} }
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
centralManager.didDiscoverCharacteristics(peripheral, service, error) _centralManager.didDiscoverCharacteristics(peripheral: peripheral, service: service, error: error)
} }
func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) { func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) {
centralManager.didDiscoverDescriptors(peripheral, characteristic, error) _centralManager.didDiscoverDescriptors(peripheral: peripheral, characteristic: characteristic, error: error)
} }
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
centralManager.didUpdateCharacteristicValue(characteristic, error) _centralManager.didUpdateCharacteristicValue(peripheral: peripheral, characteristic: characteristic, error: error)
} }
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
centralManager.didWriteCharacteristicValue(characteristic, error) _centralManager.didWriteCharacteristicValue(peripheral: peripheral, characteristic: characteristic, error: error)
} }
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) { func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
centralManager.didUpdateNotificationState(characteristic, error) _centralManager.didUpdateCharacteristicNotificationState(peripheral: peripheral, characteristic: characteristic, error: error)
} }
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?) { func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?) {
centralManager.didUpdateDescriptorValue(descriptor, error) _centralManager.didUpdateDescriptorValue(peripheral: peripheral, descriptor: descriptor, error: error)
} }
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) { func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
centralManager.didWriteDescriptorValue(descriptor, error) _centralManager.didWriteDescriptorValue(peripheral: peripheral, descriptor: descriptor, error: error)
} }
} }

View File

@ -17,76 +17,53 @@ import FlutterMacOS
#endif #endif
class MyPeripheralManager: MyPeripheralManagerHostApi { class MyPeripheralManager: MyPeripheralManagerHostApi {
init(_ binaryMessenger: FlutterBinaryMessenger) { private let _api: MyPeripheralManagerFlutterApi
self.binaryMessenger = binaryMessenger private let _peripheralManager: CBPeripheralManager
private lazy var _peripheralManagerDelegate = MyPeripheralManagerDelegate(peripheralManager: self)
private var _servicesArgs: [Int: MyGattServiceArgs]
private var _characteristicsArgs: [Int: MyGattCharacteristicArgs]
private var _descriptorsArgs: [Int: MyGattDescriptorArgs]
private var _centrals: [String: CBCentral]
private var _services: [Int64: CBMutableService]
private var _characteristics: [Int64: CBMutableCharacteristic]
private var _descriptors: [Int64: CBMutableDescriptor]
private var _requests: [Int64: CBATTRequest]
private var _addServiceCompletion: ((Result<Void, Error>) -> Void)?
private var _startAdvertisingCompletion: ((Result<Void, Error>) -> Void)?
private var _isReadyToUpdateSubscribersCallbacks: [() -> Void]
init(messenger: FlutterBinaryMessenger) {
_api = MyPeripheralManagerFlutterApi(binaryMessenger: messenger)
_peripheralManager = CBPeripheralManager()
_servicesArgs = [:]
_characteristicsArgs = [:]
_descriptorsArgs = [:]
_centrals = [:]
_services = [:]
_characteristics = [:]
_descriptors = [:]
_requests = [:]
_addServiceCompletion = nil
_startAdvertisingCompletion = nil
_isReadyToUpdateSubscribersCallbacks = []
} }
private let binaryMessenger: FlutterBinaryMessenger func setUp() throws {
private let peripheralManager = CBPeripheralManager() _clearState()
if _peripheralManager.delegate == nil {
private lazy var api = MyPeripheralManagerFlutterApi(binaryMessenger: binaryMessenger) _peripheralManager.delegate = _peripheralManagerDelegate
private lazy var peripheralManagerDelegate = MyPeripheralManagerDelegate(self)
private var centrals = [Int64: CBCentral]()
private var services = [Int64: CBMutableService]()
private var characteristics = [Int64: CBMutableCharacteristic]()
private var descriptors = [Int64: CBMutableDescriptor]()
private var requests = [Int64: CBATTRequest]()
private var centralsArgs = [Int: MyCentralArgs]()
private var servicesArgs = [Int: MyGattServiceArgs]()
private var characteristicsArgs = [Int: MyGattCharacteristicArgs]()
private var descriptorsArgs = [Int: MyGattDescriptorArgs]()
private var setUpCompletion: ((Result<MyPeripheralManagerArgs, Error>) -> Void)?
private var addServiceCompletion: ((Result<Void, Error>) -> Void)?
private var startAdvertisingCompletion: ((Result<Void, Error>) -> Void)?
private var notifyCharacteristicValueChangedCallbacks = [() -> Void]()
func setUp(completion: @escaping (Result<MyPeripheralManagerArgs, Error>) -> Void) {
do {
if setUpCompletion != nil {
throw MyError.illegalState
} }
try tearDown() _onStateChanged()
peripheralManager.delegate = peripheralManagerDelegate
if peripheralManager.state == .unknown {
setUpCompletion = completion
} else {
let stateArgs = peripheralManager.state.toArgs()
let stateNumberArgs = Int64(stateArgs.rawValue)
let args = MyPeripheralManagerArgs(stateNumberArgs: stateNumberArgs)
completion(.success(args))
}
} catch {
completion(.failure(error))
}
}
func tearDown() throws {
if(peripheralManager.isAdvertising) {
peripheralManager.stopAdvertising()
}
centrals.removeAll()
services.removeAll()
characteristics.removeAll()
descriptors.removeAll()
requests.removeAll()
centralsArgs.removeAll()
servicesArgs.removeAll()
characteristicsArgs.removeAll()
descriptorsArgs.removeAll()
setUpCompletion = nil
addServiceCompletion = nil
startAdvertisingCompletion = nil
notifyCharacteristicValueChangedCallbacks.removeAll()
} }
func addService(serviceArgs: MyGattServiceArgs, completion: @escaping (Result<Void, Error>) -> Void) { func addService(serviceArgs: MyGattServiceArgs, completion: @escaping (Result<Void, Error>) -> Void) {
do {
if addServiceCompletion != nil {
throw MyError.illegalState
}
let service = serviceArgs.toService() let service = serviceArgs.toService()
var characteristics = [CBMutableCharacteristic]() var characteristics = [CBMutableCharacteristic]()
let characteristicsArgs = serviceArgs.characteristicsArgs let characteristicsArgs = serviceArgs.characteristicsArgs
@ -95,6 +72,7 @@ class MyPeripheralManager: MyPeripheralManagerHostApi {
continue continue
} }
let characteristic = characteristicArgs.toCharacteristic() let characteristic = characteristicArgs.toCharacteristic()
characteristics.append(characteristic)
var descriptors = [CBMutableDescriptor]() var descriptors = [CBMutableDescriptor]()
let descriptorsArgs = characteristicArgs.descriptorsArgs let descriptorsArgs = characteristicArgs.descriptorsArgs
for args in descriptorsArgs { for args in descriptorsArgs {
@ -105,50 +83,242 @@ class MyPeripheralManager: MyPeripheralManagerHostApi {
descriptors.append(descriptor) descriptors.append(descriptor)
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
let descriptorHashCode = descriptor.hash let descriptorHashCode = descriptor.hash
self.descriptorsArgs[descriptorHashCode] = descriptorArgs _descriptorsArgs[descriptorHashCode] = descriptorArgs
self.descriptors[descriptorHashCodeArgs] = descriptor _descriptors[descriptorHashCodeArgs] = descriptor
} }
characteristic.descriptors = descriptors characteristic.descriptors = descriptors
characteristics.append(characteristic)
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
let characteristicHashCode = characteristic.hash let characteristicHashCode = characteristic.hash
self.characteristicsArgs[characteristicHashCode] = characteristicArgs _characteristicsArgs[characteristicHashCode] = characteristicArgs
self.characteristics[characteristicHashCodeArgs] = characteristic _characteristics[characteristicHashCodeArgs] = characteristic
} }
service.characteristics = characteristics service.characteristics = characteristics
let serviceHashCodeArgs = serviceArgs.hashCodeArgs let serviceHashCodeArgs = serviceArgs.hashCodeArgs
let serviceHashCode = service.hash let serviceHashCode = service.hash
self.servicesArgs[serviceHashCode] = serviceArgs _servicesArgs[serviceHashCode] = serviceArgs
self.services[serviceHashCodeArgs] = service _services[serviceHashCodeArgs] = service
peripheralManager.add(service) _peripheralManager.add(service)
addServiceCompletion = completion _addServiceCompletion = completion
}
func removeService(hashCodeArgs: Int64) throws {
guard let service = _services[hashCodeArgs] else {
throw MyError.illegalArgument
}
let hashCode = service.hash
guard let serviceArgs = _servicesArgs[hashCode] else {
throw MyError.illegalArgument
}
_peripheralManager.remove(service)
_clearService(serviceArgs: serviceArgs)
}
func clearServices() throws {
_peripheralManager.removeAllServices()
let servicesArgs = _servicesArgs.values
for serviceArgs in servicesArgs {
_clearService(serviceArgs: serviceArgs)
}
}
func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result<Void, 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) {
do {
let centrals = try uuidsArgs?.map { uuidArgs in
guard let central = _centrals[uuidArgs] else {
throw MyError.illegalArgument
}
return central
}
guard let characteristic = _characteristics[hashCodeArgs] else {
throw MyError.illegalArgument
}
let value = valueArgs.data
let updated = _peripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals)
if updated {
completion(.success(()))
} else {
_isReadyToUpdateSubscribersCallbacks.append {
self.updateCharacteristic(hashCodeArgs: hashCodeArgs, valueArgs: valueArgs, uuidsArgs: uuidsArgs, completion: completion)
}
}
} catch { } catch {
freeService(serviceArgs)
completion(.failure(error)) completion(.failure(error))
} }
} }
func removeService(serviceHashCodeArgs: Int64) throws { func didUpdateState(peripheral: CBPeripheralManager) {
guard let service = services[serviceHashCodeArgs] else { _onStateChanged()
throw MyError.illegalArgument
}
let serviceHashCode = service.hash
guard let serviceArgs = servicesArgs[serviceHashCode] else {
throw MyError.illegalArgument
}
peripheralManager.remove(service)
freeService(serviceArgs)
} }
func clearServices() throws { func didAdd(peripheral: CBPeripheralManager, service: CBService, error: Error?) {
peripheralManager.removeAllServices() guard let completion = _addServiceCompletion else {
let servicesArgs = self.servicesArgs.values return
for serviceArgs in servicesArgs { }
freeService(serviceArgs) _addServiceCompletion = nil
if error == nil {
completion(.success(()))
} else {
completion(.failure(error!))
let hashCode = service.hash
guard let serviceArgs = _servicesArgs[hashCode] else {
return
}
_clearService(serviceArgs: serviceArgs)
} }
} }
private func freeService(_ serviceArgs: MyGattServiceArgs) { func didStartAdvertising(peripheral: CBPeripheralManager, error: Error?) {
guard let completion = _startAdvertisingCompletion else {
return
}
_startAdvertisingCompletion = nil
if error == nil {
completion(.success(()))
} else {
completion(.failure(error!))
}
}
func didReceiveRead(peripheral: CBPeripheralManager, request: CBATTRequest) {
let central = request.central
let centralArgs = central.toArgs()
_centrals[centralArgs.uuidArgs] = central
let characteristic = request.characteristic
let hashCode = characteristic.hash
guard let characteristicArgs = _characteristicsArgs[hashCode] else {
_peripheralManager.respond(to: request, withResult: .attributeNotFound)
return
}
let hashCodeArgs = characteristicArgs.hashCodeArgs
let idArgs = request.hash.toInt64()
let offsetArgs = request.offset.toInt64()
_requests[idArgs] = request
_api.onCharacteristicReadRequest(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, idArgs: idArgs, offsetArgs: offsetArgs) {_ in }
}
func didReceiveWrite(peripheral: CBPeripheralManager, requests: [CBATTRequest]) {
// When you respond to a write request, note that the first parameter of the respond(to:with
// Result:) method expects a single CBATTRequest object, even though you received an array of
// them from the peripheralManager(_:didReceiveWrite:) method. To respond properly,
// pass in the first request of the requests array.
// see: https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393315-peripheralmanager#discussion
guard let request = requests.first else {
return
}
if requests.count > 1 {
// TODO:
let result = CBATTError.requestNotSupported
_peripheralManager.respond(to: request, withResult: result)
return
}
let central = request.central
let centralArgs = central.toArgs()
_centrals[centralArgs.uuidArgs] = central
let characteristic = request.characteristic
let hashCode = characteristic.hash
guard let characteristicArgs = _characteristicsArgs[hashCode] else {
_peripheralManager.respond(to: request, withResult: .attributeNotFound)
return
}
let hashCodeArgs = characteristicArgs.hashCodeArgs
let idArgs = request.hash.toInt64()
let offsetArgs = request.offset.toInt64()
guard let value = request.value else {
_peripheralManager.respond(to: request, withResult: .requestNotSupported)
return
}
let valueArgs = FlutterStandardTypedData(bytes: value)
_requests[idArgs] = request
_api.onCharacteristicWriteRequest(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, idArgs: idArgs, offsetArgs: offsetArgs, valueArgs: valueArgs) {_ in }
}
func didSubscribeTo(peripheral: CBPeripheralManager, central: CBCentral, characteristic: CBCharacteristic) {
let centralArgs = central.toArgs()
_centrals[centralArgs.uuidArgs] = central
let hashCode = characteristic.hash
guard let characteristicArgs = _characteristicsArgs[hashCode] else {
return
}
let hashCodeArgs = characteristicArgs.hashCodeArgs
let stateArgs = true
_api.onCharacteristicNotifyStateChanged(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, stateArgs: stateArgs) {_ in }
}
func didUnsubscribeFrom(peripheral: CBPeripheralManager, central: CBCentral, characteristic: CBCharacteristic) {
let centralArgs = central.toArgs()
_centrals[centralArgs.uuidArgs] = central
let hashCode = characteristic.hash
guard let characteristicArgs = _characteristicsArgs[hashCode] else {
return
}
let hashCodeArgs = characteristicArgs.hashCodeArgs
let stateArgs = false
_api.onCharacteristicNotifyStateChanged(centralArgs: centralArgs, hashCodeArgs: hashCodeArgs, stateArgs: stateArgs) {_ in }
}
func isReadyToUpdateSubscribers(peripheral: CBPeripheralManager) {
let callbacks = _isReadyToUpdateSubscribersCallbacks
_isReadyToUpdateSubscribersCallbacks.removeAll()
for callback in callbacks {
callback()
}
}
private func _clearState() {
if(_peripheralManager.isAdvertising) {
_peripheralManager.stopAdvertising()
}
_servicesArgs.removeAll()
_characteristicsArgs.removeAll()
_descriptorsArgs.removeAll()
_centrals.removeAll()
_services.removeAll()
_characteristics.removeAll()
_descriptors.removeAll()
_requests.removeAll()
_addServiceCompletion = nil
_startAdvertisingCompletion = nil
_isReadyToUpdateSubscribersCallbacks.removeAll()
}
private func _clearService(serviceArgs: MyGattServiceArgs) {
let characteristicsArgs = serviceArgs.characteristicsArgs let characteristicsArgs = serviceArgs.characteristicsArgs
for args in characteristicsArgs { for args in characteristicsArgs {
guard let characteristicArgs = args else { guard let characteristicArgs = args else {
@ -160,223 +330,31 @@ class MyPeripheralManager: MyPeripheralManagerHostApi {
continue continue
} }
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
guard let descriptor = self.descriptors.removeValue(forKey: descriptorHashCodeArgs) else { guard let descriptor = _descriptors.removeValue(forKey: descriptorHashCodeArgs) else {
continue continue
} }
let descriptorHashCode = descriptor.hash let descriptorHashCode = descriptor.hash
self.descriptorsArgs.removeValue(forKey: descriptorHashCode) _descriptorsArgs.removeValue(forKey: descriptorHashCode)
} }
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
guard let characteristic = self.characteristics.removeValue(forKey: characteristicHashCodeArgs) else { guard let characteristic = _characteristics.removeValue(forKey: characteristicHashCodeArgs) else {
continue continue
} }
let characteristicHashCode = characteristic.hash let characteristicHashCode = characteristic.hash
self.characteristicsArgs.removeValue(forKey: characteristicHashCode) _characteristicsArgs.removeValue(forKey: characteristicHashCode)
} }
let serviceHashCodeArgs = serviceArgs.hashCodeArgs let serviceHashCodeArgs = serviceArgs.hashCodeArgs
guard let service = self.services.removeValue(forKey: serviceHashCodeArgs) else { guard let service = _services.removeValue(forKey: serviceHashCodeArgs) else {
return return
} }
let serviceHashCode = service.hash let serviceHashCode = service.hash
self.servicesArgs.removeValue(forKey: serviceHashCode) _servicesArgs.removeValue(forKey: serviceHashCode)
} }
func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result<Void, Error>) -> Void) { private func _onStateChanged() {
do { let state = _peripheralManager.state
if startAdvertisingCompletion != nil {
throw MyError.illegalState
}
let advertisement = try advertisementArgs.toAdvertisement()
peripheralManager.startAdvertising(advertisement)
startAdvertisingCompletion = completion
} catch {
completion(.failure(error))
}
}
func stopAdvertising() throws {
peripheralManager.stopAdvertising()
}
func getMaximumWriteLength(centralHashCodeArgs: Int64) throws -> Int64 {
guard let central = centrals[centralHashCodeArgs] else {
throw MyError.illegalArgument
}
let maximumWriteLength = try central.maximumUpdateValueLength.coerceIn(20, 512)
let maximumWriteLengthArgs = Int64(maximumWriteLength)
return maximumWriteLengthArgs
}
func sendReadCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool, valueArgs: FlutterStandardTypedData) throws {
guard let request = requests.removeValue(forKey: idArgs) else {
throw MyError.illegalArgument
}
request.value = valueArgs.data
let result = statusArgs ? CBATTError.Code.success : CBATTError.Code.requestNotSupported
peripheralManager.respond(to: request, withResult: result)
}
func sendWriteCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool) throws {
guard let request = requests.removeValue(forKey: idArgs) else {
throw MyError.illegalArgument
}
let result = statusArgs ? CBATTError.Code.success : CBATTError.Code.requestNotSupported
peripheralManager.respond(to: request, withResult: result)
}
func notifyCharacteristicValueChanged(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void) {
do {
if notifyCharacteristicValueChangedCallbacks.count > 0 {
throw MyError.illegalState
}
let value = valueArgs.data
guard let characteristic = characteristics[characteristicHashCodeArgs] else {
throw MyError.illegalArgument
}
guard let central = centrals[centralHashCodeArgs] else {
throw MyError.illegalArgument
}
let centrals = [central]
let updated = peripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals)
if updated {
completion(.success(()))
} else {
notifyCharacteristicValueChangedCallbacks.append {
let updated = self.peripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals)
if updated {
completion(.success(()))
} else {
completion(.failure(MyError.unknown))
}
}
}
} catch {
completion(.failure(error))
}
}
func didUpdateState() {
let state = peripheralManager.state
let stateArgs = state.toArgs() let stateArgs = state.toArgs()
let stateNumberArgs = Int64(stateArgs.rawValue) let stateNumberArgs = stateArgs.rawValue.toInt64()
if state != .unknown && setUpCompletion != nil { _api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in }
let args = MyPeripheralManagerArgs(stateNumberArgs: stateNumberArgs)
setUpCompletion!(.success(args))
setUpCompletion = nil
}
api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in }
}
func didAdd(_ service: CBService, _ error: Error?) {
guard let completion = addServiceCompletion else {
return
}
addServiceCompletion = nil
if error == nil {
completion(.success(()))
} else {
completion(.failure(error!))
let serviceHashCode = service.hash
guard let serviceArgs = servicesArgs[serviceHashCode] else {
return
}
freeService(serviceArgs)
}
}
func didStartAdvertising(_ error: Error?) {
guard let completion = startAdvertisingCompletion else {
return
}
startAdvertisingCompletion = nil
if error == nil {
completion(.success(()))
} else {
completion(.failure(error!))
}
}
func didReceiveRead(_ request: CBATTRequest) {
let central = request.central
let centralHashCode = central.hash
let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() }
let centralHashCodeArgs = centralArgs.hashCodeArgs
centrals[centralHashCodeArgs] = central
let characteristic = request.characteristic
let characteristicHashCode = characteristic.hash
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
peripheralManager.respond(to: request, withResult: .attributeNotFound)
return
}
let idArgs = Int64(request.hash)
requests[idArgs] = request
let offsetArgs = Int64(request.offset)
api.onReadCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, idArgs: idArgs, offsetArgs: offsetArgs) {_ in }
}
func didReceiveWrite(_ requests: [CBATTRequest]) {
//
guard let request = requests.first else {
return
}
if requests.count > 1 {
// TODO:
let result = CBATTError.requestNotSupported
peripheralManager.respond(to: request, withResult: result)
}
let central = request.central
let centralHashCode = central.hash
let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() }
let centralHashCodeArgs = centralArgs.hashCodeArgs
centrals[centralHashCodeArgs] = central
let characteristic = request.characteristic
let characteristicHashCode = characteristic.hash
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
peripheralManager.respond(to: request, withResult: .attributeNotFound)
return
}
let idArgs = Int64(request.hash)
self.requests[idArgs] = request
let offsetArgs = Int64(request.offset)
guard let value = request.value else {
peripheralManager.respond(to: request, withResult: .requestNotSupported)
return
}
let valueArgs = FlutterStandardTypedData(bytes: value)
api.onWriteCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, idArgs: idArgs, offsetArgs: offsetArgs, valueArgs: valueArgs) {_ in }
}
func didSubscribeTo(_ central: CBCentral, _ characteristic: CBCharacteristic) {
let centralHashCode = central.hash
let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() }
let centralHashCodeArgs = centralArgs.hashCodeArgs
centrals[centralHashCodeArgs] = central
let characteristicHashCode = characteristic.hash
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
return
}
let stateArgs = true
api.onNotifyCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, stateArgs: stateArgs) {_ in }
}
func didUnsubscribeFrom(_ central: CBCentral, _ characteristic: CBCharacteristic) {
let centralHashCode = central.hash
let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() }
let centralHashCodeArgs = centralArgs.hashCodeArgs
centrals[centralHashCodeArgs] = central
let characteristicHashCode = characteristic.hash
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
return
}
let stateArgs = false
api.onNotifyCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, stateArgs: stateArgs) {_ in }
}
func isReadyToUpdateSubscribers() {
let callbacks = notifyCharacteristicValueChangedCallbacks
notifyCharacteristicValueChangedCallbacks.removeAll()
for callback in callbacks {
callback()
}
} }
} }

View File

@ -9,41 +9,41 @@ import Foundation
import CoreBluetooth import CoreBluetooth
class MyPeripheralManagerDelegate: NSObject, CBPeripheralManagerDelegate { class MyPeripheralManagerDelegate: NSObject, CBPeripheralManagerDelegate {
init(_ peripheralManager: MyPeripheralManager) { private let _peripheralManager: MyPeripheralManager
self.peripheralManager = peripheralManager
init(peripheralManager: MyPeripheralManager) {
_peripheralManager = peripheralManager
} }
private let peripheralManager: MyPeripheralManager
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) { func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
peripheralManager.didUpdateState() _peripheralManager.didUpdateState(peripheral: peripheral)
} }
func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) { func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) {
peripheralManager.didAdd(service, error) _peripheralManager.didAdd(peripheral: peripheral, service: service, error: error)
} }
func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) { func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) {
peripheralManager.didStartAdvertising(error) _peripheralManager.didStartAdvertising(peripheral: peripheral, error: error)
} }
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) { func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
peripheralManager.didReceiveRead(request) _peripheralManager.didReceiveRead(peripheral: peripheral, request: request)
} }
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) { func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
peripheralManager.didReceiveWrite(requests) _peripheralManager.didReceiveWrite(peripheral: peripheral, requests: requests)
} }
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) { func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
peripheralManager.didSubscribeTo(central, characteristic) _peripheralManager.didSubscribeTo(peripheral: peripheral, central: central, characteristic: characteristic)
} }
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) { func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) {
peripheralManager.didUnsubscribeFrom(central, characteristic) _peripheralManager.didUnsubscribeFrom(peripheral: peripheral, central: central, characteristic: characteristic)
} }
func peripheralManagerIsReady(toUpdateSubscribers peripheral: CBPeripheralManager) { func peripheralManagerIsReady(toUpdateSubscribers peripheral: CBPeripheralManager) {
peripheralManager.isReadyToUpdateSubscribers() _peripheralManager.isReadyToUpdateSubscribers(peripheral: peripheral)
} }
} }

View File

@ -26,4 +26,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189
COCOAPODS: 1.12.1 COCOAPODS: 1.14.3

View File

@ -8,18 +8,17 @@ import 'package:convert/convert.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
CentralManager get centralManager => CentralManager.instance;
PeripheralManager get peripheralManager => PeripheralManager.instance;
void main() { void main() {
runZonedGuarded(onStartUp, onCrashed); runZonedGuarded(onStartUp, onCrashed);
} }
void onStartUp() async { void onStartUp() async {
Logger.root.onRecord.listen(onLogRecord); Logger.root.onRecord.listen(onLogRecord);
// hierarchicalLoggingEnabled = true;
// CentralManager.instance.logLevel = Level.WARNING;
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await centralManager.setUp(); await CentralManager.instance.setUp();
await peripheralManager.setUp(); await PeripheralManager.instance.setUp();
runApp(const MyApp()); runApp(const MyApp());
} }
@ -58,6 +57,8 @@ class _MyAppState extends State<MyApp> {
return MaterialApp( return MaterialApp(
theme: ThemeData.light( theme: ThemeData.light(
useMaterial3: true, useMaterial3: true,
).copyWith(
materialTapTargetSize: MaterialTapTargetSize.padded,
), ),
home: const HomeView(), home: const HomeView(),
routes: { routes: {
@ -168,15 +169,15 @@ class _ScannerViewState extends State<ScannerView> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
state = ValueNotifier(centralManager.state); state = ValueNotifier(BluetoothLowEnergyState.unknown);
discovering = ValueNotifier(false); discovering = ValueNotifier(false);
discoveredEventArgs = ValueNotifier([]); discoveredEventArgs = ValueNotifier([]);
stateChangedSubscription = centralManager.stateChanged.listen( stateChangedSubscription = CentralManager.instance.stateChanged.listen(
(eventArgs) { (eventArgs) {
state.value = eventArgs.state; state.value = eventArgs.state;
}, },
); );
discoveredSubscription = centralManager.discovered.listen( discoveredSubscription = CentralManager.instance.discovered.listen(
(eventArgs) { (eventArgs) {
final items = discoveredEventArgs.value; final items = discoveredEventArgs.value;
final i = items.indexWhere( final i = items.indexWhere(
@ -190,6 +191,11 @@ class _ScannerViewState extends State<ScannerView> {
} }
}, },
); );
_initialize();
}
void _initialize() async {
state.value = await CentralManager.instance.getState();
} }
@override @override
@ -233,12 +239,13 @@ class _ScannerViewState extends State<ScannerView> {
} }
Future<void> startDiscovery() async { Future<void> startDiscovery() async {
await centralManager.startDiscovery(); discoveredEventArgs.value = [];
await CentralManager.instance.startDiscovery();
discovering.value = true; discovering.value = true;
} }
Future<void> stopDiscovery() async { Future<void> stopDiscovery() async {
await centralManager.stopDiscovery(); await CentralManager.instance.stopDiscovery();
discovering.value = false; discovering.value = false;
} }
@ -340,7 +347,13 @@ class _ScannerViewState extends State<ScannerView> {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
trailing: RssiWidget(rssi), trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
RssiWidget(rssi),
Text('$rssi'),
],
),
); );
}, },
separatorBuilder: (context, i) { separatorBuilder: (context, i) {
@ -378,44 +391,39 @@ class PeripheralView extends StatefulWidget {
} }
class _PeripheralViewState extends State<PeripheralView> { class _PeripheralViewState extends State<PeripheralView> {
late final ValueNotifier<bool> state; late final ValueNotifier<bool> connectionState;
late final DiscoveredEventArgs eventArgs; late final DiscoveredEventArgs eventArgs;
late final ValueNotifier<List<GattService>> services; late final ValueNotifier<List<GattService>> services;
late final ValueNotifier<List<GattCharacteristic>> characteristics; late final ValueNotifier<List<GattCharacteristic>> characteristics;
late final ValueNotifier<GattService?> service; late final ValueNotifier<GattService?> service;
late final ValueNotifier<GattCharacteristic?> characteristic; late final ValueNotifier<GattCharacteristic?> characteristic;
late final ValueNotifier<GattCharacteristicWriteType> writeType; late final ValueNotifier<GattCharacteristicWriteType> writeType;
late final ValueNotifier<int> maximumWriteLength;
late final ValueNotifier<int> rssi;
late final ValueNotifier<List<Log>> logs; late final ValueNotifier<List<Log>> logs;
late final TextEditingController writeController; late final TextEditingController writeController;
late final StreamSubscription stateChangedSubscription; late final StreamSubscription connectionStateChangedSubscription;
late final StreamSubscription valueChangedSubscription; late final StreamSubscription characteristicNotifiedSubscription;
late final StreamSubscription rssiChangedSubscription;
late final Timer rssiTimer;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
eventArgs = widget.eventArgs; eventArgs = widget.eventArgs;
state = ValueNotifier(false); connectionState = ValueNotifier(false);
services = ValueNotifier([]); services = ValueNotifier([]);
characteristics = ValueNotifier([]); characteristics = ValueNotifier([]);
service = ValueNotifier(null); service = ValueNotifier(null);
characteristic = ValueNotifier(null); characteristic = ValueNotifier(null);
writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); writeType = ValueNotifier(GattCharacteristicWriteType.withResponse);
maximumWriteLength = ValueNotifier(0);
rssi = ValueNotifier(-100);
logs = ValueNotifier([]); logs = ValueNotifier([]);
writeController = TextEditingController(); writeController = TextEditingController();
stateChangedSubscription = centralManager.peripheralStateChanged.listen( connectionStateChangedSubscription =
CentralManager.instance.connectionStateChanged.listen(
(eventArgs) { (eventArgs) {
if (eventArgs.peripheral != this.eventArgs.peripheral) { if (eventArgs.peripheral != this.eventArgs.peripheral) {
return; return;
} }
final state = eventArgs.state; final connectionState = eventArgs.connectionState;
this.state.value = state; this.connectionState.value = connectionState;
if (!state) { if (!connectionState) {
services.value = []; services.value = [];
characteristics.value = []; characteristics.value = [];
service.value = null; service.value = null;
@ -424,12 +432,13 @@ class _PeripheralViewState extends State<PeripheralView> {
} }
}, },
); );
valueChangedSubscription = centralManager.characteristicValueChanged.listen( characteristicNotifiedSubscription =
CentralManager.instance.characteristicNotified.listen(
(eventArgs) { (eventArgs) {
final characteristic = this.characteristic.value; // final characteristic = this.characteristic.value;
if (eventArgs.characteristic != characteristic) { // if (eventArgs.characteristic != characteristic) {
return; // return;
} // }
const type = LogType.notify; const type = LogType.notify;
final log = Log(type, eventArgs.value); final log = Log(type, eventArgs.value);
logs.value = [ logs.value = [
@ -438,28 +447,16 @@ class _PeripheralViewState extends State<PeripheralView> {
]; ];
}, },
); );
rssiTimer = Timer.periodic(
const Duration(seconds: 5),
(timer) async {
final state = this.state.value;
if (state) {
rssi.value = await centralManager.readRSSI(eventArgs.peripheral);
} else {
rssi.value = -100;
}
},
);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return WillPopScope( return PopScope(
onWillPop: () async { onPopInvoked: (didPop) async {
if (state.value) { if (connectionState.value) {
final peripheral = eventArgs.peripheral; final peripheral = eventArgs.peripheral;
await centralManager.disconnect(peripheral); await CentralManager.instance.disconnect(peripheral);
} }
return true;
}, },
child: Scaffold( child: Scaffold(
appBar: buildAppBar(context), appBar: buildAppBar(context),
@ -474,25 +471,17 @@ class _PeripheralViewState extends State<PeripheralView> {
title: Text(title), title: Text(title),
actions: [ actions: [
ValueListenableBuilder( ValueListenableBuilder(
valueListenable: state, valueListenable: connectionState,
builder: (context, state, child) { builder: (context, state, child) {
return TextButton( return TextButton(
onPressed: () async { onPressed: () async {
final peripheral = eventArgs.peripheral; final peripheral = eventArgs.peripheral;
if (state) { if (state) {
await centralManager.disconnect(peripheral); await CentralManager.instance.disconnect(peripheral);
maximumWriteLength.value = 0;
rssi.value = 0;
} else { } else {
await centralManager.connect(peripheral); await CentralManager.instance.connect(peripheral);
services.value = services.value =
await centralManager.discoverGATT(peripheral); await CentralManager.instance.discoverGATT(peripheral);
maximumWriteLength.value =
await centralManager.getMaximumWriteLength(
peripheral,
type: writeType.value,
);
rssi.value = await centralManager.readRSSI(peripheral);
} }
}, },
child: Text(state ? 'DISCONNECT' : 'CONNECT'), child: Text(state ? 'DISCONNECT' : 'CONNECT'),
@ -566,7 +555,32 @@ class _PeripheralViewState extends State<PeripheralView> {
hint: const Text('CHOOSE A CHARACTERISTIC'), hint: const Text('CHOOSE A CHARACTERISTIC'),
value: characteristic, value: characteristic,
onChanged: (characteristic) { onChanged: (characteristic) {
if (characteristic == null) {
return;
}
this.characteristic.value = characteristic; this.characteristic.value = characteristic;
final writeType = this.writeType.value;
final canWrite = characteristic.properties.contains(
GattCharacteristicProperty.write,
);
final canWriteWithoutResponse =
characteristic.properties.contains(
GattCharacteristicProperty.writeWithoutResponse,
);
if (writeType ==
GattCharacteristicWriteType.withResponse &&
!canWrite &&
canWriteWithoutResponse) {
this.writeType.value =
GattCharacteristicWriteType.withoutResponse;
}
if (writeType ==
GattCharacteristicWriteType.withoutResponse &&
!canWriteWithoutResponse &&
canWrite) {
this.writeType.value =
GattCharacteristicWriteType.withResponse;
}
}, },
); );
}, },
@ -624,21 +638,101 @@ class _PeripheralViewState extends State<PeripheralView> {
}, },
), ),
), ),
ValueListenableBuilder(
valueListenable: characteristic,
builder: (context, characteristic, chld) {
final bool canNotify, canRead, canWrite, canWriteWithoutResponse;
if (characteristic == null) {
canNotify =
canRead = canWrite = canWriteWithoutResponse = false;
} else {
final properties = characteristic.properties;
canNotify = properties.contains(
GattCharacteristicProperty.notify,
) ||
properties.contains(
GattCharacteristicProperty.indicate,
);
canRead = properties.contains(
GattCharacteristicProperty.read,
);
canWrite = properties.contains(
GattCharacteristicProperty.write,
);
canWriteWithoutResponse = properties.contains(
GattCharacteristicProperty.writeWithoutResponse,
);
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
margin: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: characteristic != null && canNotify
? () async {
await CentralManager.instance
.setCharacteristicNotifyState(
characteristic,
state: true,
);
}
: null,
child: const Text('NOTIFY'),
),
const SizedBox(width: 8.0),
ElevatedButton(
onPressed: characteristic != null && canRead
? () async {
final value = await CentralManager.instance
.readCharacteristic(characteristic);
const type = LogType.read;
final log = Log(type, value);
logs.value = [...logs.value, log];
}
: null,
child: const Text('READ'),
)
],
),
),
SizedBox(
height: 160.0,
child: TextField(
controller: writeController,
enabled: canWrite || canWriteWithoutResponse,
expands: true,
maxLines: null,
textAlignVertical: TextAlignVertical.top,
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
),
),
Row( Row(
children: [ children: [
ValueListenableBuilder( ValueListenableBuilder(
valueListenable: writeType, valueListenable: writeType,
builder: (context, writeType, child) { builder: (context, writeType, child) {
return ToggleButtons( return ToggleButtons(
onPressed: (i) async { onPressed: canWrite || canWriteWithoutResponse
final type = GattCharacteristicWriteType.values[i]; ? (i) {
if (!canWrite || !canWriteWithoutResponse) {
return;
}
final type =
GattCharacteristicWriteType.values[i];
this.writeType.value = type; this.writeType.value = type;
maximumWriteLength.value = }
await centralManager.getMaximumWriteLength( : null,
eventArgs.peripheral,
type: type,
);
},
constraints: const BoxConstraints( constraints: const BoxConstraints(
minWidth: 0.0, minWidth: 0.0,
minHeight: 0.0, minHeight: 0.0,
@ -647,7 +741,8 @@ class _PeripheralViewState extends State<PeripheralView> {
isSelected: GattCharacteristicWriteType.values isSelected: GattCharacteristicWriteType.values
.map((type) => type == writeType) .map((type) => type == writeType)
.toList(), .toList(),
children: GattCharacteristicWriteType.values.map((type) { children: GattCharacteristicWriteType.values.map(
(type) {
return Container( return Container(
margin: const EdgeInsets.symmetric( margin: const EdgeInsets.symmetric(
horizontal: 8.0, horizontal: 8.0,
@ -655,7 +750,8 @@ class _PeripheralViewState extends State<PeripheralView> {
), ),
child: Text(type.name), child: Text(type.name),
); );
}).toList(), },
).toList(),
); );
// final segments = // final segments =
// GattCharacteristicWriteType.values.map((type) { // GattCharacteristicWriteType.values.map((type) {
@ -679,118 +775,46 @@ class _PeripheralViewState extends State<PeripheralView> {
// ); // );
}, },
), ),
const SizedBox(width: 8.0),
ValueListenableBuilder(
valueListenable: state,
builder: (context, state, child) {
return ValueListenableBuilder(
valueListenable: maximumWriteLength,
builder: (context, maximumWriteLength, child) {
return Text('$maximumWriteLength');
},
);
},
),
const Spacer(), const Spacer(),
ValueListenableBuilder( ElevatedButton(
valueListenable: rssi,
builder: (context, rssi, child) {
return RssiWidget(rssi);
},
),
],
),
Container(
margin: const EdgeInsets.only(bottom: 16.0),
height: 160.0,
child: ValueListenableBuilder(
valueListenable: characteristic,
builder: (context, characteristic, child) {
final bool canNotify, canRead, canWrite;
if (characteristic == null) {
canNotify = canRead = canWrite = false;
} else {
final properties = characteristic.properties;
canNotify = properties.contains(
GattCharacteristicProperty.notify,
);
canRead = properties.contains(
GattCharacteristicProperty.read,
);
canWrite = properties.contains(
GattCharacteristicProperty.write,
);
}
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: TextField(
controller: writeController,
enabled: canWrite,
expands: true,
maxLines: null,
textAlignVertical: TextAlignVertical.top,
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
),
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: characteristic != null && canNotify
? () async {
await centralManager.notifyCharacteristic(
characteristic,
state: true,
);
}
: null,
child: const Text('NOTIFY'),
),
TextButton(
onPressed: characteristic != null && canRead
? () async {
final value = await centralManager
.readCharacteristic(characteristic);
const type = LogType.read;
final log = Log(type, value);
logs.value = [...logs.value, log];
}
: null,
child: const Text('READ'),
),
TextButton(
onPressed: characteristic != null && canWrite onPressed: characteristic != null && canWrite
? () async { ? () async {
final text = writeController.text; final text = writeController.text;
final elements = utf8.encode(text); final elements = utf8.encode(text);
final value = Uint8List.fromList(elements); final value = Uint8List.fromList(elements);
final type = writeType.value; final type = writeType.value;
await centralManager.writeCharacteristic( // Fragments the value by 512 bytes.
const fragmentSize = 512;
var start = 0;
while (start < value.length) {
final end = start + fragmentSize;
final fragmentedValue = end < value.length
? value.sublist(start, end)
: value.sublist(start);
await CentralManager.instance
.writeCharacteristic(
characteristic, characteristic,
value: value, value: fragmentedValue,
type: type, type: type,
); );
final log = Log(LogType.write, value); final log = Log(
LogType.write,
fragmentedValue,
);
logs.value = [...logs.value, log]; logs.value = [...logs.value, log];
start = end;
}
} }
: null, : null,
child: const Text('WRITE'), child: const Text('WRITE'),
), ),
], ],
), ),
const SizedBox(height: 16.0),
], ],
); );
}, },
), ),
),
], ],
), ),
); );
@ -799,17 +823,14 @@ class _PeripheralViewState extends State<PeripheralView> {
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
rssiTimer.cancel(); connectionStateChangedSubscription.cancel();
stateChangedSubscription.cancel(); characteristicNotifiedSubscription.cancel();
valueChangedSubscription.cancel(); connectionState.dispose();
state.dispose();
services.dispose(); services.dispose();
characteristics.dispose(); characteristics.dispose();
service.dispose(); service.dispose();
characteristic.dispose(); characteristic.dispose();
writeType.dispose(); writeType.dispose();
maximumWriteLength.dispose();
rssi.dispose();
logs.dispose(); logs.dispose();
writeController.dispose(); writeController.dispose();
} }
@ -828,87 +849,63 @@ class _AdvertiserViewState extends State<AdvertiserView>
late final ValueNotifier<bool> advertising; late final ValueNotifier<bool> advertising;
late final ValueNotifier<List<Log>> logs; late final ValueNotifier<List<Log>> logs;
late final StreamSubscription stateChangedSubscription; late final StreamSubscription stateChangedSubscription;
late final StreamSubscription readCharacteristicCommandReceivedSubscription; late final StreamSubscription characteristicReadSubscription;
late final StreamSubscription writeCharacteristicCommandReceivedSubscription; late final StreamSubscription characteristicWrittenSubscription;
late final StreamSubscription notifyCharacteristicCommandReceivedSubscription; late final StreamSubscription characteristicNotifyStateChangedSubscription;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
state = ValueNotifier(peripheralManager.state); state = ValueNotifier(BluetoothLowEnergyState.unknown);
advertising = ValueNotifier(false); advertising = ValueNotifier(false);
logs = ValueNotifier([]); logs = ValueNotifier([]);
stateChangedSubscription = peripheralManager.stateChanged.listen( stateChangedSubscription = PeripheralManager.instance.stateChanged.listen(
(eventArgs) { (eventArgs) {
state.value = eventArgs.state; state.value = eventArgs.state;
}, },
); );
readCharacteristicCommandReceivedSubscription = characteristicReadSubscription =
peripheralManager.readCharacteristicCommandReceived.listen( PeripheralManager.instance.characteristicRead.listen(
(eventArgs) async { (eventArgs) async {
final central = eventArgs.central; final central = eventArgs.central;
final characteristic = eventArgs.characteristic; final characteristic = eventArgs.characteristic;
final id = eventArgs.id; final value = eventArgs.value;
final offset = eventArgs.offset;
final log = Log( final log = Log(
LogType.read, LogType.read,
Uint8List.fromList([]), value,
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', 'central: ${central.uuid}; characteristic: ${characteristic.uuid}',
); );
logs.value = [ logs.value = [
...logs.value, ...logs.value,
log, log,
]; ];
// final maximumWriteLength = peripheralManager.getMaximumWriteLength(
// central,
// );
const status = true;
final value = Uint8List.fromList([0x01, 0x02, 0x03]);
await peripheralManager.sendReadCharacteristicReply(
central,
characteristic: characteristic,
id: id,
offset: offset,
status: status,
value: value,
);
}, },
); );
writeCharacteristicCommandReceivedSubscription = characteristicWrittenSubscription =
peripheralManager.writeCharacteristicCommandReceived.listen( PeripheralManager.instance.characteristicWritten.listen(
(eventArgs) async { (eventArgs) async {
final central = eventArgs.central; final central = eventArgs.central;
final characteristic = eventArgs.characteristic; final characteristic = eventArgs.characteristic;
final id = eventArgs.id;
final offset = eventArgs.offset;
final value = eventArgs.value; final value = eventArgs.value;
final log = Log( final log = Log(
LogType.write, LogType.write,
value, value,
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset', 'central: ${central.uuid}; characteristic: ${characteristic.uuid}',
); );
logs.value = [ logs.value = [
...logs.value, ...logs.value,
log, log,
]; ];
const status = true;
await peripheralManager.sendWriteCharacteristicReply(
central,
characteristic: characteristic,
id: id,
offset: offset,
status: status,
);
}, },
); );
notifyCharacteristicCommandReceivedSubscription = characteristicNotifyStateChangedSubscription =
peripheralManager.notifyCharacteristicCommandReceived.listen( PeripheralManager.instance.characteristicNotifyStateChanged.listen(
(eventArgs) async { (eventArgs) async {
final central = eventArgs.central; final central = eventArgs.central;
final characteristic = eventArgs.characteristic; final characteristic = eventArgs.characteristic;
final state = eventArgs.state; final state = eventArgs.state;
final log = Log( final log = Log(
LogType.write, LogType.notify,
Uint8List.fromList([]), Uint8List.fromList([]),
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state', 'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state',
); );
@ -918,15 +915,21 @@ class _AdvertiserViewState extends State<AdvertiserView>
]; ];
// Write someting to the central when notify started. // Write someting to the central when notify started.
if (state) { if (state) {
final value = Uint8List.fromList([0x03, 0x02, 0x01]); final elements = List.generate(2000, (i) => i % 256);
await peripheralManager.notifyCharacteristicValueChanged( final value = Uint8List.fromList(elements);
central, await PeripheralManager.instance.writeCharacteristic(
characteristic: characteristic, characteristic,
value: value, value: value,
central: central,
); );
} }
}, },
); );
_initialize();
}
void _initialize() async {
state.value = await PeripheralManager.instance.getState();
} }
@override @override
@ -970,7 +973,9 @@ class _AdvertiserViewState extends State<AdvertiserView>
} }
Future<void> startAdvertising() async { Future<void> startAdvertising() async {
await peripheralManager.clearServices(); await PeripheralManager.instance.clearServices();
final elements = List.generate(1000, (i) => i % 256);
final value = Uint8List.fromList(elements);
final service = GattService( final service = GattService(
uuid: UUID.short(100), uuid: UUID.short(100),
characteristics: [ characteristics: [
@ -979,15 +984,16 @@ class _AdvertiserViewState extends State<AdvertiserView>
properties: [ properties: [
GattCharacteristicProperty.read, GattCharacteristicProperty.read,
], ],
value: value,
descriptors: [], descriptors: [],
), ),
GattCharacteristic( GattCharacteristic(
uuid: UUID.short(201), uuid: UUID.short(201),
properties: [ properties: [
GattCharacteristicProperty.read,
GattCharacteristicProperty.write, GattCharacteristicProperty.write,
GattCharacteristicProperty.writeWithoutResponse, GattCharacteristicProperty.writeWithoutResponse,
], ],
value: Uint8List.fromList([]),
descriptors: [], descriptors: [],
), ),
GattCharacteristic( GattCharacteristic(
@ -996,24 +1002,41 @@ class _AdvertiserViewState extends State<AdvertiserView>
GattCharacteristicProperty.notify, GattCharacteristicProperty.notify,
GattCharacteristicProperty.indicate, GattCharacteristicProperty.indicate,
], ],
value: Uint8List.fromList([]),
descriptors: [],
),
GattCharacteristic(
uuid: UUID.short(203),
properties: [
GattCharacteristicProperty.notify,
],
value: Uint8List.fromList([]),
descriptors: [],
),
GattCharacteristic(
uuid: UUID.short(204),
properties: [
GattCharacteristicProperty.indicate,
],
value: Uint8List.fromList([]),
descriptors: [], descriptors: [],
), ),
], ],
); );
await peripheralManager.addService(service); await PeripheralManager.instance.addService(service);
final advertisement = Advertisement( final advertisement = Advertisement(
name: 'flutter', name: 'le12138',
manufacturerSpecificData: ManufacturerSpecificData( manufacturerSpecificData: ManufacturerSpecificData(
id: 0x2e19, id: 0x2e19,
data: Uint8List.fromList([0x01, 0x02, 0x03]), data: Uint8List.fromList([0x01, 0x02, 0x03]),
), ),
); );
await peripheralManager.startAdvertising(advertisement); await PeripheralManager.instance.startAdvertising(advertisement);
advertising.value = true; advertising.value = true;
} }
Future<void> stopAdvertising() async { Future<void> stopAdvertising() async {
await peripheralManager.stopAdvertising(); await PeripheralManager.instance.stopAdvertising();
advertising.value = false; advertising.value = false;
} }
@ -1074,9 +1097,9 @@ class _AdvertiserViewState extends State<AdvertiserView>
void dispose() { void dispose() {
super.dispose(); super.dispose();
stateChangedSubscription.cancel(); stateChangedSubscription.cancel();
readCharacteristicCommandReceivedSubscription.cancel(); characteristicReadSubscription.cancel();
writeCharacteristicCommandReceivedSubscription.cancel(); characteristicWrittenSubscription.cancel();
notifyCharacteristicCommandReceivedSubscription.cancel(); characteristicNotifyStateChangedSubscription.cancel();
state.dispose(); state.dispose();
advertising.dispose(); advertising.dispose();
logs.dispose(); logs.dispose();

View File

@ -20,4 +20,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
COCOAPODS: 1.12.1 COCOAPODS: 1.14.3

View File

@ -15,15 +15,15 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "4.0.0" version: "5.0.0"
bluetooth_low_energy_platform_interface: bluetooth_low_energy_platform_interface:
dependency: "direct main" dependency: "direct main"
description: description:
name: bluetooth_low_energy_platform_interface name: bluetooth_low_energy_platform_interface
sha256: a01819f4ef89d033edaa979465ec8c3af13b2618dc718d90fe681be91b6c4356 sha256: "54f92ab2d7746fb6f2b4a40a48cd7eb8e3bf772f3ee89e1979d4d7b741fb2a05"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "5.0.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -52,10 +52,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.2" version: "1.18.0"
convert: convert:
dependency: "direct main" dependency: "direct main"
description: description:
@ -102,10 +102,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.1"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -125,10 +125,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: intl name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.18.1" version: "0.19.0"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -173,10 +173,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.10.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -189,18 +189,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.1.2"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.6" version: "2.1.7"
process: process:
dependency: transitive dependency: transitive
description: description:
@ -226,18 +226,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.11.1"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
@ -266,10 +266,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.6.1"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -290,18 +290,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.7.1" version: "11.10.0"
web: web:
dependency: transitive dependency: transitive
description: description:
name: web name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.4-beta" version: "0.3.0"
webdriver: webdriver:
dependency: transitive dependency: transitive
description: description:
@ -311,5 +311,5 @@ packages:
source: hosted source: hosted
version: "3.0.2" version: "3.0.2"
sdks: sdks:
dart: ">=3.1.0-185.0.dev <4.0.0" dart: ">=3.2.0-194.0.dev <4.0.0"
flutter: ">=3.3.0" flutter: ">=3.3.0"

View File

@ -17,7 +17,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
bluetooth_low_energy_platform_interface: ^4.0.0 bluetooth_low_energy_platform_interface: ^5.0.0
bluetooth_low_energy_darwin: bluetooth_low_energy_darwin:
# When depending on this package from a real application you should use: # When depending on this package from a real application you should use:
# bluetooth_low_energy: ^x.y.z # bluetooth_low_energy: ^x.y.z
@ -30,7 +30,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
convert: ^3.1.1 convert: ^3.1.1
intl: ^0.18.1 intl: ^0.19.0
logging: ^1.2.0 logging: ^1.2.0
dev_dependencies: dev_dependencies:

View File

@ -1,11 +1,11 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'src/my_central_manager_2.dart'; import 'src/my_central_manager.dart';
import 'src/my_peripheral_manager_2.dart'; import 'src/my_peripheral_manager.dart';
abstract class BluetoothLowEnergyDarwin { abstract class BluetoothLowEnergyDarwin {
static void registerWith() { static void registerWith() {
MyCentralManager.instance = MyCentralManager2(); CentralManager.instance = MyCentralManager();
MyPeripheralManager.instance = MyPeripheralManager2(); PeripheralManager.instance = MyPeripheralManager();
} }
} }

View File

@ -9,22 +9,51 @@ import 'my_gatt_service2.dart';
export 'my_api.g.dart'; export 'my_api.g.dart';
// ToObject
extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs { extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs {
BluetoothLowEnergyState toState() { BluetoothLowEnergyState toState() {
return BluetoothLowEnergyState.values[index]; switch (this) {
case MyBluetoothLowEnergyStateArgs.unknown:
case MyBluetoothLowEnergyStateArgs.resetting:
return BluetoothLowEnergyState.unknown;
case MyBluetoothLowEnergyStateArgs.unsupported:
return BluetoothLowEnergyState.unsupported;
case MyBluetoothLowEnergyStateArgs.unauthorized:
return BluetoothLowEnergyState.unauthorized;
case MyBluetoothLowEnergyStateArgs.poweredOff:
return BluetoothLowEnergyState.poweredOff;
case MyBluetoothLowEnergyStateArgs.poweredOn:
return BluetoothLowEnergyState.poweredOn;
}
}
}
extension MyGattCharacteristicPropertyArgsX
on MyGattCharacteristicPropertyArgs {
GattCharacteristicProperty toProperty() {
return GattCharacteristicProperty.values[index];
}
}
extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs {
ManufacturerSpecificData toManufacturerSpecificData() {
final id = idArgs;
final data = dataArgs;
return ManufacturerSpecificData(
id: id,
data: data,
);
} }
} }
extension MyAdvertisementArgsX on MyAdvertisementArgs { extension MyAdvertisementArgsX on MyAdvertisementArgs {
Advertisement toAdvertisement() { Advertisement toAdvertisement() {
final name = nameArgs; final name = nameArgs;
final serviceUUIDs = serviceUUIDsArgs final serviceUUIDs =
.cast<String>() serviceUUIDsArgs.cast<String>().map((args) => args.toUUID()).toList();
.map((args) => UUID.fromString(args))
.toList();
final serviceData = serviceDataArgs.cast<String, Uint8List>().map( final serviceData = serviceDataArgs.cast<String, Uint8List>().map(
(uuidArgs, dataArgs) { (uuidArgs, dataArgs) {
final uuid = UUID.fromString(uuidArgs); final uuid = uuidArgs.toUUID();
final data = dataArgs; final data = dataArgs;
return MapEntry(uuid, data); return MapEntry(uuid, data);
}, },
@ -40,61 +69,40 @@ extension MyAdvertisementArgsX on MyAdvertisementArgs {
} }
} }
extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs { extension MyCentralArgsX on MyCentralArgs {
ManufacturerSpecificData toManufacturerSpecificData() { MyCentral toCentral() {
final id = idArgs; final uuid = uuidArgs.toUUID();
final data = dataArgs; return MyCentral(
return ManufacturerSpecificData( uuid: uuid,
id: id,
data: data,
); );
} }
} }
extension MyGattCharacteristicPropertyArgsX
on MyGattCharacteristicPropertyArgs {
GattCharacteristicProperty toProperty() {
return GattCharacteristicProperty.values[index];
}
}
extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType {
MyGattCharacteristicWriteTypeArgs toArgs() {
return MyGattCharacteristicWriteTypeArgs.values[index];
}
}
extension MyPeripheralArgsX on MyPeripheralArgs { extension MyPeripheralArgsX on MyPeripheralArgs {
Peripheral toPeripheral() { MyPeripheral toPeripheral() {
final hashCode = hashCodeArgs; final uuid = uuidArgs.toUUID();
final uuid = UUID.fromString(uuidArgs);
return MyPeripheral( return MyPeripheral(
hashCode: hashCode,
uuid: uuid, uuid: uuid,
); );
} }
} }
extension MyGattServiceArgsX on MyGattServiceArgs { extension MyGattDescriptorArgsX on MyGattDescriptorArgs {
MyGattService2 toService2() { MyGattDescriptor2 toDescriptor2(MyPeripheral peripheral) {
final hashCode = hashCodeArgs; final hashCode = hashCodeArgs;
final uuid = UUID.fromString(uuidArgs); final uuid = uuidArgs.toUUID();
final characteristics = characteristicsArgs return MyGattDescriptor2(
.cast<MyGattCharacteristicArgs>() peripheral: peripheral,
.map((args) => args.toCharacteristic2())
.toList();
return MyGattService2(
hashCode: hashCode, hashCode: hashCode,
uuid: uuid, uuid: uuid,
characteristics: characteristics,
); );
} }
} }
extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs { extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs {
MyGattCharacteristic2 toCharacteristic2() { MyGattCharacteristic2 toCharacteristic2(MyPeripheral peripheral) {
final hashCode = hashCodeArgs; final hashCode = hashCodeArgs;
final uuid = UUID.fromString(uuidArgs); final uuid = uuidArgs.toUUID();
final properties = propertyNumbersArgs.cast<int>().map( final properties = propertyNumbersArgs.cast<int>().map(
(args) { (args) {
final propertyArgs = MyGattCharacteristicPropertyArgs.values[args]; final propertyArgs = MyGattCharacteristicPropertyArgs.values[args];
@ -103,9 +111,10 @@ extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs {
).toList(); ).toList();
final descriptors = descriptorsArgs final descriptors = descriptorsArgs
.cast<MyGattDescriptorArgs>() .cast<MyGattDescriptorArgs>()
.map((args) => args.toDescriptor2()) .map((args) => args.toDescriptor2(peripheral))
.toList(); .toList();
return MyGattCharacteristic2( return MyGattCharacteristic2(
peripheral: peripheral,
hashCode: hashCode, hashCode: hashCode,
uuid: uuid, uuid: uuid,
properties: properties, properties: properties,
@ -114,45 +123,39 @@ extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs {
} }
} }
extension MyGattDescriptorArgsX on MyGattDescriptorArgs { extension MyGattServiceArgsX on MyGattServiceArgs {
MyGattDescriptor2 toDescriptor2() { MyGattService2 toService2(MyPeripheral peripheral) {
final hashCode = hashCodeArgs; final hashCode = hashCodeArgs;
final uuid = UUID.fromString(uuidArgs); final uuid = uuidArgs.toUUID();
return MyGattDescriptor2( final characteristics = characteristicsArgs
.cast<MyGattCharacteristicArgs>()
.map((args) => args.toCharacteristic2(peripheral))
.toList();
return MyGattService2(
peripheral: peripheral,
hashCode: hashCode, hashCode: hashCode,
uuid: uuid, uuid: uuid,
characteristics: characteristics,
); );
} }
} }
extension MyCentralArgsX on MyCentralArgs { extension MyUuidArgsX on String {
MyCentral toCentral() { UUID toUUID() {
final hashCode = hashCodeArgs; return UUID.fromString(this);
final uuid = UUID.fromString(uuidArgs);
return MyCentral(
hashCode: hashCode,
uuid: uuid,
);
} }
} }
extension AdvertisementX on Advertisement { // ToArgs
MyAdvertisementArgs toArgs() { extension GattCharacteristicPropertyX on GattCharacteristicProperty {
final nameArgs = name; MyGattCharacteristicPropertyArgs toArgs() {
final serviceUUIDsArgs = return MyGattCharacteristicPropertyArgs.values[index];
serviceUUIDs.map((uuid) => uuid.toString()).toList(); }
final serviceDataArgs = serviceData.map((uuid, data) { }
final uuidArgs = uuid.toString();
final dataArgs = data; extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType {
return MapEntry(uuidArgs, dataArgs); MyGattCharacteristicWriteTypeArgs toArgs() {
}); return MyGattCharacteristicWriteTypeArgs.values[index];
final manufacturerSpecificDataArgs = manufacturerSpecificData?.toArgs();
return MyAdvertisementArgs(
nameArgs: nameArgs,
serviceUUIDsArgs: serviceUUIDsArgs,
serviceDataArgs: serviceDataArgs,
manufacturerSpecificDataArgs: manufacturerSpecificDataArgs,
);
} }
} }
@ -167,18 +170,34 @@ extension ManufacturerSpecificDataX on ManufacturerSpecificData {
} }
} }
extension MyGattServiceX on MyGattService { extension AdvertisementX on Advertisement {
MyGattServiceArgs toArgs() { MyAdvertisementArgs toArgs() {
final nameArgs = name;
final serviceUUIDsArgs = serviceUUIDs.map((uuid) => uuid.toArgs()).toList();
final serviceDataArgs = serviceData.map((uuid, data) {
final uuidArgs = uuid.toArgs();
final dataArgs = data;
return MapEntry(uuidArgs, dataArgs);
});
final manufacturerSpecificDataArgs = manufacturerSpecificData?.toArgs();
return MyAdvertisementArgs(
nameArgs: nameArgs,
serviceUUIDsArgs: serviceUUIDsArgs,
serviceDataArgs: serviceDataArgs,
manufacturerSpecificDataArgs: manufacturerSpecificDataArgs,
);
}
}
extension MyGattDescriptorX on MyGattDescriptor {
MyGattDescriptorArgs toArgs() {
final hashCodeArgs = hashCode; final hashCodeArgs = hashCode;
final uuidArgs = uuid.toString(); final uuidArgs = uuid.toArgs();
final characteristicsArgs = characteristics final valueArgs = value;
.cast<MyGattCharacteristic>() return MyGattDescriptorArgs(
.map((characteristic) => characteristic.toArgs())
.toList();
return MyGattServiceArgs(
hashCodeArgs: hashCodeArgs, hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs, uuidArgs: uuidArgs,
characteristicsArgs: characteristicsArgs, valueArgs: valueArgs,
); );
} }
} }
@ -186,7 +205,7 @@ extension MyGattServiceX on MyGattService {
extension MyGattCharacteristicX on MyGattCharacteristic { extension MyGattCharacteristicX on MyGattCharacteristic {
MyGattCharacteristicArgs toArgs() { MyGattCharacteristicArgs toArgs() {
final hashCodeArgs = hashCode; final hashCodeArgs = hashCode;
final uuidArgs = uuid.toString(); final uuidArgs = uuid.toArgs();
final propertyNumbersArgs = properties.map((property) { final propertyNumbersArgs = properties.map((property) {
final propertyArgs = property.toArgs(); final propertyArgs = property.toArgs();
return propertyArgs.index; return propertyArgs.index;
@ -204,21 +223,24 @@ extension MyGattCharacteristicX on MyGattCharacteristic {
} }
} }
extension MyGattDescriptorX on MyGattDescriptor { extension MyGattServiceX on MyGattService {
MyGattDescriptorArgs toArgs() { MyGattServiceArgs toArgs() {
final hashCodeArgs = hashCode; final hashCodeArgs = hashCode;
final uuidArgs = uuid.toString(); final uuidArgs = uuid.toArgs();
final valueArgs = value; final characteristicsArgs = characteristics
return MyGattDescriptorArgs( .cast<MyGattCharacteristic>()
.map((characteristic) => characteristic.toArgs())
.toList();
return MyGattServiceArgs(
hashCodeArgs: hashCodeArgs, hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs, uuidArgs: uuidArgs,
valueArgs: valueArgs, characteristicsArgs: characteristicsArgs,
); );
} }
} }
extension GattCharacteristicPropertyX on GattCharacteristicProperty { extension UuidX on UUID {
MyGattCharacteristicPropertyArgs toArgs() { String toArgs() {
return MyGattCharacteristicPropertyArgs.values[index]; return toString().toLowerCase();
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,331 @@
import 'dart:async';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'my_api.dart';
import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart';
class MyCentralManager extends CentralManager
implements MyCentralManagerFlutterApi {
final MyCentralManagerHostApi _api;
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
final StreamController<DiscoveredEventArgs> _discoveredController;
final StreamController<ConnectionStateChangedEventArgs>
_connectionStateChangedController;
final StreamController<GattCharacteristicNotifiedEventArgs>
_characteristicNotifiedController;
final Map<String, MyPeripheral> _peripherals;
final Map<String, Map<int, MyGattCharacteristic2>> _characteristics;
BluetoothLowEnergyState _state;
MyCentralManager()
: _api = MyCentralManagerHostApi(),
_stateChangedController = StreamController.broadcast(),
_discoveredController = StreamController.broadcast(),
_connectionStateChangedController = StreamController.broadcast(),
_characteristicNotifiedController = StreamController.broadcast(),
_peripherals = {},
_characteristics = {},
_state = BluetoothLowEnergyState.unknown;
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<DiscoveredEventArgs> get discovered => _discoveredController.stream;
@override
Stream<ConnectionStateChangedEventArgs> get connectionStateChanged =>
_connectionStateChangedController.stream;
@override
Stream<GattCharacteristicNotifiedEventArgs> get characteristicNotified =>
_characteristicNotifiedController.stream;
@override
Future<void> setUp() async {
logger.info('setUp');
await _api.setUp();
MyCentralManagerFlutterApi.setup(this);
}
@override
Future<BluetoothLowEnergyState> getState() {
logger.info('getState');
return Future.value(_state);
}
@override
Future<void> startDiscovery() async {
logger.info('startDiscovery');
await _api.startDiscovery();
}
@override
Future<void> stopDiscovery() async {
logger.info('stopDiscovery');
await _api.stopDiscovery();
}
@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);
}
@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> readRSSI(Peripheral peripheral) async {
if (peripheral is! MyPeripheral) {
throw TypeError();
}
final uuidArgs = peripheral.uuid.toArgs();
logger.info('readRSSI: $uuidArgs');
final rssi = await _api.readRSSI(uuidArgs);
return rssi;
}
@override
Future<List<GattService>> discoverGATT(Peripheral peripheral) async {
if (peripheral is! MyPeripheral) {
throw TypeError();
}
// 发现 GATT 服务
final uuidArgs = peripheral.uuid.toArgs();
logger.info('discoverServices: $uuidArgs');
final servicesArgs = await _api
.discoverServices(uuidArgs)
.then((args) => args.cast<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
};
return services;
}
@override
Future<Uint8List> readCharacteristic(
GattCharacteristic characteristic,
) async {
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final peripheral = characteristic.peripheral;
final uuidArgs = peripheral.uuid.toArgs();
final hashCodeArgs = characteristic.hashCode;
logger.info('readCharacteristic: $uuidArgs.$hashCodeArgs');
final value = await _api.readCharacteristic(uuidArgs, hashCodeArgs);
return value;
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
required GattCharacteristicWriteType type,
}) async {
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final peripheral = characteristic.peripheral;
final uuidArgs = peripheral.uuid.toArgs();
final hashCodeArgs = characteristic.hashCode;
final trimmedValueArgs = value.trimGATT();
final typeArgs = type.toArgs();
final typeNumberArgs = typeArgs.index;
final fragmentSize = await _api.getMaximumWriteValueLength(
uuidArgs,
typeNumberArgs,
);
var start = 0;
while (start < trimmedValueArgs.length) {
final end = start + fragmentSize;
final fragmentedValueArgs = end < trimmedValueArgs.length
? trimmedValueArgs.sublist(start, end)
: trimmedValueArgs.sublist(start);
logger.info(
'writeCharacteristic: $uuidArgs.$hashCodeArgs - $fragmentedValueArgs, $typeArgs');
await _api.writeCharacteristic(
uuidArgs,
hashCodeArgs,
fragmentedValueArgs,
typeNumberArgs,
);
start = end;
}
}
@override
Future<void> setCharacteristicNotifyState(
GattCharacteristic characteristic, {
required bool state,
}) async {
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final peripheral = characteristic.peripheral;
final uuidArgs = peripheral.uuid.toArgs();
final hashCodeArgs = characteristic.hashCode;
final stateArgs = state;
logger.info(
'setCharacteristicNotifyState: $uuidArgs.$hashCodeArgs - $stateArgs');
await _api.setCharacteristicNotifyState(
uuidArgs,
hashCodeArgs,
stateArgs,
);
}
@override
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final peripheral = descriptor.peripheral;
final uuidArgs = peripheral.uuid.toArgs();
final hashCodeArgs = descriptor.hashCode;
logger.info('readDescriptor: $uuidArgs.$hashCodeArgs');
final value = await _api.readDescriptor(
uuidArgs,
hashCodeArgs,
);
return value;
}
@override
Future<void> writeDescriptor(
GattDescriptor descriptor, {
required Uint8List value,
}) async {
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final peripheral = descriptor.peripheral;
final uuidArgs = peripheral.uuid.toArgs();
final hashCodeArgs = descriptor.hashCode;
final trimmedValueArgs = value.trimGATT();
logger.info('writeDescriptor: $uuidArgs.$hashCodeArgs - $trimmedValueArgs');
await _api.writeDescriptor(uuidArgs, hashCodeArgs, trimmedValueArgs);
}
@override
void onStateChanged(int stateNumberArgs) {
final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs];
logger.info('onStateChanged: $stateArgs');
final state = stateArgs.toState();
if (_state == state) {
return;
}
_state = state;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
}
@override
void onDiscovered(
MyPeripheralArgs peripheralArgs,
int rssiArgs,
MyAdvertisementArgs advertisementArgs,
) {
final uuidArgs = peripheralArgs.uuidArgs;
logger.info('onDiscovered: $uuidArgs - $rssiArgs, $advertisementArgs');
final peripheral = _peripherals.putIfAbsent(
peripheralArgs.uuidArgs,
() => peripheralArgs.toPeripheral(),
);
final rssi = rssiArgs;
final advertisement = advertisementArgs.toAdvertisement();
final eventArgs = DiscoveredEventArgs(
peripheral,
rssi,
advertisement,
);
_discoveredController.add(eventArgs);
}
@override
void onConnectionStateChanged(
String uuidArgs,
bool stateArgs,
) {
logger.info('onConnectionStateChanged: $uuidArgs - $stateArgs');
final peripheral = _peripherals[uuidArgs];
if (peripheral == null) {
return;
}
final state = stateArgs;
final eventArgs = ConnectionStateChangedEventArgs(peripheral, state);
_connectionStateChangedController.add(eventArgs);
if (!state) {
_characteristics.remove(uuidArgs);
}
}
@override
void onCharacteristicNotified(
String uuidArgs,
int hashCodeArgs,
Uint8List valueArgs,
) {
logger
.info('onCharacteristicNotified: $uuidArgs.$hashCodeArgs - $valueArgs');
final characteristic = _retrieveCharacteristic(uuidArgs, hashCodeArgs);
if (characteristic == null) {
return;
}
final value = valueArgs;
final eventArgs = GattCharacteristicNotifiedEventArgs(
characteristic,
value,
);
_characteristicNotifiedController.add(eventArgs);
}
MyGattCharacteristic2? _retrieveCharacteristic(
String uuidArgs,
int hashCodeArgs,
) {
final characteristics = _characteristics[uuidArgs];
if (characteristics == null) {
return null;
}
return characteristics[hashCodeArgs];
}
}

View File

@ -1,297 +0,0 @@
import 'dart:async';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'my_api.dart';
import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart';
class MyCentralManager2 extends MyCentralManager
implements MyCentralManagerFlutterApi {
final MyCentralManagerHostApi _api;
BluetoothLowEnergyState _state;
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
final StreamController<DiscoveredEventArgs> _discoveredController;
final StreamController<PeripheralStateChangedEventArgs>
_peripheralStateChangedController;
final StreamController<GattCharacteristicValueChangedEventArgs>
_characteristicValueChangedController;
MyCentralManager2()
: _api = MyCentralManagerHostApi(),
_state = BluetoothLowEnergyState.unknown,
_stateChangedController = StreamController.broadcast(),
_discoveredController = StreamController.broadcast(),
_peripheralStateChangedController = StreamController.broadcast(),
_characteristicValueChangedController = StreamController.broadcast();
@override
BluetoothLowEnergyState get state => _state;
@protected
set state(BluetoothLowEnergyState value) {
if (_state == value) {
return;
}
_state = value;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
}
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<DiscoveredEventArgs> get discovered => _discoveredController.stream;
@override
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
_peripheralStateChangedController.stream;
@override
Stream<GattCharacteristicValueChangedEventArgs>
get characteristicValueChanged =>
_characteristicValueChangedController.stream;
Future<void> _throwWithoutState(BluetoothLowEnergyState state) async {
if (this.state != state) {
throw StateError(
'$state is expected, but current state is ${this.state}.');
}
}
@override
Future<void> setUp() async {
final args = await _api.setUp();
final stateArgs =
MyBluetoothLowEnergyStateArgs.values[args.stateNumberArgs];
state = stateArgs.toState();
MyCentralManagerFlutterApi.setup(this);
}
@override
Future<void> startDiscovery() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.startDiscovery();
}
@override
Future<void> stopDiscovery() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.stopDiscovery();
}
@override
Future<void> connect(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
await _api.connect(peripheralHashCodeArgs);
}
@override
Future<void> disconnect(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
await _api.disconnect(peripheralHashCodeArgs);
}
@override
Future<int> getMaximumWriteLength(
Peripheral peripheral, {
required GattCharacteristicWriteType type,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
final typeArgs = type.toArgs();
final typeNumberArgs = typeArgs.index;
final maximumWriteLength = await _api.getMaximumWriteLength(
peripheralHashCodeArgs,
typeNumberArgs,
);
return maximumWriteLength;
}
@override
Future<int> readRSSI(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
final rssi = await _api.readRSSI(peripheralHashCodeArgs);
return rssi;
}
@override
Future<List<GattService>> discoverGATT(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (peripheral is! MyPeripheral) {
throw TypeError();
}
final peripheralHashCodeArgs = peripheral.hashCode;
final servicesArgs = await _api.discoverGATT(peripheralHashCodeArgs);
final services = servicesArgs
.cast<MyGattServiceArgs>()
.map((args) => args.toService2())
.toList();
for (var service in services) {
for (var charactersitic in service.characteristics) {
for (var descriptor in charactersitic.descriptors) {
descriptor.characteristic = charactersitic;
}
charactersitic.service = service;
}
service.peripheral = peripheral;
}
return services;
}
@override
Future<Uint8List> readCharacteristic(
GattCharacteristic characteristic,
) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final value = await _api.readCharacteristic(
peripheralHashCodeArgs,
characteristicHashCodeArgs,
);
return value;
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
required GattCharacteristicWriteType type,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final valueArgs = value;
final typeArgs = type.toArgs();
final typeNumberArgs = typeArgs.index;
await _api.writeCharacteristic(
peripheralHashCodeArgs,
characteristicHashCodeArgs,
valueArgs,
typeNumberArgs,
);
}
@override
Future<void> notifyCharacteristic(
GattCharacteristic characteristic, {
required bool state,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final stateArgs = state;
await _api.notifyCharacteristic(
peripheralHashCodeArgs,
characteristicHashCodeArgs,
stateArgs,
);
}
@override
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final characteristic = descriptor.characteristic;
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final descriptorHashCodeArgs = descriptor.hashCode;
final value = await _api.readDescriptor(
peripheralHashCodeArgs,
descriptorHashCodeArgs,
);
return value;
}
@override
Future<void> writeDescriptor(
GattDescriptor descriptor, {
required Uint8List value,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final characteristic = descriptor.characteristic;
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final descriptorHashCodeArgs = descriptor.hashCode;
final valueArgs = value;
await _api.writeDescriptor(
peripheralHashCodeArgs,
descriptorHashCodeArgs,
valueArgs,
);
}
@override
void onStateChanged(int stateNumberArgs) {
final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs];
state = stateArgs.toState();
}
@override
void onDiscovered(
MyPeripheralArgs peripheralArgs,
int rssiArgs,
MyAdvertisementArgs advertisementArgs,
) {
final peripheral = peripheralArgs.toPeripheral();
final rssi = rssiArgs;
final advertisement = advertisementArgs.toAdvertisement();
final eventArgs = DiscoveredEventArgs(
peripheral,
rssi,
advertisement,
);
_discoveredController.add(eventArgs);
}
@override
void onPeripheralStateChanged(
MyPeripheralArgs peripheralArgs,
bool stateArgs,
) {
final peripheral = peripheralArgs.toPeripheral();
final state = stateArgs;
final eventArgs = PeripheralStateChangedEventArgs(peripheral, state);
_peripheralStateChangedController.add(eventArgs);
}
@override
void onCharacteristicValueChanged(
MyGattCharacteristicArgs characteristicArgs,
Uint8List valueArgs,
) {
final characteristic = characteristicArgs.toCharacteristic2();
final value = valueArgs;
final eventArgs = GattCharacteristicValueChangedEventArgs(
characteristic,
value,
);
_characteristicValueChangedController.add(eventArgs);
}
}

View File

@ -1,13 +1,15 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_gatt_descriptor2.dart'; import 'my_gatt_descriptor2.dart';
import 'my_gatt_service2.dart';
class MyGattCharacteristic2 extends MyGattCharacteristic { class MyGattCharacteristic2 extends MyGattCharacteristic {
late final MyGattService2 service; final MyPeripheral peripheral;
@override
final int hashCode;
MyGattCharacteristic2({ MyGattCharacteristic2({
super.hashCode, required this.peripheral,
required this.hashCode,
required super.uuid, required super.uuid,
required super.properties, required super.properties,
required List<MyGattDescriptor2> descriptors, required List<MyGattDescriptor2> descriptors,
@ -16,4 +18,11 @@ class MyGattCharacteristic2 extends MyGattCharacteristic {
@override @override
List<MyGattDescriptor2> get descriptors => List<MyGattDescriptor2> get descriptors =>
super.descriptors.cast<MyGattDescriptor2>(); super.descriptors.cast<MyGattDescriptor2>();
@override
bool operator ==(Object other) {
return other is MyGattCharacteristic2 &&
other.peripheral == peripheral &&
other.hashCode == hashCode;
}
} }

View File

@ -1,12 +1,20 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_gatt_characteristic2.dart';
class MyGattDescriptor2 extends MyGattDescriptor { class MyGattDescriptor2 extends MyGattDescriptor {
late final MyGattCharacteristic2 characteristic; final MyPeripheral peripheral;
@override
final int hashCode;
MyGattDescriptor2({ MyGattDescriptor2({
super.hashCode, required this.peripheral,
required this.hashCode,
required super.uuid, required super.uuid,
}); });
@override
bool operator ==(Object other) {
return other is MyGattDescriptor2 &&
other.peripheral == peripheral &&
other.hashCode == hashCode;
}
} }

View File

@ -3,10 +3,13 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla
import 'my_gatt_characteristic2.dart'; import 'my_gatt_characteristic2.dart';
class MyGattService2 extends MyGattService { class MyGattService2 extends MyGattService {
late final MyPeripheral peripheral; final MyPeripheral peripheral;
@override
final int hashCode;
MyGattService2({ MyGattService2({
super.hashCode, required this.peripheral,
required this.hashCode,
required super.uuid, required super.uuid,
required List<MyGattCharacteristic2> characteristics, required List<MyGattCharacteristic2> characteristics,
}) : super(characteristics: characteristics); }) : super(characteristics: characteristics);
@ -14,4 +17,11 @@ class MyGattService2 extends MyGattService {
@override @override
List<MyGattCharacteristic2> get characteristics => List<MyGattCharacteristic2> get characteristics =>
super.characteristics.cast<MyGattCharacteristic2>(); super.characteristics.cast<MyGattCharacteristic2>();
@override
bool operator ==(Object other) {
return other is MyGattService2 &&
other.peripheral == peripheral &&
other.hashCode == hashCode;
}
} }

View File

@ -0,0 +1,315 @@
import 'dart:async';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'my_api.dart';
class MyPeripheralManager extends PeripheralManager
implements MyPeripheralManagerFlutterApi {
final MyPeripheralManagerHostApi _api;
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
final StreamController<GattCharacteristicReadEventArgs>
_characteristicReadController;
final StreamController<GattCharacteristicWrittenEventArgs>
_characteristicWrittenController;
final StreamController<GattCharacteristicNotifyStateChangedEventArgs>
_characteristicNotifyStateChangedController;
final Map<int, Map<int, MyGattCharacteristic>> _characteristics;
final Map<String, Map<int, bool>> _listeners;
BluetoothLowEnergyState _state;
MyPeripheralManager()
: _api = MyPeripheralManagerHostApi(),
_stateChangedController = StreamController.broadcast(),
_characteristicReadController = StreamController.broadcast(),
_characteristicWrittenController = StreamController.broadcast(),
_characteristicNotifyStateChangedController =
StreamController.broadcast(),
_characteristics = {},
_listeners = {},
_state = BluetoothLowEnergyState.unknown;
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<GattCharacteristicReadEventArgs> get characteristicRead =>
_characteristicReadController.stream;
@override
Stream<GattCharacteristicWrittenEventArgs> get characteristicWritten =>
_characteristicWrittenController.stream;
@override
Stream<GattCharacteristicNotifyStateChangedEventArgs>
get characteristicNotifyStateChanged =>
_characteristicNotifyStateChangedController.stream;
@override
Future<void> setUp() async {
logger.info('setUp');
await _api.setUp();
MyPeripheralManagerFlutterApi.setup(this);
}
@override
Future<BluetoothLowEnergyState> getState() {
logger.info('getState');
return Future.value(_state);
}
@override
Future<void> addService(GattService service) async {
if (service is! MyGattService) {
throw TypeError();
}
final serviceArgs = service.toArgs();
final hashCodeArgs = serviceArgs.hashCodeArgs;
logger.info('addService: $hashCodeArgs');
await _api.addService(serviceArgs);
_characteristics[hashCodeArgs] = {
for (var characteristics in service.characteristics)
characteristics.hashCode: characteristics
};
}
@override
Future<void> removeService(GattService service) async {
final hashCodeArgs = service.hashCode;
logger.info('removeService: $hashCodeArgs');
await _api.removeService(hashCodeArgs);
_characteristics.remove(hashCodeArgs);
}
@override
Future<void> clearServices() async {
logger.info('clearServices');
await _api.clearServices();
_characteristics.clear();
}
@override
Future<void> startAdvertising(Advertisement advertisement) async {
final advertisementArgs = advertisement.toArgs();
logger.info('startAdvertising: $advertisementArgs');
await _api.startAdvertising(advertisementArgs);
}
@override
Future<void> stopAdvertising() async {
logger.info('stopAdvertising');
await _api.stopAdvertising();
}
@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);
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
Central? central,
}) async {
if (characteristic is! MyGattCharacteristic) {
throw TypeError();
}
characteristic.value = value;
if (central == null) {
return;
}
if (central is! MyCentral) {
throw TypeError();
}
final uuidArgs = central.uuid.toArgs();
final hashCodeArgs = characteristic.hashCode;
final listener = _retrieveListener(uuidArgs, hashCodeArgs);
if (listener == null) {
logger.warning('The central is not listening.');
return;
}
final uuidsArgs = [uuidArgs];
final trimmedValueArgs = characteristic.value;
final fragmentSize = await _api.getMaximumUpdateValueLength(uuidArgs);
var start = 0;
while (start < trimmedValueArgs.length) {
final end = start + fragmentSize;
final fragmentedValueArgs = end < trimmedValueArgs.length
? trimmedValueArgs.sublist(start, end)
: trimmedValueArgs.sublist(start);
logger.info(
'notifyCharacteristicChanged: $hashCodeArgs - $fragmentedValueArgs, $uuidsArgs');
await _api.updateCharacteristic(
hashCodeArgs,
fragmentedValueArgs,
uuidsArgs,
);
start = end;
}
}
@override
void onStateChanged(int stateNumberArgs) {
final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs];
logger.info('onStateChanged: $stateArgs');
final state = stateArgs.toState();
if (_state == state) {
return;
}
_state = state;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
}
@override
void onCharacteristicReadRequest(
MyCentralArgs centralArgs,
int hashCodeArgs,
int idArgs,
int offsetArgs,
) async {
final uuidArgs = centralArgs.uuidArgs;
logger.info(
'onCharacteristicReadRequest: $uuidArgs.$hashCodeArgs - $idArgs, $offsetArgs');
final central = centralArgs.toCentral();
final characteristic = _retrieveCharacteristic(hashCodeArgs);
if (characteristic == null) {
return;
}
const errorArgs = MyGattErrorArgs.success;
final offset = offsetArgs;
final valueArgs = _onCharacteristicRead(central, characteristic, offset);
await _tryRespond(
idArgs,
errorArgs,
valueArgs,
);
}
@override
void onCharacteristicWriteRequest(
MyCentralArgs centralArgs,
int hashCodeArgs,
int idArgs,
int offsetArgs,
Uint8List valueArgs,
) async {
final uuidArgs = centralArgs.uuidArgs;
logger.info(
'onCharacteristicWriteRequest: $uuidArgs.$hashCodeArgs - $idArgs, $offsetArgs, $valueArgs');
final central = centralArgs.toCentral();
final characteristic = _retrieveCharacteristic(hashCodeArgs);
if (characteristic == null) {
return;
}
const errorArgs = MyGattErrorArgs.success;
final value = valueArgs;
_onCharacteristicWritten(central, characteristic, value);
await _tryRespond(
idArgs,
errorArgs,
null,
);
}
@override
void onCharacteristicNotifyStateChanged(
MyCentralArgs centralArgs,
int hashCodeArgs,
bool stateArgs,
) {
final uuidArgs = centralArgs.uuidArgs;
logger.info(
'onCharacteristicNotifyStateChanged: $uuidArgs.$hashCodeArgs - $stateArgs');
final central = centralArgs.toCentral();
final characteristic = _retrieveCharacteristic(hashCodeArgs);
if (characteristic == null) {
return;
}
final state = stateArgs;
final listeners = _listeners.putIfAbsent(uuidArgs, () => {});
if (state) {
listeners[hashCodeArgs] = true;
} else {
listeners.remove(hashCodeArgs);
}
final eventArgs = GattCharacteristicNotifyStateChangedEventArgs(
central,
characteristic,
state,
);
_characteristicNotifyStateChangedController.add(eventArgs);
}
MyGattCharacteristic? _retrieveCharacteristic(int hashCodeArgs) {
final characteristics = _characteristics.values
.reduce((value, element) => value..addAll(element));
return characteristics[hashCodeArgs];
}
bool? _retrieveListener(String uuidArgs, int hashCodeArgs) {
final listeners = _listeners[uuidArgs];
if (listeners == null) {
return null;
}
return listeners[hashCodeArgs];
}
Future<void> _tryRespond(
int idArgs,
MyGattErrorArgs errorArgs,
Uint8List? valueArgs,
) async {
final errorNumberArgs = errorArgs.index;
try {
_api.respond(
idArgs,
errorNumberArgs,
valueArgs,
);
} catch (e, stack) {
logger.shout('Respond failed.', e, stack);
}
}
Uint8List _onCharacteristicRead(
MyCentral central,
MyGattCharacteristic characteristic,
int offset,
) {
final value = characteristic.value;
final trimmedValue = value.sublist(offset);
if (offset == 0) {
final eventArgs = GattCharacteristicReadEventArgs(
central,
characteristic,
value,
);
_characteristicReadController.add(eventArgs);
}
return trimmedValue;
}
void _onCharacteristicWritten(
MyCentral central,
MyGattCharacteristic characteristic,
Uint8List value,
) async {
final trimmedValue = value.trimGATT();
final eventArgs = GattCharacteristicWrittenEventArgs(
central,
characteristic,
trimmedValue,
);
_characteristicWrittenController.add(eventArgs);
}
}

View File

@ -1,252 +0,0 @@
import 'dart:async';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'my_api.dart';
class MyPeripheralManager2 extends MyPeripheralManager
implements MyPeripheralManagerFlutterApi {
final MyPeripheralManagerHostApi _api;
BluetoothLowEnergyState _state;
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
final StreamController<ReadGattCharacteristicCommandEventArgs>
_readCharacteristicCommandReceivedController;
final StreamController<WriteGattCharacteristicCommandEventArgs>
_writeCharacteristicCommandReceivedController;
final StreamController<NotifyGattCharacteristicCommandEventArgs>
_notifyCharacteristicCommandReceivedController;
MyPeripheralManager2()
: _api = MyPeripheralManagerHostApi(),
_state = BluetoothLowEnergyState.unknown,
_stateChangedController = StreamController.broadcast(),
_readCharacteristicCommandReceivedController =
StreamController.broadcast(),
_writeCharacteristicCommandReceivedController =
StreamController.broadcast(),
_notifyCharacteristicCommandReceivedController =
StreamController.broadcast();
@override
BluetoothLowEnergyState get state => _state;
@protected
set state(BluetoothLowEnergyState value) {
if (_state == value) {
return;
}
_state = value;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
}
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<ReadGattCharacteristicCommandEventArgs>
get readCharacteristicCommandReceived =>
_readCharacteristicCommandReceivedController.stream;
@override
Stream<WriteGattCharacteristicCommandEventArgs>
get writeCharacteristicCommandReceived =>
_writeCharacteristicCommandReceivedController.stream;
@override
Stream<NotifyGattCharacteristicCommandEventArgs>
get notifyCharacteristicCommandReceived =>
_notifyCharacteristicCommandReceivedController.stream;
Future<void> _throwWithoutState(BluetoothLowEnergyState state) async {
if (this.state != state) {
throw StateError(
'$state is expected, but current state is ${this.state}.');
}
}
@override
Future<void> setUp() async {
final args = await _api.setUp();
final stateArgs =
MyBluetoothLowEnergyStateArgs.values[args.stateNumberArgs];
state = stateArgs.toState();
MyPeripheralManagerFlutterApi.setup(this);
}
@override
Future<void> addService(GattService service) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (service is! MyGattService) {
throw TypeError();
}
final serviceArgs = service.toArgs();
await _api.addService(serviceArgs);
}
@override
Future<void> removeService(GattService service) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final serviceHashCodeArgs = service.hashCode;
await _api.removeService(serviceHashCodeArgs);
}
@override
Future<void> clearServices() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.clearServices();
}
@override
Future<void> startAdvertising(Advertisement advertisement) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final advertisementArgs = advertisement.toArgs();
await _api.startAdvertising(advertisementArgs);
}
@override
Future<void> stopAdvertising() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.stopAdvertising();
}
@override
Future<int> getMaximumWriteLength(Central central) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final maximumWriteLength =
await _api.getMaximumWriteLength(centralHashCodeArgs);
return maximumWriteLength;
}
@override
Future<void> sendReadCharacteristicReply(
Central central, {
required GattCharacteristic characteristic,
required int id,
required int offset,
required bool status,
required Uint8List value,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final idArgs = id;
final offsetArgs = offset;
final statusArgs = status;
final valueArgs = value;
await _api.sendReadCharacteristicReply(
centralHashCodeArgs,
characteristicHashCodeArgs,
idArgs,
offsetArgs,
statusArgs,
valueArgs,
);
}
@override
Future<void> sendWriteCharacteristicReply(
Central central, {
required GattCharacteristic characteristic,
required int id,
required int offset,
required bool status,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final idArgs = id;
final offsetArgs = offset;
final statusArgs = status;
await _api.sendWriteCharacteristicReply(
centralHashCodeArgs,
characteristicHashCodeArgs,
idArgs,
offsetArgs,
statusArgs,
);
}
@override
Future<void> notifyCharacteristicValueChanged(
Central central, {
required GattCharacteristic characteristic,
required Uint8List value,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final valueArgs = value;
await _api.notifyCharacteristicValueChanged(
centralHashCodeArgs,
characteristicHashCodeArgs,
valueArgs,
);
}
@override
void onStateChanged(int stateNumberArgs) {
final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs];
state = stateArgs.toState();
}
@override
void onReadCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
) {
final central = centralArgs.toCentral();
final characteristic = characteristicArgs.toCharacteristic2();
final id = idArgs;
final offset = offsetArgs;
final eventArgs = ReadGattCharacteristicCommandEventArgs(
central,
characteristic,
id,
offset,
);
_readCharacteristicCommandReceivedController.add(eventArgs);
}
@override
void onWriteCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
Uint8List valueArgs,
) {
final central = centralArgs.toCentral();
final characteristic = characteristicArgs.toCharacteristic2();
final id = idArgs;
final offset = offsetArgs;
final value = valueArgs;
final eventArgs = WriteGattCharacteristicCommandEventArgs(
central,
characteristic,
id,
offset,
value,
);
_writeCharacteristicCommandReceivedController.add(eventArgs);
}
@override
void onNotifyCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
bool stateArgs,
) {
final central = centralArgs.toCentral();
final characteristic = characteristicArgs.toCharacteristic2();
final state = stateArgs;
final eventArgs = NotifyGattCharacteristicCommandEventArgs(
central,
characteristic,
state,
);
_notifyCharacteristicCommandReceivedController.add(eventArgs);
}
}

View File

@ -8,152 +8,54 @@ import 'package:pigeon/pigeon.dart';
swiftOptions: SwiftOptions(), swiftOptions: SwiftOptions(),
), ),
) )
@HostApi() enum MyBluetoothLowEnergyStateArgs {
abstract class MyCentralManagerHostApi { unknown,
@async resetting,
MyCentralManagerArgs setUp(); unsupported,
void startDiscovery(); unauthorized,
void stopDiscovery(); poweredOff,
@async poweredOn,
void connect(int peripheralHashCodeArgs);
@async
void disconnect(int peripheralHashCodeArgs);
int getMaximumWriteLength(int peripheralHashCodeArgs, int typeNumberArgs);
@async
int readRSSI(int peripheralHashCodeArgs);
@async
List<MyGattServiceArgs> discoverGATT(int peripheralHashCodeArgs);
@async
Uint8List readCharacteristic(
int peripheralHashCodeArgs,
int characteristicHashCodeArgs,
);
@async
void writeCharacteristic(
int peripheralHashCodeArgs,
int characteristicHashCodeArgs,
Uint8List valueArgs,
int typeNumberArgs,
);
@async
void notifyCharacteristic(
int peripheralHashCodeArgs,
int characteristicHashCodeArgs,
bool stateArgs,
);
@async
Uint8List readDescriptor(
int peripheralHashCodeArgs,
int descriptorHashCodeArgs,
);
@async
void writeDescriptor(
int peripheralHashCodeArgs,
int descriptorHashCodeArgs,
Uint8List valueArgs,
);
} }
@FlutterApi() enum MyGattCharacteristicPropertyArgs {
abstract class MyCentralManagerFlutterApi { read,
void onStateChanged(int stateNumberArgs); write,
void onDiscovered( writeWithoutResponse,
MyPeripheralArgs peripheralArgs, notify,
int rssiArgs, indicate,
MyAdvertisementArgs advertisementArgs,
);
void onPeripheralStateChanged(
MyPeripheralArgs peripheralArgs,
bool stateArgs,
);
void onCharacteristicValueChanged(
MyGattCharacteristicArgs characteristicArgs,
Uint8List valueArgs,
);
} }
@HostApi() enum MyGattCharacteristicWriteTypeArgs {
abstract class MyPeripheralManagerHostApi { withResponse,
@async withoutResponse,
MyPeripheralManagerArgs setUp();
@async
void addService(MyGattServiceArgs serviceArgs);
void removeService(int serviceHashCodeArgs);
void clearServices();
@async
void startAdvertising(MyAdvertisementArgs advertisementArgs);
void stopAdvertising();
int getMaximumWriteLength(int centralHashCodeArgs);
void sendReadCharacteristicReply(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
int idArgs,
int offsetArgs,
bool statusArgs,
Uint8List valueArgs,
);
void sendWriteCharacteristicReply(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
int idArgs,
int offsetArgs,
bool statusArgs,
);
@async
void notifyCharacteristicValueChanged(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
Uint8List valueArgs,
);
} }
@FlutterApi() enum MyGattErrorArgs {
abstract class MyPeripheralManagerFlutterApi { success,
void onStateChanged(int stateNumberArgs); invalidHandle,
void onReadCharacteristicCommandReceived( readNotPermitted,
MyCentralArgs centralArgs, writeNotPermitted,
MyGattCharacteristicArgs characteristicArgs, invalidPDU,
int idArgs, insufficientAuthentication,
int offsetArgs, requestNotSupported,
); invalidOffset,
void onWriteCharacteristicCommandReceived( insufficientAuthorization,
MyCentralArgs centralArgs, prepareQueueFull,
MyGattCharacteristicArgs characteristicArgs, attributeNotFound,
int idArgs, attributeNotLong,
int offsetArgs, insufficientEncryptionKeySize,
Uint8List valueArgs, invalidAttributeValueLength,
); unlikelyError,
void onNotifyCharacteristicCommandReceived( insufficientEncryption,
MyCentralArgs centralArgs, unsupportedGroupType,
MyGattCharacteristicArgs characteristicArgs, insufficientResources,
bool stateArgs,
);
} }
class MyCentralManagerArgs { class MyManufacturerSpecificDataArgs {
final int stateNumberArgs; final int idArgs;
final Uint8List dataArgs;
MyCentralManagerArgs(this.stateNumberArgs); MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs);
}
class MyPeripheralManagerArgs {
final int stateNumberArgs;
MyPeripheralManagerArgs(this.stateNumberArgs);
}
class MyCentralArgs {
final int hashCodeArgs;
final String uuidArgs;
MyCentralArgs(this.hashCodeArgs, this.uuidArgs);
}
class MyPeripheralArgs {
final int hashCodeArgs;
final String uuidArgs;
MyPeripheralArgs(this.hashCodeArgs, this.uuidArgs);
} }
class MyAdvertisementArgs { class MyAdvertisementArgs {
@ -170,22 +72,27 @@ class MyAdvertisementArgs {
); );
} }
class MyManufacturerSpecificDataArgs { class MyCentralArgs {
final int idArgs; final String uuidArgs;
final Uint8List dataArgs;
MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs); MyCentralArgs(this.uuidArgs);
} }
class MyGattServiceArgs { class MyPeripheralArgs {
final String uuidArgs;
MyPeripheralArgs(this.uuidArgs);
}
class MyGattDescriptorArgs {
final int hashCodeArgs; final int hashCodeArgs;
final String uuidArgs; final String uuidArgs;
final List<MyGattCharacteristicArgs?> characteristicsArgs; final Uint8List? valueArgs;
MyGattServiceArgs( MyGattDescriptorArgs(
this.hashCodeArgs, this.hashCodeArgs,
this.uuidArgs, this.uuidArgs,
this.characteristicsArgs, this.valueArgs,
); );
} }
@ -203,39 +110,118 @@ class MyGattCharacteristicArgs {
); );
} }
class MyGattDescriptorArgs { class MyGattServiceArgs {
final int hashCodeArgs; final int hashCodeArgs;
final String uuidArgs; final String uuidArgs;
final Uint8List? valueArgs; final List<MyGattCharacteristicArgs?> characteristicsArgs;
MyGattDescriptorArgs( MyGattServiceArgs(
this.hashCodeArgs, this.hashCodeArgs,
this.uuidArgs, this.uuidArgs,
this.valueArgs, this.characteristicsArgs,
); );
} }
enum MyBluetoothLowEnergyStateArgs { @HostApi()
unknown, abstract class MyCentralManagerHostApi {
unsupported, void setUp();
unauthorized, void startDiscovery();
poweredOff, void stopDiscovery();
poweredOn, @async
void connect(String uuidArgs);
@async
void disconnect(String uuidArgs);
int getMaximumWriteValueLength(String uuidArgs, int typeNumberArgs);
@async
int readRSSI(String uuidArgs);
@async
List<MyGattServiceArgs> discoverServices(String uuidArgs);
@async
List<MyGattCharacteristicArgs> discoverCharacteristics(
String uuidArgs,
int hashCodeArgs,
);
@async
List<MyGattDescriptorArgs> discoverDescriptors(
String uuidArgs,
int hashCodeArgs,
);
@async
Uint8List readCharacteristic(String uuidArgs, int hashCodeArgs);
@async
void writeCharacteristic(
String uuidArgs,
int hashCodeArgs,
Uint8List valueArgs,
int typeNumberArgs,
);
@async
void setCharacteristicNotifyState(
String uuidArgs,
int hashCodeArgs,
bool stateArgs,
);
@async
Uint8List readDescriptor(String uuidArgs, int hashCodeArgs);
@async
void writeDescriptor(String uuidArgs, int hashCodeArgs, Uint8List valueArgs);
} }
enum MyGattCharacteristicPropertyArgs { @FlutterApi()
read, abstract class MyCentralManagerFlutterApi {
write, void onStateChanged(int stateNumberArgs);
writeWithoutResponse, void onDiscovered(
notify, MyPeripheralArgs peripheralArgs,
indicate, int rssiArgs,
MyAdvertisementArgs advertisementArgs,
);
void onConnectionStateChanged(String uuidArgs, bool stateArgs);
void onCharacteristicNotified(
String uuidArgs,
int hashCodeArgs,
Uint8List valueArgs,
);
} }
enum MyGattCharacteristicWriteTypeArgs { @HostApi()
// Write with response abstract class MyPeripheralManagerHostApi {
withResponse, void setUp();
// Write without response @async
withoutResponse, void addService(MyGattServiceArgs serviceArgs);
// Write with response and waiting for confirmation void removeService(int hashCodeArgs);
// reliable, void clearServices();
@async
void startAdvertising(MyAdvertisementArgs advertisementArgs);
void stopAdvertising();
int getMaximumUpdateValueLength(String uuidArgs);
void respond(int idArgs, int errorNumberArgs, Uint8List? valueArgs);
@async
void updateCharacteristic(
int hashCodeArgs,
Uint8List valueArgs,
List<String>? uuidsArgs,
);
}
@FlutterApi()
abstract class MyPeripheralManagerFlutterApi {
void onStateChanged(int stateNumberArgs);
void onCharacteristicReadRequest(
MyCentralArgs centralArgs,
int hashCodeArgs,
int idArgs,
int offsetArgs,
);
void onCharacteristicWriteRequest(
MyCentralArgs centralArgs,
int hashCodeArgs,
int idArgs,
int offsetArgs,
Uint8List valueArgs,
);
void onCharacteristicNotifyStateChanged(
MyCentralArgs centralArgs,
int hashCodeArgs,
bool stateArgs,
);
} }

View File

@ -1,6 +1,6 @@
name: bluetooth_low_energy_darwin name: bluetooth_low_energy_darwin
description: iOS and macOS implementation of the bluetooth_low_energy plugin. description: iOS and macOS implementation of the bluetooth_low_energy plugin.
version: 4.0.0 version: 5.0.0
homepage: https://github.com/yanshouwang/bluetooth_low_energy homepage: https://github.com/yanshouwang/bluetooth_low_energy
environment: environment:
@ -10,13 +10,13 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
bluetooth_low_energy_platform_interface: ^4.0.0 bluetooth_low_energy_platform_interface: ^5.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^3.0.0 flutter_lints: ^3.0.0
pigeon: ^12.0.1 pigeon: ^15.0.2
flutter: flutter:
plugin: plugin:

View File

@ -1,3 +1,33 @@
## 5.0.0
* Now `CentralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes.
* Add `UUID#fromAddress` constructor.
* Remove `CentralManager#getMaximumWriteLength` method.
* Move `CentralManager#state` to `CentralManager#getState()`.
* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`.
* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`.
* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`.
* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`.
* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`.
## 5.0.0-dev.4
* Add event logs.
## 5.0.0-dev.3
* Implements new Api.
## 5.0.0-dev.2
* Update interface to 5.0.0-dev.4.
## 5.0.0-dev.1
* Implement the `5.0.0` api.
* [Fix the issue that the same device was discovered multi times.](https://github.com/yanshouwang/bluetooth_low_energy/issues/32)
* [Fix the issue that some devices can't be discovered.](https://github.com/yanshouwang/bluetooth_low_energy/issues/32)
## 4.0.0 ## 4.0.0
* Remove `BluetoothLowEnergy` class. * Remove `BluetoothLowEnergy` class.

View File

@ -8,9 +8,6 @@ import 'package:convert/convert.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
CentralManager get centralManager => CentralManager.instance;
PeripheralManager get peripheralManager => PeripheralManager.instance;
void main() { void main() {
runZonedGuarded(onStartUp, onCrashed); runZonedGuarded(onStartUp, onCrashed);
} }
@ -18,8 +15,8 @@ void main() {
void onStartUp() async { void onStartUp() async {
Logger.root.onRecord.listen(onLogRecord); Logger.root.onRecord.listen(onLogRecord);
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await centralManager.setUp(); await CentralManager.instance.setUp();
await peripheralManager.setUp(); // await peripheralManager.setUp();
runApp(const MyApp()); runApp(const MyApp());
} }
@ -58,6 +55,8 @@ class _MyAppState extends State<MyApp> {
return MaterialApp( return MaterialApp(
theme: ThemeData.light( theme: ThemeData.light(
useMaterial3: true, useMaterial3: true,
).copyWith(
materialTapTargetSize: MaterialTapTargetSize.padded,
), ),
home: const HomeView(), home: const HomeView(),
routes: { routes: {
@ -73,81 +72,18 @@ class _MyAppState extends State<MyApp> {
} }
} }
class HomeView extends StatefulWidget { class HomeView extends StatelessWidget {
const HomeView({super.key}); const HomeView({super.key});
@override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
late final PageController controller;
@override
void initState() {
super.initState();
controller = PageController();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: buildBody(context), body: buildBody(context),
bottomNavigationBar: buildBottomNavigationBar(context),
); );
} }
Widget buildBody(BuildContext context) { Widget buildBody(BuildContext context) {
return PageView.builder(
controller: controller,
itemBuilder: (context, i) {
switch (i) {
case 0:
return const ScannerView(); return const ScannerView();
case 1:
return const AdvertiserView();
default:
throw ArgumentError.value(i);
}
},
itemCount: 2,
);
}
Widget buildBottomNavigationBar(BuildContext context) {
return ListenableBuilder(
listenable: controller,
builder: (context, child) {
return BottomNavigationBar(
onTap: (i) {
const duration = Duration(milliseconds: 300);
const curve = Curves.ease;
controller.animateToPage(
i,
duration: duration,
curve: curve,
);
},
currentIndex: controller.page?.toInt() ?? 0,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.radar),
label: 'scanner',
),
BottomNavigationBarItem(
icon: Icon(Icons.sensors),
label: 'advertiser',
),
],
);
},
);
}
@override
void dispose() {
super.dispose();
controller.dispose();
} }
} }
@ -168,16 +104,20 @@ class _ScannerViewState extends State<ScannerView> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
state = ValueNotifier(centralManager.state); state = ValueNotifier(BluetoothLowEnergyState.unknown);
discovering = ValueNotifier(false); discovering = ValueNotifier(false);
discoveredEventArgs = ValueNotifier([]); discoveredEventArgs = ValueNotifier([]);
stateChangedSubscription = centralManager.stateChanged.listen( stateChangedSubscription = CentralManager.instance.stateChanged.listen(
(eventArgs) { (eventArgs) {
state.value = eventArgs.state; state.value = eventArgs.state;
}, },
); );
discoveredSubscription = centralManager.discovered.listen( discoveredSubscription = CentralManager.instance.discovered.listen(
(eventArgs) { (eventArgs) {
final name = eventArgs.advertisement.name;
if (name == null || name.isEmpty) {
return;
}
final items = discoveredEventArgs.value; final items = discoveredEventArgs.value;
final i = items.indexWhere( final i = items.indexWhere(
(item) => item.peripheral == eventArgs.peripheral, (item) => item.peripheral == eventArgs.peripheral,
@ -190,6 +130,11 @@ class _ScannerViewState extends State<ScannerView> {
} }
}, },
); );
setUp();
}
void setUp() async {
state.value = await CentralManager.instance.getState();
} }
@override @override
@ -233,12 +178,13 @@ class _ScannerViewState extends State<ScannerView> {
} }
Future<void> startDiscovery() async { Future<void> startDiscovery() async {
await centralManager.startDiscovery(); discoveredEventArgs.value = [];
await CentralManager.instance.startDiscovery();
discovering.value = true; discovering.value = true;
} }
Future<void> stopDiscovery() async { Future<void> stopDiscovery() async {
await centralManager.stopDiscovery(); await CentralManager.instance.stopDiscovery();
discovering.value = false; discovering.value = false;
} }
@ -340,7 +286,13 @@ class _ScannerViewState extends State<ScannerView> {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
trailing: RssiWidget(rssi), trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
RssiWidget(rssi),
Text('$rssi'),
],
),
); );
}, },
separatorBuilder: (context, i) { separatorBuilder: (context, i) {
@ -378,44 +330,39 @@ class PeripheralView extends StatefulWidget {
} }
class _PeripheralViewState extends State<PeripheralView> { class _PeripheralViewState extends State<PeripheralView> {
late final ValueNotifier<bool> state; late final ValueNotifier<bool> connectionState;
late final DiscoveredEventArgs eventArgs; late final DiscoveredEventArgs eventArgs;
late final ValueNotifier<List<GattService>> services; late final ValueNotifier<List<GattService>> services;
late final ValueNotifier<List<GattCharacteristic>> characteristics; late final ValueNotifier<List<GattCharacteristic>> characteristics;
late final ValueNotifier<GattService?> service; late final ValueNotifier<GattService?> service;
late final ValueNotifier<GattCharacteristic?> characteristic; late final ValueNotifier<GattCharacteristic?> characteristic;
late final ValueNotifier<GattCharacteristicWriteType> writeType; late final ValueNotifier<GattCharacteristicWriteType> writeType;
late final ValueNotifier<int> maximumWriteLength;
late final ValueNotifier<int> rssi;
late final ValueNotifier<List<Log>> logs; late final ValueNotifier<List<Log>> logs;
late final TextEditingController writeController; late final TextEditingController writeController;
late final StreamSubscription stateChangedSubscription; late final StreamSubscription connectionStateChangedSubscription;
late final StreamSubscription valueChangedSubscription; late final StreamSubscription characteristicNotifiedSubscription;
late final StreamSubscription rssiChangedSubscription;
late final Timer rssiTimer;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
eventArgs = widget.eventArgs; eventArgs = widget.eventArgs;
state = ValueNotifier(false); connectionState = ValueNotifier(false);
services = ValueNotifier([]); services = ValueNotifier([]);
characteristics = ValueNotifier([]); characteristics = ValueNotifier([]);
service = ValueNotifier(null); service = ValueNotifier(null);
characteristic = ValueNotifier(null); characteristic = ValueNotifier(null);
writeType = ValueNotifier(GattCharacteristicWriteType.withResponse); writeType = ValueNotifier(GattCharacteristicWriteType.withResponse);
maximumWriteLength = ValueNotifier(0);
rssi = ValueNotifier(-100);
logs = ValueNotifier([]); logs = ValueNotifier([]);
writeController = TextEditingController(); writeController = TextEditingController();
stateChangedSubscription = centralManager.peripheralStateChanged.listen( connectionStateChangedSubscription =
CentralManager.instance.connectionStateChanged.listen(
(eventArgs) { (eventArgs) {
if (eventArgs.peripheral != this.eventArgs.peripheral) { if (eventArgs.peripheral != this.eventArgs.peripheral) {
return; return;
} }
final state = eventArgs.state; final connectionState = eventArgs.connectionState;
this.state.value = state; this.connectionState.value = connectionState;
if (!state) { if (!connectionState) {
services.value = []; services.value = [];
characteristics.value = []; characteristics.value = [];
service.value = null; service.value = null;
@ -424,12 +371,13 @@ class _PeripheralViewState extends State<PeripheralView> {
} }
}, },
); );
valueChangedSubscription = centralManager.characteristicValueChanged.listen( characteristicNotifiedSubscription =
CentralManager.instance.characteristicNotified.listen(
(eventArgs) { (eventArgs) {
final characteristic = this.characteristic.value; // final characteristic = this.characteristic.value;
if (eventArgs.characteristic != characteristic) { // if (eventArgs.characteristic != characteristic) {
return; // return;
} // }
const type = LogType.notify; const type = LogType.notify;
final log = Log(type, eventArgs.value); final log = Log(type, eventArgs.value);
logs.value = [ logs.value = [
@ -438,28 +386,16 @@ class _PeripheralViewState extends State<PeripheralView> {
]; ];
}, },
); );
rssiTimer = Timer.periodic(
const Duration(seconds: 5),
(timer) async {
final state = this.state.value;
if (state) {
rssi.value = await centralManager.readRSSI(eventArgs.peripheral);
} else {
rssi.value = -100;
}
},
);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return WillPopScope( return PopScope(
onWillPop: () async { onPopInvoked: (didPop) async {
if (state.value) { if (connectionState.value) {
final peripheral = eventArgs.peripheral; final peripheral = eventArgs.peripheral;
await centralManager.disconnect(peripheral); await CentralManager.instance.disconnect(peripheral);
} }
return true;
}, },
child: Scaffold( child: Scaffold(
appBar: buildAppBar(context), appBar: buildAppBar(context),
@ -474,25 +410,17 @@ class _PeripheralViewState extends State<PeripheralView> {
title: Text(title), title: Text(title),
actions: [ actions: [
ValueListenableBuilder( ValueListenableBuilder(
valueListenable: state, valueListenable: connectionState,
builder: (context, state, child) { builder: (context, state, child) {
return TextButton( return TextButton(
onPressed: () async { onPressed: () async {
final peripheral = eventArgs.peripheral; final peripheral = eventArgs.peripheral;
if (state) { if (state) {
await centralManager.disconnect(peripheral); await CentralManager.instance.disconnect(peripheral);
maximumWriteLength.value = 0;
rssi.value = 0;
} else { } else {
await centralManager.connect(peripheral); await CentralManager.instance.connect(peripheral);
services.value = services.value =
await centralManager.discoverGATT(peripheral); await CentralManager.instance.discoverGATT(peripheral);
maximumWriteLength.value =
await centralManager.getMaximumWriteLength(
peripheral,
type: writeType.value,
);
rssi.value = await centralManager.readRSSI(peripheral);
} }
}, },
child: Text(state ? 'DISCONNECT' : 'CONNECT'), child: Text(state ? 'DISCONNECT' : 'CONNECT'),
@ -566,7 +494,32 @@ class _PeripheralViewState extends State<PeripheralView> {
hint: const Text('CHOOSE A CHARACTERISTIC'), hint: const Text('CHOOSE A CHARACTERISTIC'),
value: characteristic, value: characteristic,
onChanged: (characteristic) { onChanged: (characteristic) {
if (characteristic == null) {
return;
}
this.characteristic.value = characteristic; this.characteristic.value = characteristic;
final writeType = this.writeType.value;
final canWrite = characteristic.properties.contains(
GattCharacteristicProperty.write,
);
final canWriteWithoutResponse =
characteristic.properties.contains(
GattCharacteristicProperty.writeWithoutResponse,
);
if (writeType ==
GattCharacteristicWriteType.withResponse &&
!canWrite &&
canWriteWithoutResponse) {
this.writeType.value =
GattCharacteristicWriteType.withoutResponse;
}
if (writeType ==
GattCharacteristicWriteType.withoutResponse &&
!canWriteWithoutResponse &&
canWrite) {
this.writeType.value =
GattCharacteristicWriteType.withResponse;
}
}, },
); );
}, },
@ -624,21 +577,101 @@ class _PeripheralViewState extends State<PeripheralView> {
}, },
), ),
), ),
ValueListenableBuilder(
valueListenable: characteristic,
builder: (context, characteristic, chld) {
final bool canNotify, canRead, canWrite, canWriteWithoutResponse;
if (characteristic == null) {
canNotify =
canRead = canWrite = canWriteWithoutResponse = false;
} else {
final properties = characteristic.properties;
canNotify = properties.contains(
GattCharacteristicProperty.notify,
) ||
properties.contains(
GattCharacteristicProperty.indicate,
);
canRead = properties.contains(
GattCharacteristicProperty.read,
);
canWrite = properties.contains(
GattCharacteristicProperty.write,
);
canWriteWithoutResponse = properties.contains(
GattCharacteristicProperty.writeWithoutResponse,
);
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
margin: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: characteristic != null && canNotify
? () async {
await CentralManager.instance
.setCharacteristicNotifyState(
characteristic,
state: true,
);
}
: null,
child: const Text('NOTIFY'),
),
const SizedBox(width: 8.0),
ElevatedButton(
onPressed: characteristic != null && canRead
? () async {
final value = await CentralManager.instance
.readCharacteristic(characteristic);
const type = LogType.read;
final log = Log(type, value);
logs.value = [...logs.value, log];
}
: null,
child: const Text('READ'),
)
],
),
),
SizedBox(
height: 160.0,
child: TextField(
controller: writeController,
enabled: canWrite || canWriteWithoutResponse,
expands: true,
maxLines: null,
textAlignVertical: TextAlignVertical.top,
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
),
),
Row( Row(
children: [ children: [
ValueListenableBuilder( ValueListenableBuilder(
valueListenable: writeType, valueListenable: writeType,
builder: (context, writeType, child) { builder: (context, writeType, child) {
return ToggleButtons( return ToggleButtons(
onPressed: (i) async { onPressed: canWrite || canWriteWithoutResponse
final type = GattCharacteristicWriteType.values[i]; ? (i) {
if (!canWrite || !canWriteWithoutResponse) {
return;
}
final type =
GattCharacteristicWriteType.values[i];
this.writeType.value = type; this.writeType.value = type;
maximumWriteLength.value = }
await centralManager.getMaximumWriteLength( : null,
eventArgs.peripheral,
type: type,
);
},
constraints: const BoxConstraints( constraints: const BoxConstraints(
minWidth: 0.0, minWidth: 0.0,
minHeight: 0.0, minHeight: 0.0,
@ -647,7 +680,8 @@ class _PeripheralViewState extends State<PeripheralView> {
isSelected: GattCharacteristicWriteType.values isSelected: GattCharacteristicWriteType.values
.map((type) => type == writeType) .map((type) => type == writeType)
.toList(), .toList(),
children: GattCharacteristicWriteType.values.map((type) { children: GattCharacteristicWriteType.values.map(
(type) {
return Container( return Container(
margin: const EdgeInsets.symmetric( margin: const EdgeInsets.symmetric(
horizontal: 8.0, horizontal: 8.0,
@ -655,7 +689,8 @@ class _PeripheralViewState extends State<PeripheralView> {
), ),
child: Text(type.name), child: Text(type.name),
); );
}).toList(), },
).toList(),
); );
// final segments = // final segments =
// GattCharacteristicWriteType.values.map((type) { // GattCharacteristicWriteType.values.map((type) {
@ -679,118 +714,46 @@ class _PeripheralViewState extends State<PeripheralView> {
// ); // );
}, },
), ),
const SizedBox(width: 8.0),
ValueListenableBuilder(
valueListenable: state,
builder: (context, state, child) {
return ValueListenableBuilder(
valueListenable: maximumWriteLength,
builder: (context, maximumWriteLength, child) {
return Text('$maximumWriteLength');
},
);
},
),
const Spacer(), const Spacer(),
ValueListenableBuilder( ElevatedButton(
valueListenable: rssi,
builder: (context, rssi, child) {
return RssiWidget(rssi);
},
),
],
),
Container(
margin: const EdgeInsets.only(bottom: 16.0),
height: 160.0,
child: ValueListenableBuilder(
valueListenable: characteristic,
builder: (context, characteristic, child) {
final bool canNotify, canRead, canWrite;
if (characteristic == null) {
canNotify = canRead = canWrite = false;
} else {
final properties = characteristic.properties;
canNotify = properties.contains(
GattCharacteristicProperty.notify,
);
canRead = properties.contains(
GattCharacteristicProperty.read,
);
canWrite = properties.contains(
GattCharacteristicProperty.write,
);
}
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: TextField(
controller: writeController,
enabled: canWrite,
expands: true,
maxLines: null,
textAlignVertical: TextAlignVertical.top,
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
),
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: characteristic != null && canNotify
? () async {
await centralManager.notifyCharacteristic(
characteristic,
state: true,
);
}
: null,
child: const Text('NOTIFY'),
),
TextButton(
onPressed: characteristic != null && canRead
? () async {
final value = await centralManager
.readCharacteristic(characteristic);
const type = LogType.read;
final log = Log(type, value);
logs.value = [...logs.value, log];
}
: null,
child: const Text('READ'),
),
TextButton(
onPressed: characteristic != null && canWrite onPressed: characteristic != null && canWrite
? () async { ? () async {
final text = writeController.text; final text = writeController.text;
final elements = utf8.encode(text); final elements = utf8.encode(text);
final value = Uint8List.fromList(elements); final value = Uint8List.fromList(elements);
final type = writeType.value; final type = writeType.value;
await centralManager.writeCharacteristic( // Fragments the value by 512 bytes.
const fragmentSize = 512;
var start = 0;
while (start < value.length) {
final end = start + fragmentSize;
final fragmentedValue = end < value.length
? value.sublist(start, end)
: value.sublist(start);
await CentralManager.instance
.writeCharacteristic(
characteristic, characteristic,
value: value, value: fragmentedValue,
type: type, type: type,
); );
final log = Log(LogType.write, value); final log = Log(
LogType.write,
fragmentedValue,
);
logs.value = [...logs.value, log]; logs.value = [...logs.value, log];
start = end;
}
} }
: null, : null,
child: const Text('WRITE'), child: const Text('WRITE'),
), ),
], ],
), ),
const SizedBox(height: 16.0),
], ],
); );
}, },
), ),
),
], ],
), ),
); );
@ -799,290 +762,19 @@ class _PeripheralViewState extends State<PeripheralView> {
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
rssiTimer.cancel(); connectionStateChangedSubscription.cancel();
stateChangedSubscription.cancel(); characteristicNotifiedSubscription.cancel();
valueChangedSubscription.cancel(); connectionState.dispose();
state.dispose();
services.dispose(); services.dispose();
characteristics.dispose(); characteristics.dispose();
service.dispose(); service.dispose();
characteristic.dispose(); characteristic.dispose();
writeType.dispose(); writeType.dispose();
maximumWriteLength.dispose();
rssi.dispose();
logs.dispose(); logs.dispose();
writeController.dispose(); writeController.dispose();
} }
} }
class AdvertiserView extends StatefulWidget {
const AdvertiserView({super.key});
@override
State<AdvertiserView> createState() => _AdvertiserViewState();
}
class _AdvertiserViewState extends State<AdvertiserView>
with SingleTickerProviderStateMixin {
late final ValueNotifier<BluetoothLowEnergyState> state;
late final ValueNotifier<bool> advertising;
late final ValueNotifier<List<Log>> logs;
late final StreamSubscription stateChangedSubscription;
late final StreamSubscription readCharacteristicCommandReceivedSubscription;
late final StreamSubscription writeCharacteristicCommandReceivedSubscription;
late final StreamSubscription notifyCharacteristicCommandReceivedSubscription;
@override
void initState() {
super.initState();
state = ValueNotifier(peripheralManager.state);
advertising = ValueNotifier(false);
logs = ValueNotifier([]);
stateChangedSubscription = peripheralManager.stateChanged.listen(
(eventArgs) {
state.value = eventArgs.state;
},
);
readCharacteristicCommandReceivedSubscription =
peripheralManager.readCharacteristicCommandReceived.listen(
(eventArgs) async {
final central = eventArgs.central;
final characteristic = eventArgs.characteristic;
final id = eventArgs.id;
final offset = eventArgs.offset;
final log = Log(
LogType.read,
Uint8List.fromList([]),
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset',
);
logs.value = [
...logs.value,
log,
];
// final maximumWriteLength = peripheralManager.getMaximumWriteLength(
// central,
// );
const status = true;
final value = Uint8List.fromList([0x01, 0x02, 0x03]);
await peripheralManager.sendReadCharacteristicReply(
central,
characteristic: characteristic,
id: id,
offset: offset,
status: status,
value: value,
);
},
);
writeCharacteristicCommandReceivedSubscription =
peripheralManager.writeCharacteristicCommandReceived.listen(
(eventArgs) async {
final central = eventArgs.central;
final characteristic = eventArgs.characteristic;
final id = eventArgs.id;
final offset = eventArgs.offset;
final value = eventArgs.value;
final log = Log(
LogType.write,
value,
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; id: $id; offset: $offset',
);
logs.value = [
...logs.value,
log,
];
const status = true;
await peripheralManager.sendWriteCharacteristicReply(
central,
characteristic: characteristic,
id: id,
offset: offset,
status: status,
);
},
);
notifyCharacteristicCommandReceivedSubscription =
peripheralManager.notifyCharacteristicCommandReceived.listen(
(eventArgs) async {
final central = eventArgs.central;
final characteristic = eventArgs.characteristic;
final state = eventArgs.state;
final log = Log(
LogType.write,
Uint8List.fromList([]),
'central: ${central.uuid}; characteristic: ${characteristic.uuid}; state: $state',
);
logs.value = [
...logs.value,
log,
];
// Write someting to the central when notify started.
if (state) {
final value = Uint8List.fromList([0x03, 0x02, 0x01]);
await peripheralManager.notifyCharacteristicValueChanged(
central,
characteristic: characteristic,
value: value,
);
}
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: buildAppBar(context),
body: buildBody(context),
);
}
PreferredSizeWidget buildAppBar(BuildContext context) {
return AppBar(
title: const Text('Advertiser'),
actions: [
ValueListenableBuilder(
valueListenable: state,
builder: (context, state, child) {
return ValueListenableBuilder(
valueListenable: advertising,
builder: (context, advertising, child) {
return TextButton(
onPressed: state == BluetoothLowEnergyState.poweredOn
? () async {
if (advertising) {
await stopAdvertising();
} else {
await startAdvertising();
}
}
: null,
child: Text(
advertising ? 'END' : 'BEGIN',
),
);
},
);
},
),
],
);
}
Future<void> startAdvertising() async {
await peripheralManager.clearServices();
final service = GattService(
uuid: UUID.short(100),
characteristics: [
GattCharacteristic(
uuid: UUID.short(200),
properties: [
GattCharacteristicProperty.read,
],
descriptors: [],
),
GattCharacteristic(
uuid: UUID.short(201),
properties: [
GattCharacteristicProperty.read,
GattCharacteristicProperty.write,
GattCharacteristicProperty.writeWithoutResponse,
],
descriptors: [],
),
GattCharacteristic(
uuid: UUID.short(202),
properties: [
GattCharacteristicProperty.notify,
GattCharacteristicProperty.indicate,
],
descriptors: [],
),
],
);
await peripheralManager.addService(service);
final advertisement = Advertisement(
name: 'flutter',
manufacturerSpecificData: ManufacturerSpecificData(
id: 0x2e19,
data: Uint8List.fromList([0x01, 0x02, 0x03]),
),
);
await peripheralManager.startAdvertising(advertisement);
advertising.value = true;
}
Future<void> stopAdvertising() async {
await peripheralManager.stopAdvertising();
advertising.value = false;
}
Widget buildBody(BuildContext context) {
final theme = Theme.of(context);
return ValueListenableBuilder(
valueListenable: logs,
builder: (context, logs, child) {
return ListView.builder(
itemBuilder: (context, i) {
final log = logs[i];
final type = log.type.name.toUpperCase().characters.first;
final Color typeColor;
switch (log.type) {
case LogType.read:
typeColor = Colors.blue;
break;
case LogType.write:
typeColor = Colors.amber;
break;
case LogType.notify:
typeColor = Colors.red;
break;
default:
typeColor = Colors.black;
}
final time = DateFormat.Hms().format(log.time);
final value = log.value;
final message = '${log.detail}; ${hex.encode(value)}';
return Text.rich(
TextSpan(
text: '[$type:${value.length}]',
children: [
TextSpan(
text: ' $time: ',
style: theme.textTheme.bodyMedium?.copyWith(
color: Colors.green,
),
),
TextSpan(
text: message,
style: theme.textTheme.bodyMedium,
),
],
style: theme.textTheme.bodyMedium?.copyWith(
color: typeColor,
),
),
);
},
itemCount: logs.length,
);
},
);
}
@override
void dispose() {
super.dispose();
stateChangedSubscription.cancel();
readCharacteristicCommandReceivedSubscription.cancel();
writeCharacteristicCommandReceivedSubscription.cancel();
notifyCharacteristicCommandReceivedSubscription.cancel();
state.dispose();
advertising.dispose();
logs.dispose();
}
}
class Log { class Log {
final DateTime time; final DateTime time;
final LogType type; final LogType type;

View File

@ -23,15 +23,15 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "4.0.0" version: "5.0.0"
bluetooth_low_energy_platform_interface: bluetooth_low_energy_platform_interface:
dependency: "direct main" dependency: "direct main"
description: description:
name: bluetooth_low_energy_platform_interface name: bluetooth_low_energy_platform_interface
sha256: a01819f4ef89d033edaa979465ec8c3af13b2618dc718d90fe681be91b6c4356 sha256: "54f92ab2d7746fb6f2b4a40a48cd7eb8e3bf772f3ee89e1979d4d7b741fb2a05"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "5.0.0"
bluez: bluez:
dependency: transitive dependency: transitive
description: description:
@ -68,10 +68,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.2" version: "1.18.0"
convert: convert:
dependency: "direct main" dependency: "direct main"
description: description:
@ -92,10 +92,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: dbus name: dbus
sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.8" version: "0.7.10"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -134,10 +134,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: ad76540d21c066228ee3f9d1dad64a9f7e46530e8bb7c85011a88bc1fd874bc5 sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.0.1"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -157,10 +157,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: intl name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.18.1" version: "0.19.0"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -205,10 +205,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.10.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -221,26 +221,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: petitparser name: petitparser
sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.4.0" version: "6.0.2"
platform: platform:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.1.2"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.6" version: "2.1.7"
process: process:
dependency: transitive dependency: transitive
description: description:
@ -266,18 +266,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.11.1"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.2"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
@ -306,10 +306,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.0" version: "0.6.1"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -330,18 +330,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vm_service name: vm_service
sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.7.1" version: "11.10.0"
web: web:
dependency: transitive dependency: transitive
description: description:
name: web name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.4-beta" version: "0.3.0"
webdriver: webdriver:
dependency: transitive dependency: transitive
description: description:
@ -354,10 +354,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: xml name: xml
sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.0" version: "6.5.0"
sdks: sdks:
dart: ">=3.1.0-185.0.dev <4.0.0" dart: ">=3.2.0 <4.0.0"
flutter: ">=3.3.0" flutter: ">=3.3.0"

View File

@ -17,7 +17,7 @@ dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
bluetooth_low_energy_platform_interface: ^4.0.0 bluetooth_low_energy_platform_interface: ^5.0.0
bluetooth_low_energy_linux: bluetooth_low_energy_linux:
# When depending on this package from a real application you should use: # When depending on this package from a real application you should use:
# bluetooth_low_energy: ^x.y.z # bluetooth_low_energy: ^x.y.z
@ -30,7 +30,7 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
convert: ^3.1.1 convert: ^3.1.1
intl: ^0.18.1 intl: ^0.19.0
dev_dependencies: dev_dependencies:
integration_test: integration_test:

View File

@ -1,9 +1,9 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'; import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'src/my_central_manager2.dart'; import 'src/my_central_manager.dart';
abstract class BluetoothLowEnergyLinux { abstract class BluetoothLowEnergyLinux {
static void registerWith() { static void registerWith() {
MyCentralManager.instance = MyCentralManager2(); CentralManager.instance = MyCentralManager();
} }
} }

View File

@ -7,81 +7,6 @@ import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart'; import 'my_gatt_descriptor2.dart';
import 'my_gatt_service2.dart'; import 'my_gatt_service2.dart';
extension BlueZAdapterX on BlueZAdapter {
BluetoothLowEnergyState get myState {
return powered
? BluetoothLowEnergyState.poweredOn
: BluetoothLowEnergyState.poweredOff;
}
}
extension BlueZDeviceX on BlueZDevice {
BlueZUUID get uuid {
final node = address.replaceAll(':', '');
// We don't know the timestamp of the bluetooth device, use nil UUID as prefix.
return BlueZUUID.fromString("00000000-0000-0000-0000-$node");
}
UUID get myUUID => uuid.toMyUUID();
List<MyGattService2> get myServices =>
gattServices.map((service) => MyGattService2(service)).toList();
Advertisement get myAdvertisement {
final myName = name.isNotEmpty ? name : null;
final myServiceUUIDs = uuids.map((uuid) => uuid.toMyUUID()).toList();
final myServiceData = serviceData.map((uuid, data) {
final myUUID = uuid.toMyUUID();
final myData = Uint8List.fromList(data);
return MapEntry(myUUID, myData);
});
return Advertisement(
name: myName,
serviceUUIDs: myServiceUUIDs,
serviceData: myServiceData,
manufacturerSpecificData: myManufacturerSpecificData,
);
}
ManufacturerSpecificData get myManufacturerSpecificData {
final entry = manufacturerData.entries.last;
final myId = entry.key.id;
final myData = Uint8List.fromList(entry.value);
return ManufacturerSpecificData(
id: myId,
data: myData,
);
}
}
extension BlueZGattServiceX on BlueZGattService {
UUID get myUUID => uuid.toMyUUID();
List<MyGattCharacteristic2> get myCharacteristics => characteristics
.map((characteristic) => MyGattCharacteristic2(characteristic))
.toList();
}
extension MyBlueZGattCharacteristic on BlueZGattCharacteristic {
UUID get myUUID => uuid.toMyUUID();
List<GattCharacteristicProperty> get myProperties => flags
.map((e) => e.toMyProperty())
.whereType<GattCharacteristicProperty>()
.toList();
List<MyGattDescriptor2> get myDescriptors =>
descriptors.map((descriptor) => MyGattDescriptor2(descriptor)).toList();
}
extension BlueZGattDescriptorX on BlueZGattDescriptor {
UUID get myUUID => uuid.toMyUUID();
}
extension BlueZUUIDX on BlueZUUID {
UUID toMyUUID() => UUID(value);
}
extension BlueZGattCharacteristicFlagX on BlueZGattCharacteristicFlag { extension BlueZGattCharacteristicFlagX on BlueZGattCharacteristicFlag {
GattCharacteristicProperty? toMyProperty() { GattCharacteristicProperty? toMyProperty() {
switch (this) { switch (this) {
@ -113,3 +38,75 @@ extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType {
} }
} }
} }
extension BlueZAdapterX on BlueZAdapter {
BluetoothLowEnergyState get myState {
return powered
? BluetoothLowEnergyState.poweredOn
: BluetoothLowEnergyState.poweredOff;
}
}
extension BlueZDeviceX on BlueZDevice {
UUID get myUUID => UUID.fromAddress(address);
List<MyGattService2> get myServices =>
gattServices.map((service) => MyGattService2(service)).toList();
Advertisement get myAdvertisement {
final myName = name.isNotEmpty ? name : null;
final myServiceUUIDs = uuids.map((uuid) => uuid.toMyUUID()).toList();
final myServiceData = serviceData.map((uuid, data) {
final myUUID = uuid.toMyUUID();
final myData = Uint8List.fromList(data);
return MapEntry(myUUID, myData);
});
return Advertisement(
name: myName,
serviceUUIDs: myServiceUUIDs,
serviceData: myServiceData,
manufacturerSpecificData: myManufacturerSpecificData,
);
}
ManufacturerSpecificData? get myManufacturerSpecificData {
final entry = manufacturerData.entries.lastOrNull;
if (entry == null) {
return null;
}
final myId = entry.key.id;
final myData = Uint8List.fromList(entry.value);
return ManufacturerSpecificData(
id: myId,
data: myData,
);
}
}
extension BlueZGattDescriptorX on BlueZGattDescriptor {
UUID get myUUID => uuid.toMyUUID();
}
extension MyBlueZGattCharacteristic on BlueZGattCharacteristic {
UUID get myUUID => uuid.toMyUUID();
List<GattCharacteristicProperty> get myProperties => flags
.map((e) => e.toMyProperty())
.whereType<GattCharacteristicProperty>()
.toList();
List<MyGattDescriptor2> get myDescriptors =>
descriptors.map((descriptor) => MyGattDescriptor2(descriptor)).toList();
}
extension BlueZGattServiceX on BlueZGattService {
UUID get myUUID => uuid.toMyUUID();
List<MyGattCharacteristic2> get myCharacteristics => characteristics
.map((characteristic) => MyGattCharacteristic2(characteristic))
.toList();
}
extension BlueZUUIDX on BlueZUUID {
UUID toMyUUID() => UUID(value);
}

View File

@ -0,0 +1,364 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
import 'my_event_args.dart';
import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart';
import 'my_gatt_service2.dart';
import 'my_peripheral2.dart';
class MyCentralManager extends CentralManager {
final BlueZClient _blueZClient;
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
final StreamController<DiscoveredEventArgs> _discoveredController;
final StreamController<ConnectionStateChangedEventArgs>
_connectionStateChangedController;
final StreamController<GattCharacteristicNotifiedEventArgs>
_characteristicNotifiedController;
final StreamController<BlueZDeviceServicesResolvedEventArgs>
_blueZServicesResolvedController;
final Map<int, StreamSubscription>
_blueZCharacteristicPropertiesChangedSubscriptions;
final Map<String, List<MyGattService2>> _services;
BluetoothLowEnergyState _state;
MyCentralManager()
: _blueZClient = BlueZClient(),
_stateChangedController = StreamController.broadcast(),
_discoveredController = StreamController.broadcast(),
_connectionStateChangedController = StreamController.broadcast(),
_characteristicNotifiedController = StreamController.broadcast(),
_blueZServicesResolvedController = StreamController.broadcast(),
_blueZCharacteristicPropertiesChangedSubscriptions = {},
_services = {},
_state = BluetoothLowEnergyState.unknown;
BlueZAdapter get _blueZAdapter => _blueZClient.adapters.first;
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<DiscoveredEventArgs> get discovered => _discoveredController.stream;
@override
Stream<ConnectionStateChangedEventArgs> get connectionStateChanged =>
_connectionStateChangedController.stream;
@override
Stream<GattCharacteristicNotifiedEventArgs> get characteristicNotified =>
_characteristicNotifiedController.stream;
Stream<BlueZDeviceServicesResolvedEventArgs> get _blueZServicesResolved =>
_blueZServicesResolvedController.stream;
@override
Future<void> setUp() async {
logger.info('setUp');
await _blueZClient.connect();
if (_blueZClient.adapters.isEmpty) {
_state = BluetoothLowEnergyState.unsupported;
return;
}
_state = _blueZAdapter.myState;
_blueZAdapter.propertiesChanged.listen(_onBlueZAdapterPropertiesChanged);
for (var blueZDevice in _blueZClient.devices) {
if (blueZDevice.adapter.address != _blueZAdapter.address) {
continue;
}
_beginBlueZDevicePropertiesChangedListener(blueZDevice);
}
_blueZClient.deviceAdded.listen(_onBlueZClientDeviceAdded);
}
@override
Future<BluetoothLowEnergyState> getState() {
logger.info('getState');
return Future.value(_state);
}
@override
Future<void> startDiscovery() async {
logger.info('startDiscovery');
await _blueZAdapter.startDiscovery();
}
@override
Future<void> stopDiscovery() async {
logger.info('stopDiscovery');
await _blueZAdapter.stopDiscovery();
}
@override
Future<void> connect(Peripheral peripheral) async {
if (peripheral is! MyPeripheral2) {
throw TypeError();
}
final blueZDevice = peripheral.blueZDevice;
final blueZAddress = blueZDevice.address;
logger.info('connect: $blueZAddress');
await blueZDevice.connect();
}
@override
Future<void> disconnect(Peripheral peripheral) async {
if (peripheral is! MyPeripheral2) {
throw TypeError();
}
final blueZDevice = peripheral.blueZDevice;
final blueZAddress = blueZDevice.address;
logger.info('disconnect: $blueZAddress');
await blueZDevice.disconnect();
}
@override
Future<int> readRSSI(Peripheral peripheral) async {
if (peripheral is! MyPeripheral2) {
throw TypeError();
}
final blueZDevice = peripheral.blueZDevice;
final blueZAddress = blueZDevice.address;
logger.info('readRSSI: $blueZAddress');
return blueZDevice.rssi;
}
@override
Future<List<GattService>> discoverGATT(Peripheral peripheral) async {
if (peripheral is! MyPeripheral2) {
throw TypeError();
}
final blueZDevice = peripheral.blueZDevice;
final blueZAddress = blueZDevice.address;
logger.info('discoverGATT: $blueZAddress');
if (!blueZDevice.connected) {
throw StateError('Peripheral is disconnected.');
}
if (!blueZDevice.servicesResolved) {
await _blueZServicesResolved.firstWhere(
(eventArgs) => eventArgs.device == blueZDevice,
);
}
final services = _services[blueZAddress] ?? [];
return services;
}
@override
Future<Uint8List> readCharacteristic(
GattCharacteristic characteristic,
) async {
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final blueZCharacteristic = characteristic.blueZCharacteristic;
final blueZUUID = blueZCharacteristic.uuid;
logger.info('readCharacteristic: $blueZUUID');
final blueZValue = await blueZCharacteristic.readValue();
return Uint8List.fromList(blueZValue);
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
required GattCharacteristicWriteType type,
}) async {
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final blueZCharacteristic = characteristic.blueZCharacteristic;
final blueZUUID = blueZCharacteristic.uuid;
final trimmedValue = value.trimGATT();
final blueZType = type.toBlueZWriteType();
logger.info('writeCharacteristic: $blueZUUID - $trimmedValue, $blueZType');
await blueZCharacteristic.writeValue(
trimmedValue,
type: blueZType,
);
}
@override
Future<void> setCharacteristicNotifyState(
GattCharacteristic characteristic, {
required bool state,
}) async {
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final blueZCharacteristic = characteristic.blueZCharacteristic;
final blueZUUID = blueZCharacteristic.uuid;
if (state) {
logger.info('startNotify: $blueZUUID');
await blueZCharacteristic.startNotify();
} else {
logger.info('stopNotify: $blueZUUID');
await blueZCharacteristic.stopNotify();
}
}
@override
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final blueZDescriptor = descriptor.blueZDescriptor;
final blueZUUID = blueZDescriptor.uuid;
logger.info('readDescriptor: $blueZUUID');
final blueZValue = await blueZDescriptor.readValue();
return Uint8List.fromList(blueZValue);
}
@override
Future<void> writeDescriptor(
GattDescriptor descriptor, {
required Uint8List value,
}) async {
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final blueZDescriptor = descriptor.blueZDescriptor;
final blueZUUID = blueZDescriptor.uuid;
final trimmedValue = value.trimGATT();
logger.info('writeDescriptor: $blueZUUID - $trimmedValue');
await blueZDescriptor.writeValue(trimmedValue);
}
void _onBlueZAdapterPropertiesChanged(List<String> blueZAdapterProperties) {
logger.info('onBlueZAdapterPropertiesChanged: $blueZAdapterProperties');
for (var blueZAdapterProperty in blueZAdapterProperties) {
switch (blueZAdapterProperty) {
case 'Powered':
final state = _blueZAdapter.myState;
if (_state == state) {
return;
}
_state = state;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
break;
default:
break;
}
}
}
void _onBlueZClientDeviceAdded(BlueZDevice blueZDevice) {
logger.info('onBlueZClientDeviceAdded: ${blueZDevice.address}');
if (blueZDevice.adapter.address != _blueZAdapter.address) {
return;
}
_onBlueZDiscovered(blueZDevice);
_beginBlueZDevicePropertiesChangedListener(blueZDevice);
}
void _onBlueZDiscovered(BlueZDevice blueZDevice) {
final peripheral = MyPeripheral2(blueZDevice);
final rssi = blueZDevice.rssi;
final advertisement = blueZDevice.myAdvertisement;
final eventArgs = DiscoveredEventArgs(
peripheral,
rssi,
advertisement,
);
_discoveredController.add(eventArgs);
}
void _beginBlueZDevicePropertiesChangedListener(BlueZDevice blueZDevice) {
blueZDevice.propertiesChanged.listen((blueZDeviceProperties) {
logger.info(
'onBlueZDevicePropertiesChanged: ${blueZDevice.address}, $blueZDeviceProperties');
for (var blueZDeviceProperty in blueZDeviceProperties) {
switch (blueZDeviceProperty) {
case 'RSSI':
_onBlueZDiscovered(blueZDevice);
break;
case 'Connected':
final peripheral = MyPeripheral2(blueZDevice);
final state = blueZDevice.connected;
final eventArgs = ConnectionStateChangedEventArgs(
peripheral,
state,
);
_connectionStateChangedController.add(eventArgs);
if (!state) {
_endBlueZCharacteristicPropertiesChangedListener(blueZDevice);
}
break;
case 'UUIDs':
break;
case 'ServicesResolved':
if (blueZDevice.servicesResolved) {
_endBlueZCharacteristicPropertiesChangedListener(blueZDevice);
_services[blueZDevice.address] = blueZDevice.myServices;
_beginBlueZCharacteristicPropertiesChangedListener(blueZDevice);
final eventArgs =
BlueZDeviceServicesResolvedEventArgs(blueZDevice);
_blueZServicesResolvedController.add(eventArgs);
}
break;
default:
break;
}
}
});
}
void _beginBlueZCharacteristicPropertiesChangedListener(
BlueZDevice blueZDevice,
) {
final services = _services[blueZDevice.address];
if (services == null) {
return;
}
for (var service in services) {
final characteristics = service.characteristics;
for (var characteristic in characteristics) {
final blueZCharacteristic = characteristic.blueZCharacteristic;
final subscription = blueZCharacteristic.propertiesChanged.listen(
(blueZCharacteristicProperties) {
logger.info(
'onBlueZCharacteristicPropertiesChanged: ${blueZDevice.address}.${blueZCharacteristic.uuid}, $blueZCharacteristicProperties');
for (var blueZCharacteristicPropety
in blueZCharacteristicProperties) {
switch (blueZCharacteristicPropety) {
case 'Value':
final value = Uint8List.fromList(blueZCharacteristic.value);
final eventArgs = GattCharacteristicNotifiedEventArgs(
characteristic,
value,
);
_characteristicNotifiedController.add(eventArgs);
break;
default:
break;
}
}
},
);
_blueZCharacteristicPropertiesChangedSubscriptions[
blueZCharacteristic.hashCode] = subscription;
}
}
}
void _endBlueZCharacteristicPropertiesChangedListener(
BlueZDevice blueZDevice,
) {
final services = _services.remove(blueZDevice.address);
if (services == null) {
return;
}
for (var service in services) {
final characteristics = service.characteristics;
for (var characteristic in characteristics) {
final blueZCharacteristic = characteristic.blueZCharacteristic;
final subscription = _blueZCharacteristicPropertiesChangedSubscriptions
.remove(blueZCharacteristic.hashCode);
subscription?.cancel();
}
}
}
}

View File

@ -1,341 +0,0 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
import 'my_event_args.dart';
import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart';
import 'my_gatt_service2.dart';
import 'my_peripheral2.dart';
class MyCentralManager2 extends MyCentralManager {
MyCentralManager2()
: _client = BlueZClient(),
_stateChangedController = StreamController.broadcast(),
_discoveredController = StreamController.broadcast(),
_peripheralStateChangedController = StreamController.broadcast(),
_characteristicValueChangedController = StreamController.broadcast(),
_deviceServicesResolvedController = StreamController.broadcast(),
_myServicesOfMyPeripherals = {},
_characteristicPropertiesChangedSubscriptions = {},
_state = BluetoothLowEnergyState.unknown;
final BlueZClient _client;
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
final StreamController<DiscoveredEventArgs> _discoveredController;
final StreamController<PeripheralStateChangedEventArgs>
_peripheralStateChangedController;
final StreamController<GattCharacteristicValueChangedEventArgs>
_characteristicValueChangedController;
final StreamController<BlueZDeviceServicesResolvedEventArgs>
_deviceServicesResolvedController;
final Map<int, List<MyGattService2>> _myServicesOfMyPeripherals;
final Map<int, StreamSubscription>
_characteristicPropertiesChangedSubscriptions;
BlueZAdapter get _adapter => _client.adapters.first;
BluetoothLowEnergyState _state;
@override
BluetoothLowEnergyState get state => _state;
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<DiscoveredEventArgs> get discovered => _discoveredController.stream;
@override
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
_peripheralStateChangedController.stream;
@override
Stream<GattCharacteristicValueChangedEventArgs>
get characteristicValueChanged =>
_characteristicValueChangedController.stream;
Stream<BlueZDeviceServicesResolvedEventArgs> get _servicesResolved =>
_deviceServicesResolvedController.stream;
Future<void> _throwWithoutState(BluetoothLowEnergyState state) async {
if (this.state != state) {
throw StateError(
'$state is expected, but current state is ${this.state}.');
}
}
@override
Future<void> setUp() async {
// TODO: hot restart is not handled.
await _client.connect();
_state = _client.adapters.isEmpty
? BluetoothLowEnergyState.unsupported
: _adapter.myState;
if (_state == BluetoothLowEnergyState.unsupported) {
return;
}
for (var device in _client.devices) {
if (device.adapter.address != _adapter.address) {
continue;
}
_beginDevicePropertiesChangedListener(device);
}
_adapter.propertiesChanged.listen(_onAdapterPropertiesChanged);
_client.deviceAdded.listen(_onDeviceAdded);
}
@override
Future<void> startDiscovery() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _adapter.startDiscovery();
}
@override
Future<void> stopDiscovery() async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _adapter.stopDiscovery();
}
@override
Future<void> connect(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myPeripheral = peripheral as MyPeripheral2;
final device = myPeripheral.device;
await device.connect();
}
@override
Future<void> disconnect(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myPeripheral = peripheral as MyPeripheral2;
final device = myPeripheral.device;
await device.disconnect();
}
@override
Future<int> getMaximumWriteLength(
Peripheral peripheral, {
required GattCharacteristicWriteType type,
}) async {
// TODO: 当前版本 `bluez` 插件不支持获取 MTU返回最小值 20.
return 20;
}
@override
Future<int> readRSSI(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myPeripheral = peripheral as MyPeripheral2;
final device = myPeripheral.device;
return device.rssi;
}
@override
Future<List<GattService>> discoverGATT(Peripheral peripheral) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myPeripheral = peripheral as MyPeripheral2;
final device = myPeripheral.device;
if (!device.connected) {
throw StateError('Peripheral is disconnected.');
}
if (!device.servicesResolved) {
await _servicesResolved.firstWhere(
(eventArgs) => eventArgs.device == device,
);
}
final myServices = _myServicesOfMyPeripherals[myPeripheral.hashCode];
if (myServices == null) {
throw ArgumentError.notNull();
}
return myServices;
}
@override
Future<Uint8List> readCharacteristic(
GattCharacteristic characteristic,
) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic2;
final blueZCharacteristic = myCharacteristic.characteristic;
final blueZValue = await blueZCharacteristic.readValue();
return Uint8List.fromList(blueZValue);
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
required GattCharacteristicWriteType type,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic2;
final blueZCharacteristic = myCharacteristic.characteristic;
await blueZCharacteristic.writeValue(
value,
type: type.toBlueZWriteType(),
);
}
@override
Future<void> notifyCharacteristic(
GattCharacteristic characteristic, {
required bool state,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic2;
final blueZCharacteristic = myCharacteristic.characteristic;
if (state) {
await blueZCharacteristic.startNotify();
} else {
await blueZCharacteristic.stopNotify();
}
}
@override
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myDescriptor = descriptor as MyGattDescriptor2;
final blueZDescriptor = myDescriptor.descriptor;
final blueZValue = await blueZDescriptor.readValue();
return Uint8List.fromList(blueZValue);
}
@override
Future<void> writeDescriptor(
GattDescriptor descriptor, {
required Uint8List value,
}) async {
await _throwWithoutState(BluetoothLowEnergyState.poweredOn);
final myDescriptor = descriptor as MyGattDescriptor2;
final blueZDescriptor = myDescriptor.descriptor;
await blueZDescriptor.writeValue(value);
}
void _onAdapterPropertiesChanged(List<String> properties) {
for (var property in properties) {
switch (property) {
case 'Powered':
final myState = _adapter.myState;
if (_state == myState) {
return;
}
_state = myState;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(myState);
_stateChangedController.add(eventArgs);
break;
default:
break;
}
}
}
void _onDeviceAdded(BlueZDevice device) {
if (device.adapter.address != _adapter.address) {
return;
}
_onDiscovered(device);
_beginDevicePropertiesChangedListener(device);
}
void _onDiscovered(BlueZDevice device) {
final myPeripheral = MyPeripheral2(device);
final myRSSI = device.rssi;
final myAdvertiseData = device.myAdvertisement;
final eventArgs = DiscoveredEventArgs(
myPeripheral,
myRSSI,
myAdvertiseData,
);
_discoveredController.add(eventArgs);
}
void _beginDevicePropertiesChangedListener(BlueZDevice device) {
device.propertiesChanged.listen((properties) {
for (var property in properties) {
switch (property) {
case 'RSSI':
_onDiscovered(device);
break;
case 'Connected':
final myPeripheral = MyPeripheral2(device);
final state = device.connected;
final eventArgs = PeripheralStateChangedEventArgs(
myPeripheral,
state,
);
_peripheralStateChangedController.add(eventArgs);
if (!state) {
_endCharacteristicPropertiesChangedListener(myPeripheral);
}
break;
case 'UUIDs':
break;
case 'ServicesResolved':
if (device.servicesResolved) {
final myPeripheral = MyPeripheral2(device);
_endCharacteristicPropertiesChangedListener(myPeripheral);
final myServices = device.myServices;
_myServicesOfMyPeripherals[myPeripheral.hashCode] = myServices;
_beginCharacteristicPropertiesChangedListener(myPeripheral);
final eventArgs = BlueZDeviceServicesResolvedEventArgs(device);
_deviceServicesResolvedController.add(eventArgs);
}
break;
default:
break;
}
}
});
}
void _beginCharacteristicPropertiesChangedListener(
MyPeripheral myPeripheral,
) {
final myServices = _myServicesOfMyPeripherals[myPeripheral.hashCode];
if (myServices == null) {
throw ArgumentError.notNull();
}
for (var myService in myServices) {
final myCharacteristics =
myService.characteristics.cast<MyGattCharacteristic2>();
for (var myCharacteristic in myCharacteristics) {
final characteristic = myCharacteristic.characteristic;
final subscription = characteristic.propertiesChanged.listen(
(properties) {
for (var property in properties) {
switch (property) {
case 'Value':
final myValue = Uint8List.fromList(characteristic.value);
final eventArgs = GattCharacteristicValueChangedEventArgs(
myCharacteristic,
myValue,
);
_characteristicValueChangedController.add(eventArgs);
break;
default:
break;
}
}
},
);
_characteristicPropertiesChangedSubscriptions[
myCharacteristic.hashCode] = subscription;
}
}
}
void _endCharacteristicPropertiesChangedListener(MyPeripheral myPeripheral) {
final myServices = _myServicesOfMyPeripherals.remove(myPeripheral.hashCode);
if (myServices == null) {
return;
}
for (var myService in myServices) {
final myCharacteristics = myService.characteristics;
for (var myCharacteristic in myCharacteristics) {
final subscription = _characteristicPropertiesChangedSubscriptions
.remove(myCharacteristic.hashCode);
subscription?.cancel();
}
}
}
}

View File

@ -2,15 +2,28 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla
import 'package:bluez/bluez.dart'; import 'package:bluez/bluez.dart';
import 'my_bluez.dart'; import 'my_bluez.dart';
import 'my_gatt_descriptor2.dart';
class MyGattCharacteristic2 extends MyGattCharacteristic { class MyGattCharacteristic2 extends MyGattCharacteristic {
final BlueZGattCharacteristic characteristic; final BlueZGattCharacteristic blueZCharacteristic;
MyGattCharacteristic2(this.characteristic) MyGattCharacteristic2(this.blueZCharacteristic)
: super( : super(
hashCode: characteristic.hashCode, uuid: blueZCharacteristic.myUUID,
uuid: characteristic.myUUID, properties: blueZCharacteristic.myProperties,
properties: characteristic.myProperties, descriptors: blueZCharacteristic.myDescriptors,
descriptors: characteristic.myDescriptors,
); );
@override
List<MyGattDescriptor2> get descriptors =>
super.descriptors.cast<MyGattDescriptor2>();
@override
int get hashCode => blueZCharacteristic.hashCode;
@override
bool operator ==(Object other) {
return other is MyGattCharacteristic2 &&
other.blueZCharacteristic == blueZCharacteristic;
}
} }

View File

@ -4,11 +4,19 @@ import 'package:bluez/bluez.dart';
import 'my_bluez.dart'; import 'my_bluez.dart';
class MyGattDescriptor2 extends MyGattDescriptor { class MyGattDescriptor2 extends MyGattDescriptor {
final BlueZGattDescriptor descriptor; final BlueZGattDescriptor blueZDescriptor;
MyGattDescriptor2(this.descriptor) MyGattDescriptor2(this.blueZDescriptor)
: super( : super(
hashCode: descriptor.hashCode, uuid: blueZDescriptor.myUUID,
uuid: descriptor.myUUID,
); );
@override
int get hashCode => blueZDescriptor.hashCode;
@override
bool operator ==(Object other) {
return other is MyGattDescriptor2 &&
other.blueZDescriptor == blueZDescriptor;
}
} }

View File

@ -2,14 +2,26 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla
import 'package:bluez/bluez.dart'; import 'package:bluez/bluez.dart';
import 'my_bluez.dart'; import 'my_bluez.dart';
import 'my_gatt_characteristic2.dart';
class MyGattService2 extends MyGattService { class MyGattService2 extends MyGattService {
final BlueZGattService service; final BlueZGattService blueZService;
MyGattService2(this.service) MyGattService2(this.blueZService)
: super( : super(
hashCode: service.hashCode, uuid: blueZService.myUUID,
uuid: service.myUUID, characteristics: blueZService.myCharacteristics,
characteristics: service.myCharacteristics,
); );
@override
List<MyGattCharacteristic2> get characteristics =>
super.characteristics.cast<MyGattCharacteristic2>();
@override
int get hashCode => blueZService.hashCode;
@override
bool operator ==(Object other) {
return other is MyGattService2 && other.blueZService == blueZService;
}
} }

View File

@ -4,11 +4,10 @@ import 'package:bluez/bluez.dart';
import 'my_bluez.dart'; import 'my_bluez.dart';
class MyPeripheral2 extends MyPeripheral { class MyPeripheral2 extends MyPeripheral {
final BlueZDevice device; final BlueZDevice blueZDevice;
MyPeripheral2(this.device) MyPeripheral2(this.blueZDevice)
: super( : super(
hashCode: device.hashCode, uuid: blueZDevice.myUUID,
uuid: device.myUUID,
); );
} }

View File

@ -1,6 +1,6 @@
name: bluetooth_low_energy_linux name: bluetooth_low_energy_linux
description: Linux implementation of the bluetooth_low_energy plugin. description: Linux implementation of the bluetooth_low_energy plugin.
version: 4.0.0 version: 5.0.0
homepage: https://github.com/yanshouwang/bluetooth_low_energy homepage: https://github.com/yanshouwang/bluetooth_low_energy
environment: environment:
@ -10,7 +10,7 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
bluetooth_low_energy_platform_interface: ^4.0.0 bluetooth_low_energy_platform_interface: ^5.0.0
bluez: ^0.8.1 bluez: ^0.8.1
dev_dependencies: dev_dependencies:

View File

@ -1,3 +1,86 @@
## 5.0.0
* Now `CentralManager#writeCharacteristic` and `PeripheralManager#writeCharacteristic` will fragment the value automatically, the maximum write length is 512 bytes.
* Add `UUID#fromAddress` constructor.
* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`.
* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`.
* Add `PeripheralManager#readCharacteristic`.
* Remove `CentralManager#getMaximumWriteLength` method.
* Remove `PeripheralManager#getMaximumWriteLength` method.
* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`.
* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`.
* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`.
* Move `CentralManager#state` to `CentralManager#getState()`.
* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`.
* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`.
* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`.
* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`.
* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`.
* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#writeCharacteristic`.
* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`.
* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`.
## 5.0.0-dev.10
* Fix `Uint8List#trimGATT` throws when the value is not exceeded 512 bytes.
## 5.0.0-dev.9
* Add `PeripheralManager#characteristicRead`.
## 5.0.0-dev.8
* Add `PeripheralManager#readCharacteristic`.
* Move `PeripheralManager#notifyCharacteristic` to `PeripheralManager#writeCharacteristic`.
## 5.0.0-dev.7
* Remove `GattCharacteristicReadEventArgs`.
* Remove `PeripheralManager#characteristicRead`.
## 5.0.0-dev.6
* Remove the final modifier form `MyGattCharacteristic#value` and `MyGattDescriptor#value` and trim by 512 bytes.
## 5.0.0-dev.5
* Move `CentralManager#state` to `CentralManager#getState()`.
* Move `PeripheralStateChangedEventArgs` to `ConnectionStateChangedEventArgs`.
* Move `GattCharacteristicValueChangedEventArgs` to `GattCharacteristicNotifiedEventArgs`.
* Move `CentralManager#peripheralStateChanged` to `CentralManager#connectionStateChanged`.
* Move `CentralManager#characteristicValueChanged` to `CentralManager#characteristicNotified`.
* Move `CentralManager#notifyCharacteristic` to `CentralManager#setCharacteristicNotifyState`.
* Remove `ReadGattCharacteristicCommandEventArgs` and `WriteGattCharacteristicCommandEventArgs`.
* Move `NotifyGattCharacteristicCommandEventArgs` to `GattCharacteristicNotifyStateChangedEventArgs`.
* Remove `PeripheralManager#readCharacteristicCommandReceived` and `PeripheralManager#writeCharacteristicCommandReceived`.
* Add `PeripheralManager#characteristicRead` and `PeripheralManager#characteristicWritten`.
* Move `PeripheralManager#notifyCharacteristicCommandReceived` to `PeripheralManager#characteristicNotifyStateChanged`.
* Remove `PeripheralManager#sendReadCharacteristicReply` and `PeripheralManager#sendWriteCharacteristicReply`.
* Add `GattCharacteristicReadEventArgs` and `GattCharacteristicWrittenEventArgs`.
* Move `PeripheralManager#notifyCharacteristicValueChanged` to `PeripheralManager#notifyCharacteristic`.
* Remove `MyCentralManager` and `MyPeripheralManager`.
## 5.0.0-dev.4
* Optimize `MyGattService` and `MyGattCharacteristic`.
## 5.0.0-dev.3
* Remove `CentralManager#getMaximumWriteLength` method.
* Remove `PeripheralManager#getMaximumWriteLength` method.
## 5.0.0-dev.2
* Add `UUID#fromAddress` constructor.
* Override `hashCode` and `==` of `MyCentral` and `MyPeripheral`.
## 5.0.0-dev.1
* Add `MyBluetoothLowEnergyPeer` and `MyGattAttribute`.
* Remove `MyObject` base class.
* Use `LoggerProvider` instead of custom logger.
## 4.0.0 ## 4.0.0
* Remove `BluetoothLowEnergy` class. * Remove `BluetoothLowEnergy` class.

View File

@ -9,12 +9,12 @@ same interface.
# Usage # Usage
To implement a new platform-specific implementation of `bluetooth_low_energy`, To implement a new platform-specific implementation of `bluetooth_low_energy`,
extend [`MyCentralManager`][2] with an implementation that performs the extend [`CentralManager`][2] with an implementation that performs the
platform-specific behavior, and when you register your plugin, set the default platform-specific behavior, and when you register your plugin, set the default
`MyCentralManager` by calling `MyCentralManager.instance = MyCentralManagerImpl()`, `CentralManager` by calling `CentralManager.instance = MyCentralManager()`,
extend [`MyPeripheralManager`][3] with an implementation that performs the extend [`PeripheralManager`][3] with an implementation that performs the
platform-specific behavior, and when you register your plugin, set the default platform-specific behavior, and when you register your plugin, set the default
`MyPeripheralManager` by calling `MyPeripheralManager.instance = MyPeripheralManagerImpl()`. `PeripheralManager` by calling `PeripheralManager.instance = MyPeripheralManager()`.
# Note on breaking changes # Note on breaking changes

View File

@ -14,10 +14,10 @@ export 'src/bluetooth_low_energy_state.dart';
export 'src/bluetooth_low_energy_event_args.dart'; export 'src/bluetooth_low_energy_event_args.dart';
export 'src/bluetooth_low_energy_manager.dart'; export 'src/bluetooth_low_energy_manager.dart';
export 'src/bluetooth_low_energy_peer.dart'; export 'src/bluetooth_low_energy_peer.dart';
export 'src/central_manager_event_args.dart'; export 'src/central_event_args.dart';
export 'src/central_manager.dart'; export 'src/central_manager.dart';
export 'src/central.dart'; export 'src/central.dart';
export 'src/peripheral_manager_event_args.dart'; export 'src/peripheral_event_args.dart';
export 'src/peripheral_manager.dart'; export 'src/peripheral_manager.dart';
export 'src/peripheral.dart'; export 'src/peripheral.dart';
export 'src/uuid.dart'; export 'src/uuid.dart';
@ -28,11 +28,10 @@ export 'src/gatt_characteristic.dart';
export 'src/gatt_characteristic_property.dart'; export 'src/gatt_characteristic_property.dart';
export 'src/gatt_characteristic_write_type.dart'; export 'src/gatt_characteristic_write_type.dart';
export 'src/gatt_descriptor.dart'; export 'src/gatt_descriptor.dart';
export 'src/my_central_manager.dart'; export 'src/my_bluetooth_low_energy_peer.dart';
export 'src/my_peripheral_manager.dart';
export 'src/my_object.dart';
export 'src/my_central.dart'; export 'src/my_central.dart';
export 'src/my_peripheral.dart'; export 'src/my_peripheral.dart';
export 'src/my_gatt_attribute.dart';
export 'src/my_gatt_service.dart'; export 'src/my_gatt_service.dart';
export 'src/my_gatt_characteristic.dart'; export 'src/my_gatt_characteristic.dart';
export 'src/my_gatt_descriptor.dart'; export 'src/my_gatt_descriptor.dart';

View File

@ -5,12 +5,12 @@ import 'bluetooth_low_energy_state.dart';
/// The abstract base class that manages central and peripheral objects. /// The abstract base class that manages central and peripheral objects.
abstract class BluetoothLowEnergyManager implements LogController { abstract class BluetoothLowEnergyManager implements LogController {
/// The current state of the manager. /// Tells the manager's state updated.
BluetoothLowEnergyState get state;
/// Tells the managers state updated.
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged; Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged;
/// Sets up the manager. /// Sets up the manager.
Future<void> setUp(); Future<void> setUp();
/// Gets the manager's state.
Future<BluetoothLowEnergyState> getState();
} }

View File

@ -0,0 +1,51 @@
import 'dart:typed_data';
import 'advertisement.dart';
import 'event_args.dart';
import 'gatt_characteristic.dart';
import 'peripheral.dart';
/// The discovered event arguments.
class DiscoveredEventArgs extends EventArgs {
/// The disvered peripheral.
final Peripheral peripheral;
/// The rssi of the peripheral.
final int rssi;
/// The advertisement of the peripheral.
final Advertisement advertisement;
/// Constructs a [DiscoveredEventArgs].
DiscoveredEventArgs(this.peripheral, this.rssi, this.advertisement);
}
/// The connection state cahnged event arguments.
class ConnectionStateChangedEventArgs extends EventArgs {
/// The peripheral which connection state changed.
final Peripheral peripheral;
/// The connection state.
final bool connectionState;
/// Constructs a [ConnectionStateChangedEventArgs].
ConnectionStateChangedEventArgs(
this.peripheral,
this.connectionState,
);
}
/// The GATT characteristic notified event arguments.
class GattCharacteristicNotifiedEventArgs extends EventArgs {
/// The GATT characteristic which notified.
final GattCharacteristic characteristic;
/// The notified value.
final Uint8List value;
/// Constructs a [GattCharacteristicNotifiedEventArgs].
GattCharacteristicNotifiedEventArgs(
this.characteristic,
this.value,
);
}

View File

@ -1,28 +1,55 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:log_service/log_service.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'bluetooth_low_energy_manager.dart'; import 'bluetooth_low_energy_manager.dart';
import 'central_manager_event_args.dart'; import 'central_event_args.dart';
import 'gatt_characteristic.dart'; import 'gatt_characteristic.dart';
import 'gatt_characteristic_write_type.dart'; import 'gatt_characteristic_write_type.dart';
import 'gatt_descriptor.dart'; import 'gatt_descriptor.dart';
import 'gatt_service.dart'; import 'gatt_service.dart';
import 'my_central_manager.dart';
import 'peripheral.dart'; import 'peripheral.dart';
/// An object that scans for, discovers, connects to, and manages peripherals. /// An object that scans for, discovers, connects to, and manages peripherals.
abstract class CentralManager extends BluetoothLowEnergyManager { ///
/// The instance of [CentralManager] to use. /// Platform-specific implementations should implement this class to support [CentralManager].
static CentralManager get instance => MyCentralManager.instance; abstract class CentralManager extends PlatformInterface
with LoggerProvider, LoggerController
implements BluetoothLowEnergyManager {
static final Object _token = Object();
static CentralManager? _instance;
/// The default instance of [CentralManager] to use.
static CentralManager get instance {
final instance = _instance;
if (instance == null) {
throw UnimplementedError(
'CentralManager is not implemented on this platform.');
}
return instance;
}
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [CentralManager] when
/// they register themselves.
static set instance(CentralManager instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
/// Constructs a [CentralManager].
CentralManager() : super(token: _token);
/// Tells the central manager discovered a peripheral while scanning for devices. /// Tells the central manager discovered a peripheral while scanning for devices.
Stream<DiscoveredEventArgs> get discovered; Stream<DiscoveredEventArgs> get discovered;
/// Tells that retrieving the specified peripheral's state changed. /// Tells that retrieving the specified peripheral's connection lost.
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged; Stream<ConnectionStateChangedEventArgs> get connectionStateChanged;
/// Tells that retrieving the specified characteristics value changed. /// Tells that retrieving the specified characteristics value changed.
Stream<GattCharacteristicValueChangedEventArgs> Stream<GattCharacteristicNotifiedEventArgs> get characteristicNotified;
get characteristicValueChanged;
/// Scans for peripherals that are advertising services. /// Scans for peripherals that are advertising services.
Future<void> startDiscovery(); Future<void> startDiscovery();
@ -36,12 +63,6 @@ abstract class CentralManager extends BluetoothLowEnergyManager {
/// Cancels an active or pending local connection to a peripheral. /// Cancels an active or pending local connection to a peripheral.
Future<void> disconnect(Peripheral peripheral); Future<void> disconnect(Peripheral peripheral);
/// Gets the maximum amount of data, in bytes, you can send to a characteristic in a single write type.
Future<int> getMaximumWriteLength(
Peripheral peripheral, {
required GattCharacteristicWriteType type,
});
/// Retrieves the current RSSI value for the peripheral while connected to the central manager. /// Retrieves the current RSSI value for the peripheral while connected to the central manager.
Future<int> readRSSI(Peripheral peripheral); Future<int> readRSSI(Peripheral peripheral);
@ -52,6 +73,8 @@ abstract class CentralManager extends BluetoothLowEnergyManager {
Future<Uint8List> readCharacteristic(GattCharacteristic characteristic); Future<Uint8List> readCharacteristic(GattCharacteristic characteristic);
/// Writes the value of a characteristic. /// Writes the value of a characteristic.
///
/// The maximum size of the value is 512, all bytes that exceed this size will be discarded.
Future<void> writeCharacteristic( Future<void> writeCharacteristic(
GattCharacteristic characteristic, { GattCharacteristic characteristic, {
required Uint8List value, required Uint8List value,
@ -59,7 +82,7 @@ abstract class CentralManager extends BluetoothLowEnergyManager {
}); });
/// Sets notifications or indications for the value of a specified characteristic. /// Sets notifications or indications for the value of a specified characteristic.
Future<void> notifyCharacteristic( Future<void> setCharacteristicNotifyState(
GattCharacteristic characteristic, { GattCharacteristic characteristic, {
required bool state, required bool state,
}); });

View File

@ -1,45 +0,0 @@
import 'dart:typed_data';
import 'advertisement.dart';
import 'event_args.dart';
import 'gatt_characteristic.dart';
import 'peripheral.dart';
/// The discovered event arguments.
class DiscoveredEventArgs extends EventArgs {
/// The disvered peripheral.
final Peripheral peripheral;
/// The rssi of the peripheral.
final int rssi;
/// The advertisement of the peripheral.
final Advertisement advertisement;
/// Constructs a [DiscoveredEventArgs].
DiscoveredEventArgs(this.peripheral, this.rssi, this.advertisement);
}
/// The peripheral state changed event arguments.
class PeripheralStateChangedEventArgs extends EventArgs {
/// The peripheral which state is changed.
final Peripheral peripheral;
/// The new state of the peripheral.
final bool state;
/// Constructs a [PeripheralStateChangedEventArgs].
PeripheralStateChangedEventArgs(this.peripheral, this.state);
}
/// The GATT characteristic value changed event arguments.
class GattCharacteristicValueChangedEventArgs extends EventArgs {
/// The GATT characteristic which value is changed.
final GattCharacteristic characteristic;
/// The changed value of the characteristic.
final Uint8List value;
/// Constructs a [GattCharacteristicValueChangedEventArgs].
GattCharacteristicValueChangedEventArgs(this.characteristic, this.value);
}

View File

@ -1,3 +1,5 @@
import 'dart:typed_data';
import 'gatt_attribute.dart'; import 'gatt_attribute.dart';
import 'gatt_characteristic_property.dart'; import 'gatt_characteristic_property.dart';
import 'gatt_descriptor.dart'; import 'gatt_descriptor.dart';
@ -17,11 +19,13 @@ abstract class GattCharacteristic extends GattAttribute {
factory GattCharacteristic({ factory GattCharacteristic({
required UUID uuid, required UUID uuid,
required List<GattCharacteristicProperty> properties, required List<GattCharacteristicProperty> properties,
required Uint8List value,
required List<GattDescriptor> descriptors, required List<GattDescriptor> descriptors,
}) => }) =>
MyGattCharacteristic( MyGattCharacteristic(
uuid: uuid, uuid: uuid,
properties: properties, properties: properties,
value: value,
descriptors: descriptors.cast<MyGattDescriptor>(), descriptors: descriptors.cast<MyGattDescriptor>(),
); );
} }

View File

@ -0,0 +1,11 @@
import 'bluetooth_low_energy_peer.dart';
import 'uuid.dart';
abstract class MyBluetoothLowEnergyPeer implements BluetoothLowEnergyPeer {
@override
final UUID uuid;
MyBluetoothLowEnergyPeer({
required this.uuid,
});
}

View File

@ -1,13 +1,16 @@
import 'central.dart'; import 'central.dart';
import 'my_object.dart'; import 'my_bluetooth_low_energy_peer.dart';
import 'uuid.dart';
class MyCentral extends MyObject implements Central {
@override
final UUID uuid;
class MyCentral extends MyBluetoothLowEnergyPeer implements Central {
MyCentral({ MyCentral({
required super.hashCode, required super.uuid,
required this.uuid,
}); });
@override
int get hashCode => uuid.hashCode;
@override
bool operator ==(Object other) {
return other is Central && other.uuid == uuid;
}
} }

View File

@ -1,41 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:log_service/log_service.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'central_manager.dart';
/// Platform-specific implementations should implement this class to support
/// [CentralManager].
abstract class MyCentralManager extends PlatformInterface
with LoggerController
implements CentralManager {
static final Object _token = Object();
static MyCentralManager? _instance;
/// The default instance of [MyCentralManager] to use.
static MyCentralManager get instance {
final instance = _instance;
if (instance == null) {
throw UnimplementedError(
'CentralManager is not implemented on this platform.',
);
}
return instance;
}
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [MyCentralManager] when
/// they register themselves.
static set instance(MyCentralManager instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
/// Constructs a [MyCentralManager].
MyCentralManager() : super(token: _token);
@protected
@override
Logger get logger => Logger('$CentralManager');
}

View File

@ -0,0 +1,19 @@
import 'dart:typed_data';
import 'gatt_attribute.dart';
import 'uuid.dart';
abstract class MyGattAttribute implements GattAttribute {
@override
final UUID uuid;
MyGattAttribute({
required this.uuid,
});
}
extension MyGattAttributeUint8List on Uint8List {
Uint8List trimGATT() {
return length > 512 ? sublist(0, 512) : this;
}
}

View File

@ -1,21 +1,27 @@
import 'dart:typed_data';
import 'gatt_characteristic.dart'; import 'gatt_characteristic.dart';
import 'gatt_characteristic_property.dart'; import 'gatt_characteristic_property.dart';
import 'my_gatt_attribute.dart';
import 'my_gatt_descriptor.dart'; import 'my_gatt_descriptor.dart';
import 'my_object.dart';
import 'uuid.dart';
class MyGattCharacteristic extends MyObject implements GattCharacteristic { class MyGattCharacteristic extends MyGattAttribute
@override implements GattCharacteristic {
final UUID uuid;
@override @override
final List<GattCharacteristicProperty> properties; final List<GattCharacteristicProperty> properties;
Uint8List? _value;
@override @override
final List<MyGattDescriptor> descriptors; final List<MyGattDescriptor> descriptors;
MyGattCharacteristic({ MyGattCharacteristic({
super.hashCode, required super.uuid,
required this.uuid,
required this.properties, required this.properties,
Uint8List? value,
required this.descriptors, required this.descriptors,
}); }) : _value = value?.trimGATT();
Uint8List get value => _value ?? Uint8List.fromList([]);
set value(Uint8List value) {
_value = value.trimGATT();
}
} }

View File

@ -1,17 +1,18 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'gatt_descriptor.dart'; import 'gatt_descriptor.dart';
import 'my_object.dart'; import 'my_gatt_attribute.dart';
import 'uuid.dart';
class MyGattDescriptor extends MyObject implements GattDescriptor { class MyGattDescriptor extends MyGattAttribute implements GattDescriptor {
@override Uint8List? _value;
final UUID uuid;
final Uint8List? value;
MyGattDescriptor({ MyGattDescriptor({
super.hashCode, required super.uuid,
required this.uuid, Uint8List? value,
this.value, }) : _value = value?.trimGATT();
});
Uint8List get value => _value ?? Uint8List.fromList([]);
set value(Uint8List value) {
_value = value.trimGATT();
}
} }

View File

@ -1,17 +1,13 @@
import 'gatt_service.dart'; import 'gatt_service.dart';
import 'my_gatt_attribute.dart';
import 'my_gatt_characteristic.dart'; import 'my_gatt_characteristic.dart';
import 'my_object.dart';
import 'uuid.dart';
class MyGattService extends MyObject implements GattService { class MyGattService extends MyGattAttribute implements GattService {
@override
final UUID uuid;
@override @override
final List<MyGattCharacteristic> characteristics; final List<MyGattCharacteristic> characteristics;
MyGattService({ MyGattService({
super.hashCode, required super.uuid,
required this.uuid,
required this.characteristics, required this.characteristics,
}); });
} }

View File

@ -1,13 +0,0 @@
abstract class MyObject {
final int? _hashCode;
MyObject({int? hashCode}) : _hashCode = hashCode;
@override
int get hashCode => _hashCode ?? super.hashCode;
@override
bool operator ==(Object other) {
return other is MyObject && other.hashCode == hashCode;
}
}

View File

@ -1,13 +1,16 @@
import 'my_object.dart'; import 'my_bluetooth_low_energy_peer.dart';
import 'peripheral.dart'; import 'peripheral.dart';
import 'uuid.dart';
class MyPeripheral extends MyObject implements Peripheral {
@override
final UUID uuid;
class MyPeripheral extends MyBluetoothLowEnergyPeer implements Peripheral {
MyPeripheral({ MyPeripheral({
required super.hashCode, required super.uuid,
required this.uuid,
}); });
@override
int get hashCode => uuid.hashCode;
@override
bool operator ==(Object other) {
return other is Peripheral && other.uuid == uuid;
}
} }

View File

@ -1,41 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:log_service/log_service.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'peripheral_manager.dart';
/// Platform-specific implementations should implement this class to support
/// [PeripheralManager].
abstract class MyPeripheralManager extends PlatformInterface
with LoggerController
implements PeripheralManager {
static final Object _token = Object();
static MyPeripheralManager? _instance;
/// The default instance of [MyPeripheralManager] to use.
static MyPeripheralManager get instance {
final instance = _instance;
if (instance == null) {
throw UnimplementedError(
'PeripheralManager is not implemented on this platform.',
);
}
return instance;
}
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [MyPeripheralManager] when
/// they register themselves.
static set instance(MyPeripheralManager instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
/// Constructs a [MyPeripheralManager].
MyPeripheralManager() : super(token: _token);
@protected
@override
Logger get logger => Logger('$PeripheralManager');
}

View File

@ -0,0 +1,61 @@
import 'dart:typed_data';
import 'central.dart';
import 'gatt_characteristic.dart';
/// The GATT characteristic written event arguments.
class GattCharacteristicReadEventArgs {
/// The central which read this characteristic.
final Central central;
/// The GATT characteristic which value is read.
final GattCharacteristic characteristic;
/// The value.
final Uint8List value;
/// Constructs a [GattCharacteristicReadEventArgs].
GattCharacteristicReadEventArgs(
this.central,
this.characteristic,
this.value,
);
}
/// The GATT characteristic written event arguments.
class GattCharacteristicWrittenEventArgs {
/// The central which wrote this characteristic.
final Central central;
/// The GATT characteristic which value is written.
final GattCharacteristic characteristic;
/// The value.
final Uint8List value;
/// Constructs a [GattCharacteristicWrittenEventArgs].
GattCharacteristicWrittenEventArgs(
this.central,
this.characteristic,
this.value,
);
}
/// The GATT characteristic notify state changed event arguments.
class GattCharacteristicNotifyStateChangedEventArgs {
/// The central which set this notify state.
final Central central;
/// The GATT characteristic which notify state changed.
final GattCharacteristic characteristic;
/// The notify state.
final bool state;
/// Constructs a [GattCharacteristicNotifyStateChangedEventArgs].
GattCharacteristicNotifyStateChangedEventArgs(
this.central,
this.characteristic,
this.state,
);
}

Some files were not shown because too many files have changed in this diff Show More