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
* Remove `BluetoothLowEnergy` class.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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
* Remove `BluetoothLowEnergy` class.

View File

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

View File

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

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
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.
* @property code The error code.
@ -82,84 +85,55 @@ enum class MyGattCharacteristicWriteTypeArgs(val raw: Int) {
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyCentralManagerArgs (
val stateNumberArgs: Long
enum class MyGattCharacteristicNotifyStateArgs(val raw: Int) {
NONE(0),
NOTIFY(1),
INDICATE(2);
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyCentralManagerArgs {
val stateNumberArgs = list[0].let { if (it is Int) it.toLong() else it as Long }
return MyCentralManagerArgs(stateNumberArgs)
fun ofRaw(raw: Int): MyGattCharacteristicNotifyStateArgs? {
return values().firstOrNull { it.raw == raw }
}
}
fun toList(): List<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. */
data class MyPeripheralManagerArgs (
val stateNumberArgs: Long
data class MyManufacturerSpecificDataArgs (
val idArgs: Long,
val dataArgs: ByteArray
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyPeripheralManagerArgs {
val stateNumberArgs = list[0].let { if (it is Int) it.toLong() else it as Long }
return MyPeripheralManagerArgs(stateNumberArgs)
fun fromList(list: List<Any?>): MyManufacturerSpecificDataArgs {
val idArgs = list[0].let { if (it is Int) it.toLong() else it as Long }
val dataArgs = list[1] as ByteArray
return MyManufacturerSpecificDataArgs(idArgs, dataArgs)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
stateNumberArgs,
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyCentralArgs (
val hashCodeArgs: Long,
val uuidArgs: String
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<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,
idArgs,
dataArgs,
)
}
}
@ -195,48 +169,64 @@ data class MyAdvertisementArgs (
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyManufacturerSpecificDataArgs (
val idArgs: Long,
val dataArgs: ByteArray
data class MyCentralArgs (
val addressArgs: String
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyManufacturerSpecificDataArgs {
val idArgs = list[0].let { if (it is Int) it.toLong() else it as Long }
val dataArgs = list[1] as ByteArray
return MyManufacturerSpecificDataArgs(idArgs, dataArgs)
fun fromList(list: List<Any?>): MyCentralArgs {
val addressArgs = list[0] as String
return MyCentralArgs(addressArgs)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
idArgs,
dataArgs,
addressArgs,
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyGattServiceArgs (
val hashCodeArgs: Long,
val uuidArgs: String,
val characteristicsArgs: List<MyGattCharacteristicArgs?>
data class MyPeripheralArgs (
val addressArgs: String
) {
companion object {
@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 uuidArgs = list[1] as String
val characteristicsArgs = list[2] as List<MyGattCharacteristicArgs?>
return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs)
val valueArgs = list[2] as ByteArray?
return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, valueArgs)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
hashCodeArgs,
uuidArgs,
characteristicsArgs,
valueArgs,
)
}
}
@ -270,26 +260,26 @@ data class MyGattCharacteristicArgs (
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyGattDescriptorArgs (
data class MyGattServiceArgs (
val hashCodeArgs: Long,
val uuidArgs: String,
val valueArgs: ByteArray? = null
val characteristicsArgs: List<MyGattCharacteristicArgs?>
) {
companion object {
@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 uuidArgs = list[1] as String
val valueArgs = list[2] as ByteArray?
return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, valueArgs)
val characteristicsArgs = list[2] as List<MyGattCharacteristicArgs?>
return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
hashCodeArgs,
uuidArgs,
valueArgs,
characteristicsArgs,
)
}
}
@ -300,20 +290,15 @@ private object MyCentralManagerHostApiCodec : StandardMessageCodec() {
return when (type) {
128.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyCentralManagerArgs.fromList(it)
MyGattCharacteristicArgs.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)
}
}
131.toByte() -> {
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyGattServiceArgs.fromList(it)
}
@ -323,20 +308,16 @@ private object MyCentralManagerHostApiCodec : StandardMessageCodec() {
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
when (value) {
is MyCentralManagerArgs -> {
is MyGattCharacteristicArgs -> {
stream.write(128)
writeValue(stream, value.toList())
}
is MyGattCharacteristicArgs -> {
is MyGattDescriptorArgs -> {
stream.write(129)
writeValue(stream, value.toList())
}
is MyGattDescriptorArgs -> {
stream.write(130)
writeValue(stream, value.toList())
}
is MyGattServiceArgs -> {
stream.write(131)
stream.write(130)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
@ -346,20 +327,19 @@ private object MyCentralManagerHostApiCodec : StandardMessageCodec() {
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface MyCentralManagerHostApi {
fun setUp(callback: (Result<MyCentralManagerArgs>) -> Unit)
fun setUp()
fun startDiscovery(callback: (Result<Unit>) -> Unit)
fun stopDiscovery()
fun connect(peripheralHashCodeArgs: Long, callback: (Result<Unit>) -> Unit)
fun disconnect(peripheralHashCodeArgs: Long, callback: (Result<Unit>) -> Unit)
fun getMaximumWriteLength(peripheralHashCodeArgs: Long, typeNumberArgs: Long): Long
fun readRSSI(peripheralHashCodeArgs: Long, callback: (Result<Long>) -> Unit)
fun discoverGATT(peripheralHashCodeArgs: Long, callback: (Result<List<MyGattServiceArgs>>) -> Unit)
fun requestMTU(peripheralHashCodeArgs: Long, mtuArgs: Long, callback: (Result<Long>) -> Unit)
fun readCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit)
fun writeCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, typeNumberArgs: Long, callback: (Result<Unit>) -> Unit)
fun notifyCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, stateArgs: Boolean, callback: (Result<Unit>) -> Unit)
fun readDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit)
fun writeDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result<Unit>) -> Unit)
fun connect(addressArgs: String, callback: (Result<Unit>) -> Unit)
fun disconnect(addressArgs: String, callback: (Result<Unit>) -> Unit)
fun requestMTU(addressArgs: String, mtuArgs: Long, callback: (Result<Long>) -> Unit)
fun readRSSI(addressArgs: String, callback: (Result<Long>) -> Unit)
fun discoverServices(addressArgs: String, callback: (Result<List<MyGattServiceArgs>>) -> Unit)
fun readCharacteristic(addressArgs: String, hashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit)
fun writeCharacteristic(addressArgs: String, hashCodeArgs: Long, valueArgs: ByteArray, typeNumberArgs: Long, callback: (Result<Unit>) -> Unit)
fun setCharacteristicNotifyState(addressArgs: String, hashCodeArgs: Long, stateNumberArgs: Long, callback: (Result<Unit>) -> Unit)
fun readDescriptor(addressArgs: String, hashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit)
fun writeDescriptor(addressArgs: String, hashCodeArgs: Long, valueArgs: ByteArray, callback: (Result<Unit>) -> Unit)
companion object {
/** 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)
if (api != null) {
channel.setMessageHandler { _, reply ->
api.setUp() { result: Result<MyCentralManagerArgs> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
var wrapped: List<Any?>
try {
api.setUp()
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
@ -426,8 +405,8 @@ interface MyCentralManagerHostApi {
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
api.connect(peripheralHashCodeArgsArg) { result: Result<Unit> ->
val addressArgsArg = args[0] as String
api.connect(addressArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
@ -445,8 +424,8 @@ interface MyCentralManagerHostApi {
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
api.disconnect(peripheralHashCodeArgsArg) { result: Result<Unit> ->
val addressArgsArg = args[0] as String
api.disconnect(addressArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
@ -460,19 +439,21 @@ interface MyCentralManagerHostApi {
}
}
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) {
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 typeNumberArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.getMaximumWriteLength(peripheralHashCodeArgsArg, typeNumberArgsArg))
} catch (exception: Throwable) {
wrapped = wrapError(exception)
val addressArgsArg = args[0] as String
val mtuArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
api.requestMTU(addressArgsArg, mtuArgsArg) { result: Result<Long> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
@ -483,8 +464,8 @@ interface MyCentralManagerHostApi {
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
api.readRSSI(peripheralHashCodeArgsArg) { result: Result<Long> ->
val addressArgsArg = args[0] as String
api.readRSSI(addressArgsArg) { result: Result<Long> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
@ -499,33 +480,12 @@ interface MyCentralManagerHostApi {
}
}
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) {
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 }
api.discoverGATT(peripheralHashCodeArgsArg) { 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 addressArgsArg = args[0] as String
api.discoverServices(addressArgsArg) { result: Result<List<MyGattServiceArgs>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
@ -544,9 +504,9 @@ interface MyCentralManagerHostApi {
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
api.readCharacteristic(peripheralHashCodeArgsArg, characteristicHashCodeArgsArg) { result: Result<ByteArray> ->
val addressArgsArg = args[0] as String
val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
api.readCharacteristic(addressArgsArg, hashCodeArgsArg) { result: Result<ByteArray> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
@ -565,11 +525,11 @@ interface MyCentralManagerHostApi {
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val addressArgsArg = args[0] as String
val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val valueArgsArg = args[2] as ByteArray
val typeNumberArgsArg = args[3].let { if (it is Int) it.toLong() else it as Long }
api.writeCharacteristic(peripheralHashCodeArgsArg, characteristicHashCodeArgsArg, valueArgsArg, typeNumberArgsArg) { result: Result<Unit> ->
api.writeCharacteristic(addressArgsArg, hashCodeArgsArg, valueArgsArg, typeNumberArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
@ -583,14 +543,14 @@ interface MyCentralManagerHostApi {
}
}
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) {
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 characteristicHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val stateArgsArg = args[2] as Boolean
api.notifyCharacteristic(peripheralHashCodeArgsArg, characteristicHashCodeArgsArg, stateArgsArg) { result: Result<Unit> ->
val addressArgsArg = args[0] as String
val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val stateNumberArgsArg = args[2].let { if (it is Int) it.toLong() else it as Long }
api.setCharacteristicNotifyState(addressArgsArg, hashCodeArgsArg, stateNumberArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
@ -608,9 +568,9 @@ interface MyCentralManagerHostApi {
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val descriptorHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
api.readDescriptor(peripheralHashCodeArgsArg, descriptorHashCodeArgsArg) { result: Result<ByteArray> ->
val addressArgsArg = args[0] as String
val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
api.readDescriptor(addressArgsArg, hashCodeArgsArg) { result: Result<ByteArray> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
@ -629,10 +589,10 @@ interface MyCentralManagerHostApi {
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val peripheralHashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val descriptorHashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val addressArgsArg = args[0] as String
val hashCodeArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val valueArgsArg = args[2] as ByteArray
api.writeDescriptor(peripheralHashCodeArgsArg, descriptorHashCodeArgsArg, valueArgsArg) { result: Result<Unit> ->
api.writeDescriptor(addressArgsArg, hashCodeArgsArg, valueArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
@ -658,21 +618,11 @@ private object MyCentralManagerFlutterApiCodec : StandardMessageCodec() {
}
}
129.toByte() -> {
return (readValue(buffer) as? List<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 {
MyManufacturerSpecificDataArgs.fromList(it)
}
}
132.toByte() -> {
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyPeripheralArgs.fromList(it)
}
@ -686,20 +636,12 @@ private object MyCentralManagerFlutterApiCodec : StandardMessageCodec() {
stream.write(128)
writeValue(stream, value.toList())
}
is MyGattCharacteristicArgs -> {
is MyManufacturerSpecificDataArgs -> {
stream.write(129)
writeValue(stream, value.toList())
}
is MyGattDescriptorArgs -> {
stream.write(130)
writeValue(stream, value.toList())
}
is MyManufacturerSpecificDataArgs -> {
stream.write(131)
writeValue(stream, value.toList())
}
is MyPeripheralArgs -> {
stream.write(132)
stream.write(130)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
@ -717,58 +659,77 @@ class MyCentralManagerFlutterApi(private val binaryMessenger: BinaryMessenger) {
}
}
fun onStateChanged(stateNumberArgsArg: Long, callback: (Result<Unit>) -> 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)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)));
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit));
callback(Result.success(Unit))
}
} else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", "")));
callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onDiscovered(peripheralArgsArg: MyPeripheralArgs, rssiArgsArg: Long, advertisementArgsArg: MyAdvertisementArgs, callback: (Result<Unit>) -> 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)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)));
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit));
callback(Result.success(Unit))
}
} else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", "")));
callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onPeripheralStateChanged(peripheralArgsArg: MyPeripheralArgs, stateArgsArg: Boolean, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onPeripheralStateChanged", codec)
channel.send(listOf(peripheralArgsArg, stateArgsArg)) {
fun onConnectionStateChanged(addressArgsArg: String, stateArgsArg: Boolean, callback: (Result<Unit>) -> Unit) {
val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onConnectionStateChanged"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, stateArgsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)));
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit));
callback(Result.success(Unit))
}
} else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", "")));
callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onCharacteristicValueChanged(characteristicArgsArg: MyGattCharacteristicArgs, valueArgsArg: ByteArray, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onCharacteristicValueChanged", codec)
channel.send(listOf(characteristicArgsArg, valueArgsArg)) {
fun onMtuChanged(addressArgsArg: String, mtuArgsArg: Long, callback: (Result<Unit>) -> Unit) {
val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralManagerFlutterApi.onMtuChanged"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, mtuArgsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)));
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit));
callback(Result.success(Unit))
}
} else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", "")));
callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onCharacteristicNotified(addressArgsArg: String, hashCodeArgsArg: Long, valueArgsArg: ByteArray, callback: (Result<Unit>) -> 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)
}
}
133.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyPeripheralManagerArgs.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
@ -832,10 +788,6 @@ private object MyPeripheralManagerHostApiCodec : StandardMessageCodec() {
stream.write(132)
writeValue(stream, value.toList())
}
is MyPeripheralManagerArgs -> {
stream.write(133)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@ -843,16 +795,14 @@ private object MyPeripheralManagerHostApiCodec : StandardMessageCodec() {
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface MyPeripheralManagerHostApi {
fun setUp(callback: (Result<MyPeripheralManagerArgs>) -> Unit)
fun setUp()
fun addService(serviceArgs: MyGattServiceArgs, callback: (Result<Unit>) -> Unit)
fun removeService(serviceHashCodeArgs: Long)
fun removeService(hashCodeArgs: Long)
fun clearServices()
fun startAdvertising(advertisementArgs: MyAdvertisementArgs, callback: (Result<Unit>) -> Unit)
fun stopAdvertising()
fun getMaximumWriteLength(centralHashCodeArgs: Long): Long
fun sendReadCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean, valueArgs: ByteArray)
fun sendWriteCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean)
fun notifyCharacteristicValueChanged(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result<Unit>) -> Unit)
fun sendResponse(addressArgs: String, idArgs: Long, statusNumberArgs: Long, offsetArgs: Long, valueArgs: ByteArray?)
fun notifyCharacteristicChanged(hashCodeArgs: Long, valueArgs: ByteArray, confirmArgs: Boolean, addressArgs: String, callback: (Result<Unit>) -> Unit)
companion object {
/** 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)
if (api != null) {
channel.setMessageHandler { _, reply ->
api.setUp() { result: Result<MyPeripheralManagerArgs> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
var wrapped: List<Any?>
try {
api.setUp()
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
@ -904,10 +853,10 @@ interface MyPeripheralManagerHostApi {
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<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?>
try {
api.removeService(serviceHashCodeArgsArg)
api.removeService(hashCodeArgsArg)
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
@ -972,37 +921,18 @@ interface MyPeripheralManagerHostApi {
}
}
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) {
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 }
var wrapped: List<Any?>
try {
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 addressArgsArg = args[0] as String
val idArgsArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val statusNumberArgsArg = args[2].let { if (it is Int) it.toLong() else it as Long }
val offsetArgsArg = args[3].let { if (it is Int) it.toLong() else it as Long }
val statusArgsArg = args[4] as Boolean
val valueArgsArg = args[5] as ByteArray
val valueArgsArg = args[4] as ByteArray?
var wrapped: List<Any?>
try {
api.sendReadCharacteristicReply(centralHashCodeArgsArg, characteristicHashCodeArgsArg, idArgsArg, offsetArgsArg, statusArgsArg, valueArgsArg)
api.sendResponse(addressArgsArg, idArgsArg, statusNumberArgsArg, offsetArgsArg, valueArgsArg)
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
@ -1014,37 +944,15 @@ interface MyPeripheralManagerHostApi {
}
}
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) {
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 statusArgsArg = args[4] as Boolean
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 hashCodeArgsArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val valueArgsArg = args[1] as ByteArray
val confirmArgsArg = args[2] as Boolean
val addressArgsArg = args[3] as String
api.notifyCharacteristicChanged(hashCodeArgsArg, valueArgsArg, confirmArgsArg, addressArgsArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
@ -1069,16 +977,6 @@ private object MyPeripheralManagerFlutterApiCodec : StandardMessageCodec() {
MyCentralArgs.fromList(it)
}
}
129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyGattCharacteristicArgs.fromList(it)
}
}
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyGattDescriptorArgs.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
@ -1088,14 +986,6 @@ private object MyPeripheralManagerFlutterApiCodec : StandardMessageCodec() {
stream.write(128)
writeValue(stream, value.toList())
}
is MyGattCharacteristicArgs -> {
stream.write(129)
writeValue(stream, value.toList())
}
is MyGattDescriptorArgs -> {
stream.write(130)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@ -1111,58 +1001,107 @@ class MyPeripheralManagerFlutterApi(private val binaryMessenger: BinaryMessenger
}
}
fun onStateChanged(stateNumberArgsArg: Long, callback: (Result<Unit>) -> 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)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)));
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit));
callback(Result.success(Unit))
}
} else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", "")));
callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onReadCharacteristicCommandReceived(centralArgsArg: MyCentralArgs, characteristicArgsArg: MyGattCharacteristicArgs, idArgsArg: Long, offsetArgsArg: Long, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived", codec)
channel.send(listOf(centralArgsArg, characteristicArgsArg, idArgsArg, offsetArgsArg)) {
fun onConnectionStateChanged(centralArgsArg: MyCentralArgs, stateArgsArg: Boolean, callback: (Result<Unit>) -> Unit) {
val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onConnectionStateChanged"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(centralArgsArg, stateArgsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)));
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit));
callback(Result.success(Unit))
}
} else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", "")));
callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onWriteCharacteristicCommandReceived(centralArgsArg: MyCentralArgs, characteristicArgsArg: MyGattCharacteristicArgs, idArgsArg: Long, offsetArgsArg: Long, valueArgsArg: ByteArray, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onWriteCharacteristicCommandReceived", codec)
channel.send(listOf(centralArgsArg, characteristicArgsArg, idArgsArg, offsetArgsArg, valueArgsArg)) {
fun onMtuChanged(addressArgsArg: String, mtuArgsArg: Long, callback: (Result<Unit>) -> Unit) {
val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onMtuChanged"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, mtuArgsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)));
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit));
callback(Result.success(Unit))
}
} else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", "")));
callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onNotifyCharacteristicCommandReceived(centralArgsArg: MyCentralArgs, characteristicArgsArg: MyGattCharacteristicArgs, stateArgsArg: Boolean, callback: (Result<Unit>) -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onNotifyCharacteristicCommandReceived", codec)
channel.send(listOf(centralArgsArg, characteristicArgsArg, stateArgsArg)) {
fun onCharacteristicReadRequest(addressArgsArg: String, hashCodeArgsArg: Long, idArgsArg: Long, offsetArgsArg: Long, callback: (Result<Unit>) -> Unit) {
val channelName = "dev.flutter.pigeon.bluetooth_low_energy_android.MyPeripheralManagerFlutterApi.onCharacteristicReadRequest"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(addressArgsArg, hashCodeArgsArg, idArgsArg, offsetArgsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)));
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit));
callback(Result.success(Unit))
}
} else {
callback(Result.failure(FlutterError("channel-error", "Unable to establish connection on channel.", "")));
callback(Result.failure(createConnectionError(channelName)))
}
}
}
fun onCharacteristicWriteRequest(addressArgsArg: String, hashCodeArgsArg: Long, idArgsArg: Long, offsetArgsArg: Long, valueArgsArg: ByteArray, preparedWriteArgsArg: Boolean, responseNeededArgsArg: Boolean, callback: (Result<Unit>) -> 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,108 +2,54 @@ package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattService
import android.bluetooth.le.AdvertiseData
import android.bluetooth.le.ScanRecord
import android.bluetooth.le.ScanResult
import android.os.ParcelUuid
import android.util.SparseArray
import java.util.UUID
val Any.TAG get() = this::class.java.simpleName as String
val BluetoothAdapter.stateArgs: MyBluetoothLowEnergyStateArgs
get() = state.toBluetoothLowEnergyStateArgs()
fun Int.toBluetoothLowEnergyStateArgs(): MyBluetoothLowEnergyStateArgs {
//region ToObj
fun MyGattCharacteristicWriteTypeArgs.toType(): Int {
return when (this) {
BluetoothAdapter.STATE_ON -> MyBluetoothLowEnergyStateArgs.POWEREDON
else -> MyBluetoothLowEnergyStateArgs.POWEREDOFF
MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
}
}
fun BluetoothDevice.toPeripheralArgs(): MyPeripheralArgs {
val hashCodeArgs = hashCode().toLong()
val uuid = this.uuid.toString()
return MyPeripheralArgs(hashCodeArgs, uuid)
fun MyGattCharacteristicNotifyStateArgs.toValue(): ByteArray {
return when (this) {
MyGattCharacteristicNotifyStateArgs.NONE -> BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
MyGattCharacteristicNotifyStateArgs.NOTIFY -> BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
MyGattCharacteristicNotifyStateArgs.INDICATE -> BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
}
}
fun BluetoothDevice.toCentralArgs(): MyCentralArgs {
val hashCodeArgs = hashCode().toLong()
val uuid = this.uuid.toString()
return MyCentralArgs(hashCodeArgs, uuid)
fun MyGattStatusArgs.toStatus(): Int {
return when (this) {
MyGattStatusArgs.SUCCESS -> BluetoothGatt.GATT_SUCCESS
MyGattStatusArgs.READNOTPERMITTED -> BluetoothGatt.GATT_READ_NOT_PERMITTED
MyGattStatusArgs.WRITENOTPERMITTED -> BluetoothGatt.GATT_READ_NOT_PERMITTED
MyGattStatusArgs.REQUESTNOTSUPPORTED -> BluetoothGatt.GATT_READ_NOT_PERMITTED
MyGattStatusArgs.INVALIDOFFSET -> BluetoothGatt.GATT_INVALID_OFFSET
MyGattStatusArgs.INSUFFICIENTAUTHENTICATION -> BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION
MyGattStatusArgs.INSUFFICIENTENCRYPTION -> BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION
MyGattStatusArgs.INVALIDATTRIBUTELENGTH -> BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH
MyGattStatusArgs.CONNECTIONCONGESTED -> BluetoothGatt.GATT_CONNECTION_CONGESTED
MyGattStatusArgs.FAILURE -> BluetoothGatt.GATT_FAILURE
}
}
val BluetoothDevice.uuid: UUID
get() {
val node = address.filter { char -> char != ':' }
// We don't know the timestamp of the bluetooth device, use nil UUID as prefix.
return UUID.fromString("00000000-0000-0000-0000-$node")
}
val ScanResult.advertisementArgs: MyAdvertisementArgs
get() {
val record = scanRecord
return if (record == null) {
val nameArgs = null
val serviceUUIDsArgs = emptyList<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? {
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()
}
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 {
val advertiseDataBuilder = AdvertiseData.Builder()
if (nameArgs == null) {
advertiseDataBuilder.setIncludeDeviceName(false)
} else {
// TODO: There is an issue that Android will use the cached name before setName takes effect.
// see https://stackoverflow.com/questions/8377558/change-the-android-bluetooth-device-name
adapter.name = nameArgs
advertiseDataBuilder.setIncludeDeviceName(true)
}
@ -124,48 +70,55 @@ fun MyAdvertisementArgs.toAdvertiseData(adapter: BluetoothAdapter): AdvertiseDat
return advertiseDataBuilder.build()
}
fun BluetoothGattService.toManufacturerSpecificDataArgs(characteristicsArgs: List<MyGattCharacteristicArgs>): MyGattServiceArgs {
val hashCodeArgs = hashCode().toLong()
val uuidArgs = this.uuid.toString()
return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs)
fun MyGattDescriptorArgs.toDescriptor(): BluetoothGattDescriptor {
val uuid = UUID.fromString(uuidArgs)
val permissions =
BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
return BluetoothGattDescriptor(uuid, permissions)
}
fun BluetoothGattCharacteristic.toManufacturerSpecificDataArgs(descriptorsArgs: List<MyGattDescriptorArgs>): MyGattCharacteristicArgs {
val hashCodeArgs = hashCode().toLong()
val uuidArgs = this.uuid.toString()
return MyGattCharacteristicArgs(hashCodeArgs, uuidArgs, propertyNumbersArgs, descriptorsArgs)
fun MyGattCharacteristicArgs.toCharacteristic(): BluetoothGattCharacteristic {
val uuid = UUID.fromString(uuidArgs)
val properties = getProperties()
val permissions = getPermissions()
return BluetoothGattCharacteristic(uuid, properties, permissions)
}
val BluetoothGattCharacteristic.propertyNumbersArgs: List<Long>
get() {
val numbersArgs = mutableListOf<Long>()
if (properties and BluetoothGattCharacteristic.PROPERTY_READ != 0) {
val number = MyGattCharacteristicPropertyArgs.READ.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE != 0) {
val number = MyGattCharacteristicPropertyArgs.WRITE.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE != 0) {
val number = MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) {
val number = MyGattCharacteristicPropertyArgs.NOTIFY.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_INDICATE != 0) {
val number = MyGattCharacteristicPropertyArgs.INDICATE.raw.toLong()
numbersArgs.add(number)
}
return numbersArgs
fun MyGattCharacteristicArgs.getProperties(): Int {
val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args ->
val raw = args.toInt()
MyGattCharacteristicPropertyArgs.ofRaw(raw)
}
val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ)
val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE)
val writeWithoutResponse =
propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE)
val notify = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.NOTIFY)
val indicate = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.INDICATE)
var properties = 0
if (read) properties = properties or BluetoothGattCharacteristic.PROPERTY_READ
if (write) properties = properties or BluetoothGattCharacteristic.PROPERTY_WRITE
if (writeWithoutResponse) properties =
properties or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
if (notify) properties = properties or BluetoothGattCharacteristic.PROPERTY_NOTIFY
if (indicate) properties = properties or BluetoothGattCharacteristic.PROPERTY_INDICATE
return properties
}
fun BluetoothGattDescriptor.toManufacturerSpecificDataArgs(): MyGattDescriptorArgs {
val hashCodeArgs = hashCode().toLong()
val uuidArgs = this.uuid.toString()
return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, null)
fun MyGattCharacteristicArgs.getPermissions(): Int {
val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args ->
val raw = args.toInt()
MyGattCharacteristicPropertyArgs.ofRaw(raw)
}
val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ)
val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE)
val writeWithoutResponse =
propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE)
var permissions = 0
if (read) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_READ
if (write || writeWithoutResponse) permissions =
permissions or BluetoothGattCharacteristic.PERMISSION_WRITE
return permissions
}
fun MyGattServiceArgs.toService(): BluetoothGattService {
@ -173,61 +126,142 @@ fun MyGattServiceArgs.toService(): BluetoothGattService {
val serviceType = BluetoothGattService.SERVICE_TYPE_PRIMARY
return BluetoothGattService(uuid, serviceType)
}
//endregion
fun MyGattCharacteristicArgs.toCharacteristic(): BluetoothGattCharacteristic {
val uuid = UUID.fromString(uuidArgs)
return BluetoothGattCharacteristic(uuid, properties, permissions)
}
val MyGattCharacteristicArgs.properties: Int
get() {
val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args ->
val raw = args.toInt()
MyGattCharacteristicPropertyArgs.ofRaw(raw)
}
val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ)
val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE)
val writeWithoutResponse = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE)
val notify = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.NOTIFY)
val indicate = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.INDICATE)
var properties = 0
if (read) properties = properties or BluetoothGattCharacteristic.PROPERTY_READ
if (write) properties = properties or BluetoothGattCharacteristic.PROPERTY_WRITE
if (writeWithoutResponse) properties = properties or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
if (notify) properties = properties or BluetoothGattCharacteristic.PROPERTY_NOTIFY
if (indicate) properties = properties or BluetoothGattCharacteristic.PROPERTY_INDICATE
return properties
}
val MyGattCharacteristicArgs.permissions: Int
get() {
val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args ->
val raw = args.toInt()
MyGattCharacteristicPropertyArgs.ofRaw(raw)
}
val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ)
val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE)
val writeWithoutResponse = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE)
var permissions = 0
if (read) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_READ
if (write || writeWithoutResponse) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_WRITE
return permissions
}
fun MyGattDescriptorArgs.toDescriptor(): BluetoothGattDescriptor {
val uuid = UUID.fromString(uuidArgs)
val permissions = BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
return BluetoothGattDescriptor(uuid, permissions)
}
fun Long.toWriteTypeArgs(): MyGattCharacteristicWriteTypeArgs {
val raw = toInt()
return MyGattCharacteristicWriteTypeArgs.ofRaw(raw) ?: throw IllegalArgumentException()
}
fun MyGattCharacteristicWriteTypeArgs.toType(): Int {
//region ToArgs
fun Int.toBluetoothLowEnergyStateArgs(): MyBluetoothLowEnergyStateArgs {
return when (this) {
MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
BluetoothAdapter.STATE_ON -> MyBluetoothLowEnergyStateArgs.POWEREDON
else -> MyBluetoothLowEnergyStateArgs.POWEREDOFF
}
}
}
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 uuidArgs = this.uuid.toString()
val characteristicsArgs = characteristics.map { it.toArgs() }
return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs)
}
fun BluetoothGattCharacteristic.toArgs(): MyGattCharacteristicArgs {
val hashCodeArgs = hashCode().toLong()
val uuidArgs = this.uuid.toString()
val propertyNumbersArgs = getPropertyNumbersArgs()
val descriptorsArgs = descriptors.map { it.toArgs() }
return MyGattCharacteristicArgs(hashCodeArgs, uuidArgs, propertyNumbersArgs, descriptorsArgs)
}
fun BluetoothGattCharacteristic.getPropertyNumbersArgs(): List<Long> {
val numbersArgs = mutableListOf<Long>()
if (properties and BluetoothGattCharacteristic.PROPERTY_READ != 0) {
val number = MyGattCharacteristicPropertyArgs.READ.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE != 0) {
val number = MyGattCharacteristicPropertyArgs.WRITE.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE != 0) {
val number = MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) {
val number = MyGattCharacteristicPropertyArgs.NOTIFY.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_INDICATE != 0) {
val number = MyGattCharacteristicPropertyArgs.INDICATE.raw.toLong()
numbersArgs.add(number)
}
return numbersArgs
}
fun BluetoothGattDescriptor.toArgs(): MyGattDescriptorArgs {
val hashCodeArgs = hashCode().toLong()
val uuidArgs = this.uuid.toString()
return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, null)
}
fun ByteArray.toNotifyStateArgs(): MyGattCharacteristicNotifyStateArgs {
return if (this contentEquals BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) {
MyGattCharacteristicNotifyStateArgs.NOTIFY
} else if (this contentEquals BluetoothGattDescriptor.ENABLE_INDICATION_VALUE) {
MyGattCharacteristicNotifyStateArgs.INDICATE
} else if (this contentEquals BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE) {
MyGattCharacteristicNotifyStateArgs.NONE
} else {
throw IllegalArgumentException()
}
}
//endregion
val Any.TAG get() = this::class.java.simpleName as String
//val ScanRecord.rawValues: Map<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()
// }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,8 +2,17 @@ package dev.yanshouwang.bluetooth_low_energy_android
import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener
class MyRequestPermissionResultListener(private val bluetoothLowEnergyManager: MyBluetoothLowEnergyManager) : RequestPermissionsResultListener {
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, results: IntArray): Boolean {
return bluetoothLowEnergyManager.onRequestPermissionsResult(requestCode, results)
class MyRequestPermissionResultListener(manager: MyBluetoothLowEnergyManager) :
RequestPermissionsResultListener {
private val mManager: MyBluetoothLowEnergyManager
init {
mManager = manager
}
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<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.ScanResult
class MyScanCallback(private val centralManager: MyCentralManager) : ScanCallback() {
class MyScanCallback(manager: MyCentralManager) : ScanCallback() {
private val mManager: MyCentralManager
init {
mManager = manager
}
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
centralManager.onScanFailed(errorCode)
mManager.onScanFailed(errorCode)
}
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
centralManager.onScanResult(result)
mManager.onScanResult(result)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@ -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 {
unknown,
unsupported,
@ -238,10 +28,212 @@ enum MyGattCharacteristicPropertyArgs {
}
enum MyGattCharacteristicWriteTypeArgs {
// Write with response
withResponse,
// Write without response
withoutResponse,
// Write with response and waiting for confirmation
// reliable,
}
enum MyGattCharacteristicNotifyStateArgs {
none,
notify,
indicate,
}
enum MyGattStatusArgs {
success,
readNotPermitted,
writeNotPermitted,
requestNotSupported,
invalidOffset,
insufficientAuthentication,
insufficientEncryption,
invalidAttributeLength,
connectionCongested,
failure,
}
class MyManufacturerSpecificDataArgs {
final int idArgs;
final Uint8List dataArgs;
MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs);
}
class MyAdvertisementArgs {
final String? nameArgs;
final List<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
description: Android implementation of the bluetooth_low_energy plugin.
version: 4.0.0
version: 5.0.0
homepage: https://github.com/yanshouwang/bluetooth_low_energy
environment:
@ -10,13 +10,13 @@ environment:
dependencies:
flutter:
sdk: flutter
bluetooth_low_energy_platform_interface: ^4.0.0
bluetooth_low_energy_platform_interface: ^5.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
pigeon: ^12.0.1
pigeon: ^15.0.2
flutter:
plugin:

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
* Remove `BluetoothLowEnergy` class.

View File

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

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
import Foundation
@ -10,10 +10,6 @@ import FlutterMacOS
#error("Unsupported platform.")
#endif
private func isNullish(_ value: Any?) -> Bool {
return value is NSNull || value == nil
}
private func wrapResult(_ result: Any?) -> [Any?] {
return [result]
}
@ -33,6 +29,14 @@ private func wrapError(_ error: Any) -> [Any?] {
]
}
private func createConnectionError(withChannelName channelName: String) -> FlutterError {
return FlutterError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "")
}
private func isNullish(_ value: Any?) -> Bool {
return value is NSNull || value == nil
}
private func nilOrValue<T>(_ value: Any?) -> T? {
if value is NSNull { return nil }
return value as! T?
@ -40,10 +44,11 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
enum MyBluetoothLowEnergyStateArgs: Int {
case unknown = 0
case unsupported = 1
case unauthorized = 2
case poweredOff = 3
case poweredOn = 4
case resetting = 1
case unsupported = 2
case unauthorized = 3
case poweredOff = 4
case poweredOn = 5
}
enum MyGattCharacteristicPropertyArgs: Int {
@ -59,82 +64,45 @@ enum MyGattCharacteristicWriteTypeArgs: Int {
case withoutResponse = 1
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyCentralManagerArgs {
var stateNumberArgs: Int64
static func fromList(_ list: [Any?]) -> MyCentralManagerArgs? {
let stateNumberArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
return MyCentralManagerArgs(
stateNumberArgs: stateNumberArgs
)
}
func toList() -> [Any?] {
return [
stateNumberArgs,
]
}
enum MyGattErrorArgs: Int {
case success = 0
case invalidHandle = 1
case readNotPermitted = 2
case writeNotPermitted = 3
case invalidPDU = 4
case insufficientAuthentication = 5
case requestNotSupported = 6
case invalidOffset = 7
case insufficientAuthorization = 8
case prepareQueueFull = 9
case attributeNotFound = 10
case attributeNotLong = 11
case insufficientEncryptionKeySize = 12
case invalidAttributeValueLength = 13
case unlikelyError = 14
case insufficientEncryption = 15
case unsupportedGroupType = 16
case insufficientResources = 17
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyPeripheralManagerArgs {
var stateNumberArgs: Int64
struct MyManufacturerSpecificDataArgs {
var idArgs: Int64
var dataArgs: FlutterStandardTypedData
static func fromList(_ list: [Any?]) -> MyPeripheralManagerArgs? {
let stateNumberArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
static func fromList(_ list: [Any?]) -> MyManufacturerSpecificDataArgs? {
let idArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
let dataArgs = list[1] as! FlutterStandardTypedData
return MyPeripheralManagerArgs(
stateNumberArgs: stateNumberArgs
return MyManufacturerSpecificDataArgs(
idArgs: idArgs,
dataArgs: dataArgs
)
}
func toList() -> [Any?] {
return [
stateNumberArgs,
]
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyCentralArgs {
var hashCodeArgs: Int64
var uuidArgs: String
static func fromList(_ list: [Any?]) -> MyCentralArgs? {
let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
let uuidArgs = list[1] as! String
return MyCentralArgs(
hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs
)
}
func toList() -> [Any?] {
return [
hashCodeArgs,
uuidArgs,
]
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyPeripheralArgs {
var hashCodeArgs: Int64
var uuidArgs: String
static func fromList(_ list: [Any?]) -> MyPeripheralArgs? {
let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
let uuidArgs = list[1] as! String
return MyPeripheralArgs(
hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs
)
}
func toList() -> [Any?] {
return [
hashCodeArgs,
uuidArgs,
idArgs,
dataArgs,
]
}
}
@ -173,49 +141,63 @@ struct MyAdvertisementArgs {
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyManufacturerSpecificDataArgs {
var idArgs: Int64
var dataArgs: FlutterStandardTypedData
struct MyCentralArgs {
var uuidArgs: String
static func fromList(_ list: [Any?]) -> MyManufacturerSpecificDataArgs? {
let idArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
let dataArgs = list[1] as! FlutterStandardTypedData
static func fromList(_ list: [Any?]) -> MyCentralArgs? {
let uuidArgs = list[0] as! String
return MyManufacturerSpecificDataArgs(
idArgs: idArgs,
dataArgs: dataArgs
return MyCentralArgs(
uuidArgs: uuidArgs
)
}
func toList() -> [Any?] {
return [
idArgs,
dataArgs,
uuidArgs,
]
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyGattServiceArgs {
struct MyPeripheralArgs {
var uuidArgs: String
static func fromList(_ list: [Any?]) -> MyPeripheralArgs? {
let uuidArgs = list[0] as! String
return MyPeripheralArgs(
uuidArgs: uuidArgs
)
}
func toList() -> [Any?] {
return [
uuidArgs,
]
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyGattDescriptorArgs {
var hashCodeArgs: Int64
var uuidArgs: String
var characteristicsArgs: [MyGattCharacteristicArgs?]
var valueArgs: FlutterStandardTypedData? = nil
static func fromList(_ list: [Any?]) -> MyGattServiceArgs? {
static func fromList(_ list: [Any?]) -> MyGattDescriptorArgs? {
let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
let uuidArgs = list[1] as! String
let characteristicsArgs = list[2] as! [MyGattCharacteristicArgs?]
let valueArgs: FlutterStandardTypedData? = nilOrValue(list[2])
return MyGattServiceArgs(
return MyGattDescriptorArgs(
hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs,
characteristicsArgs: characteristicsArgs
valueArgs: valueArgs
)
}
func toList() -> [Any?] {
return [
hashCodeArgs,
uuidArgs,
characteristicsArgs,
valueArgs,
]
}
}
@ -251,27 +233,27 @@ struct MyGattCharacteristicArgs {
}
/// Generated class from Pigeon that represents data sent in messages.
struct MyGattDescriptorArgs {
struct MyGattServiceArgs {
var hashCodeArgs: Int64
var uuidArgs: String
var valueArgs: FlutterStandardTypedData? = nil
var characteristicsArgs: [MyGattCharacteristicArgs?]
static func fromList(_ list: [Any?]) -> MyGattDescriptorArgs? {
static func fromList(_ list: [Any?]) -> MyGattServiceArgs? {
let hashCodeArgs = list[0] is Int64 ? list[0] as! Int64 : Int64(list[0] as! Int32)
let uuidArgs = list[1] as! String
let valueArgs: FlutterStandardTypedData? = nilOrValue(list[2])
let characteristicsArgs = list[2] as! [MyGattCharacteristicArgs?]
return MyGattDescriptorArgs(
return MyGattServiceArgs(
hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs,
valueArgs: valueArgs
characteristicsArgs: characteristicsArgs
)
}
func toList() -> [Any?] {
return [
hashCodeArgs,
uuidArgs,
valueArgs,
characteristicsArgs,
]
}
}
@ -280,12 +262,10 @@ private class MyCentralManagerHostApiCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? {
switch type {
case 128:
return MyCentralManagerArgs.fromList(self.readValue() as! [Any?])
case 129:
return MyGattCharacteristicArgs.fromList(self.readValue() as! [Any?])
case 130:
case 129:
return MyGattDescriptorArgs.fromList(self.readValue() as! [Any?])
case 131:
case 130:
return MyGattServiceArgs.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
@ -295,17 +275,14 @@ private class MyCentralManagerHostApiCodecReader: FlutterStandardReader {
private class MyCentralManagerHostApiCodecWriter: FlutterStandardWriter {
override func writeValue(_ value: Any) {
if let value = value as? MyCentralManagerArgs {
if let value = value as? MyGattCharacteristicArgs {
super.writeByte(128)
super.writeValue(value.toList())
} else if let value = value as? MyGattCharacteristicArgs {
} else if let value = value as? MyGattDescriptorArgs {
super.writeByte(129)
super.writeValue(value.toList())
} else if let value = value as? MyGattDescriptorArgs {
super.writeByte(130)
super.writeValue(value.toList())
} else if let value = value as? MyGattServiceArgs {
super.writeByte(131)
super.writeByte(130)
super.writeValue(value.toList())
} else {
super.writeValue(value)
@ -329,19 +306,21 @@ class MyCentralManagerHostApiCodec: FlutterStandardMessageCodec {
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol MyCentralManagerHostApi {
func setUp(completion: @escaping (Result<MyCentralManagerArgs, Error>) -> Void)
func setUp() throws
func startDiscovery() throws
func stopDiscovery() throws
func connect(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void)
func disconnect(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void)
func getMaximumWriteLength(peripheralHashCodeArgs: Int64, typeNumberArgs: Int64) throws -> Int64
func readRSSI(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Int64, Error>) -> Void)
func discoverGATT(peripheralHashCodeArgs: Int64, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void)
func readCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void)
func writeCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void)
func notifyCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, stateArgs: Bool, completion: @escaping (Result<Void, Error>) -> Void)
func readDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void)
func writeDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void)
func connect(uuidArgs: String, completion: @escaping (Result<Void, Error>) -> Void)
func disconnect(uuidArgs: String, completion: @escaping (Result<Void, Error>) -> Void)
func getMaximumWriteValueLength(uuidArgs: String, typeNumberArgs: Int64) throws -> Int64
func readRSSI(uuidArgs: String, completion: @escaping (Result<Int64, Error>) -> Void)
func discoverServices(uuidArgs: String, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void)
func discoverCharacteristics(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattCharacteristicArgs], Error>) -> Void)
func discoverDescriptors(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattDescriptorArgs], Error>) -> Void)
func readCharacteristic(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void)
func writeCharacteristic(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, 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`.
@ -353,13 +332,11 @@ class MyCentralManagerHostApiSetup {
let setUpChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setUp", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
setUpChannel.setMessageHandler { _, reply in
api.setUp() { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
do {
try api.setUp()
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
}
}
} else {
@ -395,8 +372,8 @@ class MyCentralManagerHostApiSetup {
if let api = api {
connectChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
api.connect(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in
let uuidArgsArg = args[0] as! String
api.connect(uuidArgs: uuidArgsArg) { result in
switch result {
case .success:
reply(wrapResult(nil))
@ -412,8 +389,8 @@ class MyCentralManagerHostApiSetup {
if let api = api {
disconnectChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
api.disconnect(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in
let uuidArgsArg = args[0] as! String
api.disconnect(uuidArgs: uuidArgsArg) { result in
switch result {
case .success:
reply(wrapResult(nil))
@ -425,28 +402,28 @@ class MyCentralManagerHostApiSetup {
} else {
disconnectChannel.setMessageHandler(nil)
}
let getMaximumWriteLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.getMaximumWriteLength", binaryMessenger: binaryMessenger, codec: codec)
let getMaximumWriteValueLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.getMaximumWriteValueLength", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
getMaximumWriteLengthChannel.setMessageHandler { message, reply in
getMaximumWriteValueLengthChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let uuidArgsArg = args[0] as! String
let typeNumberArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
do {
let result = try api.getMaximumWriteLength(peripheralHashCodeArgs: peripheralHashCodeArgsArg, typeNumberArgs: typeNumberArgsArg)
let result = try api.getMaximumWriteValueLength(uuidArgs: uuidArgsArg, typeNumberArgs: typeNumberArgsArg)
reply(wrapResult(result))
} catch {
reply(wrapError(error))
}
}
} else {
getMaximumWriteLengthChannel.setMessageHandler(nil)
getMaximumWriteValueLengthChannel.setMessageHandler(nil)
}
let readRSSIChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readRSSI", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
readRSSIChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
api.readRSSI(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in
let uuidArgsArg = args[0] as! String
api.readRSSI(uuidArgs: uuidArgsArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
@ -458,12 +435,12 @@ class MyCentralManagerHostApiSetup {
} else {
readRSSIChannel.setMessageHandler(nil)
}
let discoverGATTChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverGATT", binaryMessenger: binaryMessenger, codec: codec)
let discoverServicesChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverServices", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
discoverGATTChannel.setMessageHandler { message, reply in
discoverServicesChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
api.discoverGATT(peripheralHashCodeArgs: peripheralHashCodeArgsArg) { result in
let uuidArgsArg = args[0] as! String
api.discoverServices(uuidArgs: uuidArgsArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
@ -473,15 +450,51 @@ class MyCentralManagerHostApiSetup {
}
}
} else {
discoverGATTChannel.setMessageHandler(nil)
discoverServicesChannel.setMessageHandler(nil)
}
let discoverCharacteristicsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverCharacteristics", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
discoverCharacteristicsChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let uuidArgsArg = args[0] as! String
let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
api.discoverCharacteristics(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
discoverCharacteristicsChannel.setMessageHandler(nil)
}
let discoverDescriptorsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.discoverDescriptors", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
discoverDescriptorsChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let uuidArgsArg = args[0] as! String
let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
api.discoverDescriptors(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
discoverDescriptorsChannel.setMessageHandler(nil)
}
let readCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readCharacteristic", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
readCharacteristicChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
api.readCharacteristic(peripheralHashCodeArgs: peripheralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg) { result in
let uuidArgsArg = args[0] as! String
let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
api.readCharacteristic(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
@ -497,11 +510,11 @@ class MyCentralManagerHostApiSetup {
if let api = api {
writeCharacteristicChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let uuidArgsArg = args[0] as! String
let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let valueArgsArg = args[2] as! FlutterStandardTypedData
let typeNumberArgsArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32)
api.writeCharacteristic(peripheralHashCodeArgs: peripheralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, valueArgs: valueArgsArg, typeNumberArgs: typeNumberArgsArg) { result in
api.writeCharacteristic(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg, typeNumberArgs: typeNumberArgsArg) { result in
switch result {
case .success:
reply(wrapResult(nil))
@ -513,14 +526,14 @@ class MyCentralManagerHostApiSetup {
} else {
writeCharacteristicChannel.setMessageHandler(nil)
}
let notifyCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.notifyCharacteristic", binaryMessenger: binaryMessenger, codec: codec)
let setCharacteristicNotifyStateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.setCharacteristicNotifyState", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
notifyCharacteristicChannel.setMessageHandler { message, reply in
setCharacteristicNotifyStateChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let uuidArgsArg = args[0] as! String
let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let stateArgsArg = args[2] as! Bool
api.notifyCharacteristic(peripheralHashCodeArgs: peripheralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, stateArgs: stateArgsArg) { result in
api.setCharacteristicNotifyState(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, stateArgs: stateArgsArg) { result in
switch result {
case .success:
reply(wrapResult(nil))
@ -530,15 +543,15 @@ class MyCentralManagerHostApiSetup {
}
}
} else {
notifyCharacteristicChannel.setMessageHandler(nil)
setCharacteristicNotifyStateChannel.setMessageHandler(nil)
}
let readDescriptorChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerHostApi.readDescriptor", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
readDescriptorChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let descriptorHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
api.readDescriptor(peripheralHashCodeArgs: peripheralHashCodeArgsArg, descriptorHashCodeArgs: descriptorHashCodeArgsArg) { result in
let uuidArgsArg = args[0] as! String
let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
api.readDescriptor(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
@ -554,10 +567,10 @@ class MyCentralManagerHostApiSetup {
if let api = api {
writeDescriptorChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let peripheralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let descriptorHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let uuidArgsArg = args[0] as! String
let hashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let valueArgsArg = args[2] as! FlutterStandardTypedData
api.writeDescriptor(peripheralHashCodeArgs: peripheralHashCodeArgsArg, descriptorHashCodeArgs: descriptorHashCodeArgsArg, valueArgs: valueArgsArg) { result in
api.writeDescriptor(uuidArgs: uuidArgsArg, hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg) { result in
switch result {
case .success:
reply(wrapResult(nil))
@ -577,12 +590,8 @@ private class MyCentralManagerFlutterApiCodecReader: FlutterStandardReader {
case 128:
return MyAdvertisementArgs.fromList(self.readValue() as! [Any?])
case 129:
return MyGattCharacteristicArgs.fromList(self.readValue() as! [Any?])
case 130:
return MyGattDescriptorArgs.fromList(self.readValue() as! [Any?])
case 131:
return MyManufacturerSpecificDataArgs.fromList(self.readValue() as! [Any?])
case 132:
case 130:
return MyPeripheralArgs.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
@ -595,17 +604,11 @@ private class MyCentralManagerFlutterApiCodecWriter: FlutterStandardWriter {
if let value = value as? MyAdvertisementArgs {
super.writeByte(128)
super.writeValue(value.toList())
} else if let value = value as? MyGattCharacteristicArgs {
} else if let value = value as? MyManufacturerSpecificDataArgs {
super.writeByte(129)
super.writeValue(value.toList())
} else if let value = value as? MyGattDescriptorArgs {
super.writeByte(130)
super.writeValue(value.toList())
} else if let value = value as? MyManufacturerSpecificDataArgs {
super.writeByte(131)
super.writeValue(value.toList())
} else if let value = value as? MyPeripheralArgs {
super.writeByte(132)
super.writeByte(130)
super.writeValue(value.toList())
} else {
super.writeValue(value)
@ -629,10 +632,10 @@ class MyCentralManagerFlutterApiCodec: FlutterStandardMessageCodec {
/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.
protocol MyCentralManagerFlutterApiProtocol {
func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, 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 onCharacteristicValueChanged(characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, valueArgs valueArgsArg: FlutterStandardTypedData, 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 onConnectionStateChanged(uuidArgs uuidArgsArg: String, stateArgs stateArgsArg: Bool, 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 {
private let binaryMessenger: FlutterBinaryMessenger
@ -642,28 +645,76 @@ class MyCentralManagerFlutterApi: MyCentralManagerFlutterApiProtocol {
var codec: FlutterStandardMessageCodec {
return MyCentralManagerFlutterApiCodec.shared
}
func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onStateChanged", binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([stateNumberArgsArg] as [Any?]) { _ in
completion(.success(Void()))
func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onStateChanged"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([stateNumberArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void()))
}
}
}
func onDiscovered(peripheralArgs peripheralArgsArg: MyPeripheralArgs, rssiArgs rssiArgsArg: Int64, advertisementArgs advertisementArgsArg: MyAdvertisementArgs, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onDiscovered", binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([peripheralArgsArg, rssiArgsArg, advertisementArgsArg] as [Any?]) { _ in
completion(.success(Void()))
func onDiscovered(peripheralArgs peripheralArgsArg: MyPeripheralArgs, rssiArgs rssiArgsArg: Int64, advertisementArgs advertisementArgsArg: MyAdvertisementArgs, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onDiscovered"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([peripheralArgsArg, rssiArgsArg, advertisementArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void()))
}
}
}
func onPeripheralStateChanged(peripheralArgs peripheralArgsArg: MyPeripheralArgs, stateArgs stateArgsArg: Bool, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onPeripheralStateChanged", binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([peripheralArgsArg, stateArgsArg] as [Any?]) { _ in
completion(.success(Void()))
func onConnectionStateChanged(uuidArgs uuidArgsArg: String, stateArgs stateArgsArg: Bool, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onConnectionStateChanged"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([uuidArgsArg, stateArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void()))
}
}
}
func onCharacteristicValueChanged(characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicValueChanged", binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([characteristicArgsArg, valueArgsArg] as [Any?]) { _ in
completion(.success(Void()))
func onCharacteristicNotified(uuidArgs uuidArgsArg: String, hashCodeArgs hashCodeArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyCentralManagerFlutterApi.onCharacteristicNotified"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([uuidArgsArg, hashCodeArgsArg, valueArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void()))
}
}
}
}
@ -680,8 +731,6 @@ private class MyPeripheralManagerHostApiCodecReader: FlutterStandardReader {
return MyGattServiceArgs.fromList(self.readValue() as! [Any?])
case 132:
return MyManufacturerSpecificDataArgs.fromList(self.readValue() as! [Any?])
case 133:
return MyPeripheralManagerArgs.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
}
@ -705,9 +754,6 @@ private class MyPeripheralManagerHostApiCodecWriter: FlutterStandardWriter {
} else if let value = value as? MyManufacturerSpecificDataArgs {
super.writeByte(132)
super.writeValue(value.toList())
} else if let value = value as? MyPeripheralManagerArgs {
super.writeByte(133)
super.writeValue(value.toList())
} else {
super.writeValue(value)
}
@ -730,16 +776,15 @@ class MyPeripheralManagerHostApiCodec: FlutterStandardMessageCodec {
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol MyPeripheralManagerHostApi {
func setUp(completion: @escaping (Result<MyPeripheralManagerArgs, Error>) -> Void)
func setUp() throws
func addService(serviceArgs: MyGattServiceArgs, completion: @escaping (Result<Void, Error>) -> Void)
func removeService(serviceHashCodeArgs: Int64) throws
func removeService(hashCodeArgs: Int64) throws
func clearServices() throws
func startAdvertising(advertisementArgs: MyAdvertisementArgs, completion: @escaping (Result<Void, Error>) -> Void)
func stopAdvertising() throws
func getMaximumWriteLength(centralHashCodeArgs: Int64) throws -> Int64
func sendReadCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool, valueArgs: FlutterStandardTypedData) throws
func sendWriteCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool) throws
func notifyCharacteristicValueChanged(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void)
func getMaximumUpdateValueLength(uuidArgs: String) throws -> Int64
func respond(idArgs: Int64, errorNumberArgs: Int64, valueArgs: FlutterStandardTypedData?) throws
func updateCharacteristic(hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, uuidsArgs: [String]?, completion: @escaping (Result<Void, Error>) -> Void)
}
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
@ -751,13 +796,11 @@ class MyPeripheralManagerHostApiSetup {
let setUpChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.setUp", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
setUpChannel.setMessageHandler { _, reply in
api.setUp() { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
do {
try api.setUp()
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
}
}
} else {
@ -784,9 +827,9 @@ class MyPeripheralManagerHostApiSetup {
if let api = api {
removeServiceChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let serviceHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let hashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
do {
try api.removeService(serviceHashCodeArgs: serviceHashCodeArgsArg)
try api.removeService(hashCodeArgs: hashCodeArgsArg)
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
@ -838,68 +881,46 @@ class MyPeripheralManagerHostApiSetup {
} else {
stopAdvertisingChannel.setMessageHandler(nil)
}
let getMaximumWriteLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.getMaximumWriteLength", binaryMessenger: binaryMessenger, codec: codec)
let getMaximumUpdateValueLengthChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.getMaximumUpdateValueLength", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
getMaximumWriteLengthChannel.setMessageHandler { message, reply in
getMaximumUpdateValueLengthChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let uuidArgsArg = args[0] as! String
do {
let result = try api.getMaximumWriteLength(centralHashCodeArgs: centralHashCodeArgsArg)
let result = try api.getMaximumUpdateValueLength(uuidArgs: uuidArgsArg)
reply(wrapResult(result))
} catch {
reply(wrapError(error))
}
}
} else {
getMaximumWriteLengthChannel.setMessageHandler(nil)
getMaximumUpdateValueLengthChannel.setMessageHandler(nil)
}
let sendReadCharacteristicReplyChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.sendReadCharacteristicReply", binaryMessenger: binaryMessenger, codec: codec)
let respondChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.respond", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
sendReadCharacteristicReplyChannel.setMessageHandler { message, reply in
respondChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let idArgsArg = args[2] is Int64 ? args[2] as! Int64 : Int64(args[2] as! Int32)
let offsetArgsArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32)
let statusArgsArg = args[4] as! Bool
let valueArgsArg = args[5] as! FlutterStandardTypedData
let idArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let errorNumberArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let valueArgsArg: FlutterStandardTypedData? = nilOrValue(args[2])
do {
try api.sendReadCharacteristicReply(centralHashCodeArgs: centralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, idArgs: idArgsArg, offsetArgs: offsetArgsArg, statusArgs: statusArgsArg, valueArgs: valueArgsArg)
try api.respond(idArgs: idArgsArg, errorNumberArgs: errorNumberArgsArg, valueArgs: valueArgsArg)
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
}
}
} else {
sendReadCharacteristicReplyChannel.setMessageHandler(nil)
respondChannel.setMessageHandler(nil)
}
let sendWriteCharacteristicReplyChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.sendWriteCharacteristicReply", binaryMessenger: binaryMessenger, codec: codec)
let updateCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.updateCharacteristic", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
sendWriteCharacteristicReplyChannel.setMessageHandler { message, reply in
updateCharacteristicChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let idArgsArg = args[2] is Int64 ? args[2] as! Int64 : Int64(args[2] as! Int32)
let offsetArgsArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32)
let statusArgsArg = args[4] as! Bool
do {
try api.sendWriteCharacteristicReply(centralHashCodeArgs: centralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, idArgs: idArgsArg, offsetArgs: offsetArgsArg, statusArgs: statusArgsArg)
reply(wrapResult(nil))
} catch {
reply(wrapError(error))
}
}
} else {
sendWriteCharacteristicReplyChannel.setMessageHandler(nil)
}
let notifyCharacteristicValueChangedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerHostApi.notifyCharacteristicValueChanged", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
notifyCharacteristicValueChangedChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let centralHashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let characteristicHashCodeArgsArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let valueArgsArg = args[2] as! FlutterStandardTypedData
api.notifyCharacteristicValueChanged(centralHashCodeArgs: centralHashCodeArgsArg, characteristicHashCodeArgs: characteristicHashCodeArgsArg, valueArgs: valueArgsArg) { result in
let hashCodeArgsArg = args[0] is Int64 ? args[0] as! Int64 : Int64(args[0] as! Int32)
let valueArgsArg = args[1] as! FlutterStandardTypedData
let uuidsArgsArg: [String]? = nilOrValue(args[2])
api.updateCharacteristic(hashCodeArgs: hashCodeArgsArg, valueArgs: valueArgsArg, uuidsArgs: uuidsArgsArg) { result in
switch result {
case .success:
reply(wrapResult(nil))
@ -909,7 +930,7 @@ class MyPeripheralManagerHostApiSetup {
}
}
} else {
notifyCharacteristicValueChangedChannel.setMessageHandler(nil)
updateCharacteristicChannel.setMessageHandler(nil)
}
}
}
@ -918,10 +939,6 @@ private class MyPeripheralManagerFlutterApiCodecReader: FlutterStandardReader {
switch type {
case 128:
return MyCentralArgs.fromList(self.readValue() as! [Any?])
case 129:
return MyGattCharacteristicArgs.fromList(self.readValue() as! [Any?])
case 130:
return MyGattDescriptorArgs.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
}
@ -933,12 +950,6 @@ private class MyPeripheralManagerFlutterApiCodecWriter: FlutterStandardWriter {
if let value = value as? MyCentralArgs {
super.writeByte(128)
super.writeValue(value.toList())
} else if let value = value as? MyGattCharacteristicArgs {
super.writeByte(129)
super.writeValue(value.toList())
} else if let value = value as? MyGattDescriptorArgs {
super.writeByte(130)
super.writeValue(value.toList())
} else {
super.writeValue(value)
}
@ -961,10 +972,10 @@ class MyPeripheralManagerFlutterApiCodec: FlutterStandardMessageCodec {
/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.
protocol MyPeripheralManagerFlutterApiProtocol {
func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void)
func onReadCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, 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 onNotifyCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, stateArgs stateArgsArg: Bool, completion: @escaping (Result<Void, FlutterError>) -> Void)
func onStateChanged(stateNumberArgs stateNumberArgsArg: 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 onCharacteristicWriteRequest(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, valueArgs valueArgsArg: FlutterStandardTypedData, 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 {
private let binaryMessenger: FlutterBinaryMessenger
@ -974,28 +985,76 @@ class MyPeripheralManagerFlutterApi: MyPeripheralManagerFlutterApiProtocol {
var codec: FlutterStandardMessageCodec {
return MyPeripheralManagerFlutterApiCodec.shared
}
func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onStateChanged", binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([stateNumberArgsArg] as [Any?]) { _ in
completion(.success(Void()))
func onStateChanged(stateNumberArgs stateNumberArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onStateChanged"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([stateNumberArgsArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName:channelName)))
return
}
if (listResponse.count > 1) {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(FlutterError(code: code, message: message, details: details)));
} else {
completion(.success(Void()))
}
}
}
func onReadCharacteristicCommandReceived(centralArgs centralArgsArg: MyCentralArgs, characteristicArgs characteristicArgsArg: MyGattCharacteristicArgs, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, completion: @escaping (Result<Void, FlutterError>) -> Void) {
let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bluetooth_low_energy_darwin.MyPeripheralManagerFlutterApi.onReadCharacteristicCommandReceived", binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([centralArgsArg, characteristicArgsArg, idArgsArg, offsetArgsArg] as [Any?]) { _ in
completion(.success(Void()))
func onCharacteristicReadRequest(centralArgs centralArgsArg: MyCentralArgs, hashCodeArgs hashCodeArgsArg: Int64, idArgs idArgsArg: Int64, offsetArgs offsetArgsArg: Int64, completion: @escaping (Result<Void, 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 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 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 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()))
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.")
#endif
// This extension of Error is required to do use FlutterError in any Swift code.
extension FlutterError: Error {}
extension CBManagerState {
func toArgs() -> MyBluetoothLowEnergyStateArgs {
switch self {
case .unauthorized:
return .unauthorized
case .poweredOff:
return .poweredOff
case .poweredOn:
return .poweredOn
default:
return .unsupported
// ToObj
extension [MyGattCharacteristicPropertyArgs] {
func toProperties() -> CBCharacteristicProperties {
var properties: CBCharacteristicProperties = []
for args in self {
switch args {
case .read:
properties.insert(.read)
case .write:
properties.insert(.write)
case .writeWithoutResponse:
properties.insert(.writeWithoutResponse)
case .notify:
properties.insert(.notify)
case .indicate:
properties.insert(.indicate)
}
}
}
}
extension CBPeer {
var uuidArgs: String { identifier.uuidString }
}
extension CBCentral {
func toArgs() -> MyCentralArgs {
let hashCodeArgs = Int64(hash)
return MyCentralArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs)
}
}
extension CBPeripheral {
func toArgs() -> MyPeripheralArgs {
let hashCodeArgs = Int64(hash)
return MyPeripheralArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs)
}
}
extension CBAttribute {
var uuidArgs: String { uuid.uuidString }
}
extension CBService {
func toArgs(_ characteristicsArgs: [MyGattCharacteristicArgs]) -> MyGattServiceArgs {
let hashCodeArgs = Int64(hash)
return MyGattServiceArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, characteristicsArgs: characteristicsArgs)
}
}
extension CBCharacteristic {
func toArgs(_ descriptorsArgs: [MyGattDescriptorArgs]) -> MyGattCharacteristicArgs {
let hashCodeArgs = Int64(hash)
return MyGattCharacteristicArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, propertyNumbersArgs: propertyNumbersArgs, descriptorsArgs: descriptorsArgs)
return properties
}
var propertyNumbersArgs: [Int64] {
var propertiesArgs = [MyGattCharacteristicPropertyArgs]()
let properties = self.properties
if properties.contains(.read) {
propertiesArgs.append(.read)
func toPermissions() -> CBAttributePermissions {
var permissions: CBAttributePermissions = []
for args in self {
switch args {
case .read:
permissions.insert(.readable)
case .write, .writeWithoutResponse:
permissions.insert(.writeable)
default:
continue
}
}
if properties.contains(.write) {
propertiesArgs.append(.write)
}
if properties.contains(.writeWithoutResponse) {
propertiesArgs.append(.writeWithoutResponse)
}
if properties.contains(.notify) {
propertiesArgs.append(.notify)
}
if properties.contains(.indicate) {
propertiesArgs.append(.indicate)
}
return propertiesArgs.map { args in Int64(args.rawValue) }
}
}
extension CBDescriptor {
func toArgs() -> MyGattDescriptorArgs {
let hashCodeArgs = Int64(hash)
return MyGattDescriptorArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs)
return permissions
}
}
@ -109,21 +64,151 @@ extension MyGattCharacteristicWriteTypeArgs {
}
}
extension String {
var data: Data { data(using: String.Encoding.utf8)! }
}
extension NSNumber {
var data: Data {
var source = self
return Data(bytes: &source, count: MemoryLayout<NSNumber>.size)
extension MyGattErrorArgs {
func toError() -> CBATTError.Code {
switch self {
case .success:
return .success
case .invalidHandle:
return .invalidHandle
case .readNotPermitted:
return .readNotPermitted
case .writeNotPermitted:
return .writeNotPermitted
case .invalidPDU:
return .invalidPdu
case .insufficientAuthentication:
return .insufficientAuthentication
case .requestNotSupported:
return .requestNotSupported
case .invalidOffset:
return .invalidOffset
case .insufficientAuthorization:
return .insufficientAuthorization
case .prepareQueueFull:
return .prepareQueueFull
case .attributeNotFound:
return .attributeNotFound
case .attributeNotLong:
return .attributeNotLong
case .insufficientEncryptionKeySize:
return .insufficientEncryptionKeySize
case .invalidAttributeValueLength:
return .invalidAttributeValueLength
case .unlikelyError:
return .unlikelyError
case .insufficientEncryption:
return .insufficientEncryption
case .unsupportedGroupType:
return .unsupportedGroupType
case .insufficientResources:
return .insufficientResources
}
}
}
extension UInt16 {
var data: Data {
var source = self
return Data(bytes: &source, count: MemoryLayout<UInt16>.size)
extension MyAdvertisementArgs {
func toAdvertisement() -> [String : Any] {
// CoreBluetooth only support `CBAdvertisementDataLocalNameKey` and `CBAdvertisementDataServiceUUIDsKey`
// see https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising
var advertisement = [String: Any]()
if nameArgs != nil {
let name = nameArgs!
advertisement[CBAdvertisementDataLocalNameKey] = name
}
if serviceUUIDsArgs.count > 0 {
var serviceUUIDs = [CBUUID]()
for args in serviceUUIDsArgs {
guard let uuidArgs = args else {
continue
}
let uuid = uuidArgs.toCBUUID()
serviceUUIDs.append(uuid)
}
advertisement[CBAdvertisementDataServiceUUIDsKey] = serviceUUIDs
}
return advertisement
}
}
extension MyGattDescriptorArgs {
func toDescriptor() -> CBMutableDescriptor {
let type = uuidArgs.toCBUUID()
let value = valueArgs?.data
return CBMutableDescriptor(type: type, value: value)
}
}
extension MyGattCharacteristicArgs {
func toCharacteristic() -> CBMutableCharacteristic {
let type = uuidArgs.toCBUUID()
var propertiesArgs = [MyGattCharacteristicPropertyArgs]()
for args in propertyNumbersArgs {
guard let propertyNumberArgs = args else {
continue
}
let propertyNumber = propertyNumberArgs.toInt()
guard let propertyArgs = MyGattCharacteristicPropertyArgs(rawValue: propertyNumber) else {
continue
}
propertiesArgs.append(propertyArgs)
}
let properties = propertiesArgs.toProperties()
let permissions = propertiesArgs.toPermissions()
return CBMutableCharacteristic(type: type, properties: properties, value: nil, permissions: permissions)
}
}
extension MyGattServiceArgs {
func toService() -> CBMutableService {
let type = uuidArgs.toCBUUID()
let primary = true
return CBMutableService(type: type, primary: primary)
}
}
extension Int64 {
func toInt() -> Int {
return Int(self)
}
}
extension String {
func toCBUUID() -> CBUUID {
return CBUUID(string: self)
}
}
// ToArgs
extension CBManagerState {
func toArgs() -> MyBluetoothLowEnergyStateArgs {
switch self {
case .resetting:
return .resetting
case .unsupported:
return .unsupported
case .unauthorized:
return .unauthorized
case .poweredOff:
return .poweredOff
case .poweredOn:
return .poweredOn
default:
return .unknown
}
}
}
extension Data {
func toManufacturerSpecificDataArgs() -> MyManufacturerSpecificDataArgs? {
if count > 2 {
let idArgs = Int64(self[0]) | (Int64(self[1]) << 8)
let data = self[2...count - 1]
let dataArgs = FlutterStandardTypedData(bytes: data)
return MyManufacturerSpecificDataArgs(idArgs: idArgs, dataArgs: dataArgs)
} else {
return nil
}
}
}
@ -145,160 +230,105 @@ extension [String: Any] {
}
}
extension Data {
func toManufacturerSpecificDataArgs() -> MyManufacturerSpecificDataArgs? {
if count > 2 {
let idArgs = Int64(self[0]) | (Int64(self[1]) << 8)
let data = self[2...count - 1]
let dataArgs = FlutterStandardTypedData(bytes: data)
return MyManufacturerSpecificDataArgs(idArgs: idArgs, dataArgs: dataArgs)
} else {
return nil
}
extension CBCentral {
func toArgs() -> MyCentralArgs {
let uuidArgs = identifier.toArgs()
return MyCentralArgs(uuidArgs: uuidArgs)
}
}
extension MyAdvertisementArgs {
func toAdvertisement() throws -> [String : Any] {
// CoreBluetooth only support `CBAdvertisementDataLocalNameKey` and `CBAdvertisementDataServiceUUIDsKey`, see https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising
var advertisement = [String: Any]()
if nameArgs != nil {
let name = nameArgs!
advertisement[CBAdvertisementDataLocalNameKey] = name
}
if serviceUUIDsArgs.count > 0 {
var serviceUUIDs = [CBUUID]()
for args in serviceUUIDsArgs {
guard let uuidArgs = args else {
throw MyError.illegalArgument
}
let uuid = CBUUID(string: uuidArgs)
serviceUUIDs.append(uuid)
}
advertisement[CBAdvertisementDataServiceUUIDsKey] = serviceUUIDs
}
// if serviceDataArgs.count > 0 {
// var serviceData = [CBUUID: Data]()
// for args in serviceDataArgs {
// guard let uuidArgs = args.key else {
// throw MyError.illegalArgument
// }
// guard let dataArgs = args.value else {
// throw MyError.illegalArgument
// }
// let uuid = CBUUID(string: uuidArgs)
// let data = dataArgs.data
// serviceData[uuid] = data
// }
// advertisement[CBAdvertisementDataServiceDataKey] = serviceData
// }
// if manufacturerSpecificDataArgs != nil {
// let manufacturerSpecificData = manufacturerSpecificDataArgs!.toManufacturerSpecificData()
// advertisement[CBAdvertisementDataManufacturerDataKey] = manufacturerSpecificData
// }
return advertisement
extension CBPeripheral {
func toArgs() -> MyPeripheralArgs {
let uuidArgs = identifier.toArgs()
return MyPeripheralArgs(uuidArgs: uuidArgs)
}
}
//extension MyManufacturerSpecificDataArgs {
// func toManufacturerSpecificData() -> Data {
// let id = UInt16(idArgs).data
// let data = dataArgs.data
// return id + data
// }
//}
extension MyGattServiceArgs {
func toService() -> CBMutableService {
let type = CBUUID(string: uuidArgs)
let primary = true
return CBMutableService(type: type, primary: primary)
extension CBDescriptor {
func toArgs() -> MyGattDescriptorArgs {
let hashCodeArgs = hash.toInt64()
let uuidArgs = uuid.toArgs()
return MyGattDescriptorArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs)
}
}
extension MyGattCharacteristicArgs {
func toCharacteristic() -> CBMutableCharacteristic {
let type = CBUUID(string: uuidArgs)
return CBMutableCharacteristic(type: type, properties: properties, value: nil, permissions: permissions)
}
var properties: CBCharacteristicProperties {
var properties: CBCharacteristicProperties = []
for args in propertyNumbersArgs {
guard let rawArgs = args else {
continue
}
let rawValue = Int(rawArgs)
let property = MyGattCharacteristicPropertyArgs(rawValue: rawValue)
switch property {
case .read:
properties.insert(.read)
case .write:
properties.insert(.write)
case .writeWithoutResponse:
properties.insert(.writeWithoutResponse)
case .notify:
properties.insert(.notify)
case .indicate:
properties.insert(.indicate)
default:
continue
}
}
return properties
}
var permissions: CBAttributePermissions {
var permissions: CBAttributePermissions = []
for args in propertyNumbersArgs {
guard let rawArgs = args else {
continue
}
let rawValue = Int(rawArgs)
let property = MyGattCharacteristicPropertyArgs(rawValue: rawValue)
switch property {
case .read:
permissions.insert(.readable)
case .write, .writeWithoutResponse:
permissions.insert(.writeable)
default:
continue
}
}
return permissions
extension CBCharacteristic {
func toArgs() -> MyGattCharacteristicArgs {
let hashCodeArgs = hash.toInt64()
let uuidArgs = uuid.toArgs()
let propertyNumbersArgs = properties.toArgs().map { args in args.rawValue.toInt64() }
let descriptorsArgs = descriptors?.map { descriptor in descriptor.toArgs() } ?? []
return MyGattCharacteristicArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, propertyNumbersArgs: propertyNumbersArgs, descriptorsArgs: descriptorsArgs)
}
}
extension MyGattDescriptorArgs {
func toDescriptor() -> CBMutableDescriptor {
let type = CBUUID(string: uuidArgs)
let value = valueArgs?.data
return CBMutableDescriptor(type: type, value: value)
extension CBCharacteristicProperties {
func toArgs() -> [MyGattCharacteristicPropertyArgs] {
var args = [MyGattCharacteristicPropertyArgs]()
if contains(.read) {
args.append(.read)
}
if contains(.write) {
args.append(.write)
}
if contains(.writeWithoutResponse) {
args.append(.writeWithoutResponse)
}
if contains(.notify) {
args.append(.notify)
}
if contains(.indicate) {
args.append(.indicate)
}
return args
}
}
extension CBService {
func toArgs() -> MyGattServiceArgs {
let hashCodeArgs = hash.toInt64()
let uuidArgs = uuid.toArgs()
let characteristicsArgs = characteristics?.map { characteristic in characteristic.toArgs() } ?? []
return MyGattServiceArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, characteristicsArgs: characteristicsArgs)
}
}
extension Int {
func coerceIn(_ minimum: Int, _ maximum: Int) throws -> Int {
if minimum > maximum {
throw MyError.illegalArgument
}
if self < minimum {
return minimum
}
if self > maximum {
return maximum
}
return self
func toInt64() -> Int64 {
return Int64(self)
}
}
extension Dictionary {
mutating func getOrPut(_ key: Key, _ defaultValue: () -> Value) -> Value {
guard let value = self[key] else {
let value1 = defaultValue()
self[key] = value1
return value1
}
return value
extension UUID {
func toArgs() -> String {
return uuidString.lowercased()
}
}
extension CBUUID {
func toArgs() -> String {
return uuidString.lowercased()
}
}
// This extension of Error is required to do use FlutterError in any Swift code.
extension FlutterError: Error {}
extension String {
var data: Data { data(using: String.Encoding.utf8)! }
}
extension NSNumber {
var data: Data {
var source = self
// TODO: resolve warning: Forming 'UnsafeRawPointer' to a variable of type 'NSNumber'; this is likely incorrect because 'NSNumber' may contain an object reference.
return Data(bytes: &source, count: MemoryLayout<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
class MyCentralManager: MyCentralManagerHostApi {
init(_ binaryMessenger: FlutterBinaryMessenger) {
self.binaryMessenger = binaryMessenger
private let _api: MyCentralManagerFlutterApi
private let _centralManager: CBCentralManager
private lazy var _centralManagerDelegate = MyCentralManagerDelegate(centralManager: self)
private lazy var _peripheralDelegate = MyPeripheralDelegate(centralManager: self)
private var _peripherals: [String: CBPeripheral]
private var _services: [String: [Int64: CBService]]
private var _characteristics: [String: [Int64: CBCharacteristic]]
private var _descriptors: [String: [Int64: CBDescriptor]]
private var _connectCompletions: [String: (Result<Void, 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
private let centralManager = CBCentralManager()
private lazy var api = MyCentralManagerFlutterApi(binaryMessenger: binaryMessenger)
private lazy var centralManagerDelegate = MyCentralManagerDelegate(self)
private lazy var peripheralDelegate = MyPeripheralDelegate(self)
private var peripherals = [Int64: CBPeripheral]()
private var services = [Int64: CBService]()
private var characteristics = [Int64: CBCharacteristic]()
private var descriptors = [Int64: CBDescriptor]()
private var peripheralsArgs = [Int: MyPeripheralArgs]()
private var servicesArgsOfPeripheralsArgs = [Int64: [MyGattServiceArgs]]()
private var servicesArgs = [Int: MyGattServiceArgs]()
private var characteristicsArgs = [Int: MyGattCharacteristicArgs]()
private var descriptorsArgs = [Int: MyGattDescriptorArgs]()
private var setUpCompletion: ((Result<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()
centralManager.delegate = centralManagerDelegate
if centralManager.state == .unknown {
setUpCompletion = completion
} else {
let stateArgs = centralManager.state.toArgs()
let stateNumberArgs = Int64(stateArgs.rawValue)
let args = MyCentralManagerArgs(stateNumberArgs: stateNumberArgs)
completion(.success(args))
}
} catch {
completion(.failure(error))
func setUp() throws {
_clearState()
if _centralManager.delegate == nil {
_centralManager.delegate = _centralManagerDelegate
}
}
func tearDown() throws {
if(centralManager.isScanning) {
centralManager.stopScan()
}
for peripheral in peripherals.values {
if peripheral.state != .disconnected {
centralManager.cancelPeripheralConnection(peripheral)
}
}
peripherals.removeAll()
services.removeAll()
characteristics.removeAll()
descriptors.removeAll()
peripheralsArgs.removeAll()
servicesArgsOfPeripheralsArgs.removeAll()
servicesArgs.removeAll()
characteristicsArgs.removeAll()
descriptorsArgs.removeAll()
setUpCompletion = nil
connectCompletions.removeAll()
disconnectCompletions.removeAll()
readRssiCompletions.removeAll()
discoverGattCompletions.removeAll()
unfinishedServices.removeAll()
unfinishedCharacteristics.removeAll()
readCharacteristicCompletions.removeAll()
writeCharacteristicCompletions.removeAll()
notifyCharacteristicCompletions.removeAll()
readDescriptorCompletions.removeAll()
writeDescriptorCompletions.removeAll()
_onStateChanged()
}
func startDiscovery() throws {
let options = [CBCentralManagerScanOptionAllowDuplicatesKey: true]
centralManager.scanForPeripherals(withServices: nil, options: options)
_centralManager.scanForPeripherals(withServices: nil, options: options)
}
func stopDiscovery() throws {
centralManager.stopScan()
_centralManager.stopScan()
}
func connect(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
func connect(uuidArgs: String, completion: @escaping (Result<Void, Error>) -> Void) {
do {
let unfinishedCompletion = connectCompletions[peripheralHashCodeArgs]
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
centralManager.connect(peripheral)
connectCompletions[peripheralHashCodeArgs] = completion
_centralManager.connect(peripheral)
_connectCompletions[uuidArgs] = completion
} catch {
completion(.failure(error))
}
}
func disconnect(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
func disconnect(uuidArgs: String, completion: @escaping (Result<Void, Error>) -> Void) {
do {
let unfinishedCompletion = disconnectCompletions[peripheralHashCodeArgs]
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
centralManager.cancelPeripheralConnection(peripheral)
disconnectCompletions[peripheralHashCodeArgs] = completion
_centralManager.cancelPeripheralConnection(peripheral)
_disconnectCompletions[uuidArgs] = completion
} catch {
completion(.failure(error))
}
}
func getMaximumWriteLength(peripheralHashCodeArgs: Int64, typeNumberArgs: Int64) throws -> Int64 {
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
func getMaximumWriteValueLength(uuidArgs: String, typeNumberArgs: Int64) throws -> Int64 {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
let typeRawValue = Int(typeNumberArgs)
guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeRawValue) else {
let typeNumber = typeNumberArgs.toInt()
guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeNumber) else {
throw MyError.illegalArgument
}
let type = typeArgs.toWriteType()
let maximumWriteLength = try peripheral.maximumWriteValueLength(for: type).coerceIn(20, 512)
let maximumWriteLengthArgs = Int64(maximumWriteLength)
return maximumWriteLengthArgs
let maximumWriteValueLength = peripheral.maximumWriteValueLength(for: type)
let maximumWriteValueLengthArgs = maximumWriteValueLength.toInt64()
return maximumWriteValueLengthArgs
}
func readRSSI(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Int64, Error>) -> Void) {
func readRSSI(uuidArgs: String, completion: @escaping (Result<Int64, Error>) -> Void) {
do {
let unfinishedCompletion = readRssiCompletions[peripheralHashCodeArgs]
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
peripheral.readRSSI()
readRssiCompletions[peripheralHashCodeArgs] = completion
_readRssiCompletions[uuidArgs] = completion
} catch {
completion(.failure(error))
}
}
func discoverGATT(peripheralHashCodeArgs: Int64, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void) {
func discoverServices(uuidArgs: String, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void) {
do {
let unfinishedCompletion = discoverGattCompletions[peripheralHashCodeArgs]
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
peripheral.discoverServices(nil)
discoverGattCompletions[peripheralHashCodeArgs] = completion
_discoverServicesCompletions[uuidArgs] = completion
} catch {
completion(.failure(error))
}
}
func readCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
func discoverCharacteristics(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattCharacteristicArgs], Error>) -> Void) {
do {
let unfinishedCompletion = readCharacteristicCompletions[characteristicHashCodeArgs]
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
guard let characteristic = characteristics[characteristicHashCodeArgs] else {
guard let service = _retrieveService(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument
}
peripheral.discoverCharacteristics(nil, for: service)
_discoverCharacteristicsCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch {
completion(.failure(error))
}
}
func discoverDescriptors(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<[MyGattDescriptorArgs], Error>) -> Void){
do {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
guard let characteristic = _retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument
}
peripheral.discoverDescriptors(for: characteristic)
_discoverDescriptorsCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch {
completion(.failure(error))
}
}
func readCharacteristic(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<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
}
peripheral.readValue(for: characteristic)
readCharacteristicCompletions[characteristicHashCodeArgs] = completion
_readCharacteristicCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch {
completion(.failure(error))
}
}
func writeCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
func writeCharacteristic(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, typeNumberArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
do {
let unfinishedCompletion = writeCharacteristicCompletions[characteristicHashCodeArgs]
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
guard let characteristic = characteristics[characteristicHashCodeArgs] else {
guard let characteristic = _retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument
}
let data = valueArgs.data
let typeRawValue = Int(typeNumberArgs)
guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeRawValue) else {
let typeNumber = typeNumberArgs.toInt()
guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeNumber) else {
throw MyError.illegalArgument
}
let type = typeArgs.toWriteType()
peripheral.writeValue(data, for: characteristic, type: type)
if type == .withResponse {
writeCharacteristicCompletions[characteristicHashCodeArgs] = completion
_writeCharacteristicCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} else {
completion(.success(()))
}
@ -239,159 +211,149 @@ class MyCentralManager: MyCentralManagerHostApi {
}
}
func notifyCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, stateArgs: Bool, completion: @escaping (Result<Void, Error>) -> Void) {
func setCharacteristicNotifyState(uuidArgs: String, hashCodeArgs: Int64, stateArgs: Bool, completion: @escaping (Result<Void, Error>) -> Void) {
do {
let unfinishedCompletion = notifyCharacteristicCompletions[characteristicHashCodeArgs]
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
guard let characteristic = characteristics[characteristicHashCodeArgs] else {
guard let characteristic = _retrieveCharacteristic(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument
}
let enabled = stateArgs
peripheral.setNotifyValue(enabled, for: characteristic)
notifyCharacteristicCompletions[characteristicHashCodeArgs] = completion
_setCharacteristicNotifyStateCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch {
completion(.failure(error))
}
}
func readDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
func readDescriptor(uuidArgs: String, hashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
do {
let unfinishedCompletion = readDescriptorCompletions[descriptorHashCodeArgs]
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
guard let descriptor = descriptors[descriptorHashCodeArgs] else {
guard let descriptor = _retrieveDescriptor(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument
}
peripheral.readValue(for: descriptor)
readDescriptorCompletions[descriptorHashCodeArgs] = completion
_readDescriptorCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch {
completion(.failure(error))
}
}
func writeDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void) {
func writeDescriptor(uuidArgs: String, hashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void) {
do {
let unfinishedCompletion = writeDescriptorCompletions[descriptorHashCodeArgs]
if unfinishedCompletion != nil {
throw MyError.illegalState
}
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
guard let peripheral = _peripherals[uuidArgs] else {
throw MyError.illegalArgument
}
guard let descriptor = descriptors[descriptorHashCodeArgs] else {
guard let descriptor = _retrieveDescriptor(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs) else {
throw MyError.illegalArgument
}
let data = valueArgs.data
peripheral.writeValue(data, for: descriptor)
writeDescriptorCompletions[descriptorHashCodeArgs] = completion
_writeDescriptorCompletions[uuidArgs, default: [:]][hashCodeArgs] = completion
} catch {
completion(.failure(error))
}
}
func didUpdateState() {
let state = centralManager.state
let stateArgs = state.toArgs()
let stateNumberArgs = Int64(stateArgs.rawValue)
if state != .unknown && setUpCompletion != nil {
let args = MyCentralManagerArgs(stateNumberArgs: stateNumberArgs)
setUpCompletion!(.success(args))
setUpCompletion = nil
}
api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in }
func didUpdateState(central: CBCentralManager) {
_onStateChanged()
}
func didDiscover(_ peripheral: CBPeripheral, _ advertisementData: [String : Any], _ rssi: NSNumber) {
func didDiscover(central: CBCentralManager, peripheral: CBPeripheral, advertisementData: [String : Any], rssi: NSNumber) {
let peripheralArgs = peripheral.toArgs()
let peripheralHashCode = peripheral.hash
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
peripheral.delegate = peripheralDelegate
peripherals[peripheralHashCodeArgs] = peripheral
peripheralsArgs[peripheralHashCode] = peripheralArgs
let uuidArgs = peripheralArgs.uuidArgs
let rssiArgs = rssi.int64Value
let advertisementArgs = advertisementData.toAdvertisementArgs()
api.onDiscovered(peripheralArgs: peripheralArgs, rssiArgs: rssiArgs, advertisementArgs: advertisementArgs) {_ in }
if peripheral.delegate == nil {
peripheral.delegate = _peripheralDelegate
}
_peripherals[uuidArgs] = peripheral
_api.onDiscovered(peripheralArgs: peripheralArgs, rssiArgs: rssiArgs, advertisementArgs: advertisementArgs) {_ in }
}
func didConnect(_ peripheral: CBPeripheral) {
let peripheralHashCode = peripheral.hash
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
func didConnect(central: CBCentralManager, peripheral: CBPeripheral) {
let uuidArgs = peripheral.identifier.toArgs()
let stateArgs = true
api.onPeripheralStateChanged(peripheralArgs: peripheralArgs, stateArgs: stateArgs) {_ in }
guard let completion = connectCompletions.removeValue(forKey: peripheralHashCodeArgs) else {
_api.onConnectionStateChanged(uuidArgs: uuidArgs, stateArgs: stateArgs) {_ in }
guard let completion = _connectCompletions.removeValue(forKey: uuidArgs) else {
return
}
completion(.success(()))
}
func didFailToConnect(_ peripheral: CBPeripheral, _ error: Error?) {
let peripheralHashCode = peripheral.hash
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
func didFailToConnect(central: CBCentralManager, peripheral: CBPeripheral, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
guard let completion = _connectCompletions.removeValue(forKey: uuidArgs) else {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
let completion = connectCompletions.removeValue(forKey: peripheralHashCodeArgs)
completion?(.failure(error ?? MyError.unknown))
completion(.failure(error ?? MyError.unknown))
}
func didDisconnectPeripheral(_ peripheral: CBPeripheral, _ error: Error?) {
let peripheralHashCode = peripheral.hash
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
return
func didDisconnectPeripheral(central: CBCentralManager, peripheral: CBPeripheral, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
_services.removeValue(forKey: uuidArgs)
_characteristics.removeValue(forKey: uuidArgs)
_descriptors.removeValue(forKey: uuidArgs)
let errorNotNil = error ?? MyError.unknown
let readRssiCompletion = _readRssiCompletions.removeValue(forKey: uuidArgs)
readRssiCompletion?(.failure(errorNotNil))
let discoverServicesCompletion = _discoverServicesCompletions.removeValue(forKey: uuidArgs)
discoverServicesCompletion?(.failure(errorNotNil))
let discoverCharacteristicsCompletions = _discoverCharacteristicsCompletions.removeValue(forKey: uuidArgs)
if discoverCharacteristicsCompletions != nil {
let completions = discoverCharacteristicsCompletions!.values
for completion in completions {
completion(.failure(errorNotNil))
}
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
let readRssiCompletion = readRssiCompletions.removeValue(forKey: peripheralHashCodeArgs)
readRssiCompletion?(.failure(error ?? MyError.unknown))
let discoverGattCompletion = discoverGattCompletions.removeValue(forKey: peripheralHashCodeArgs)
discoverGattCompletion?(.failure(error ?? MyError.unknown))
unfinishedServices.removeValue(forKey: peripheralHashCodeArgs)
unfinishedCharacteristics.removeValue(forKey: peripheralHashCodeArgs)
let servicesArgs = servicesArgsOfPeripheralsArgs.removeValue(forKey: peripheralHashCodeArgs) ?? []
for serviceArgs in servicesArgs {
let serviceHashCodeArgs = serviceArgs.hashCodeArgs
let service = services.removeValue(forKey: serviceHashCodeArgs)!
let serviceHashCode = service.hash
self.servicesArgs.removeValue(forKey: serviceHashCode)
let characteristicsArgs = serviceArgs.characteristicsArgs.map { args in args! }
for characteristicArgs in characteristicsArgs {
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
let characteristic = characteristics.removeValue(forKey: characteristicHashCodeArgs)!
let characteristicHashCode = characteristic.hash
self.characteristicsArgs.removeValue(forKey: characteristicHashCode)
let readCharacteristicCompletion = readCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs)
let writeCharacteristicCompletion = writeCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs)
let notifyCharacteristicCompletion = notifyCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs)
readCharacteristicCompletion?(.failure(error ?? MyError.unknown))
writeCharacteristicCompletion?(.failure(error ?? MyError.unknown))
notifyCharacteristicCompletion?(.failure(error ?? MyError.unknown))
let descriptorsArgs = characteristicArgs.descriptorsArgs.map { args in args! }
for descriptorArgs in descriptorsArgs {
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
let descriptor = descriptors.removeValue(forKey: descriptorHashCodeArgs)!
let descriptorHashCode = descriptor.hash
self.descriptorsArgs.removeValue(forKey: descriptorHashCode)
let readDescriptorCompletion = readDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs)
let writeDescriptorCompletion = writeDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs)
readDescriptorCompletion?(.failure(error ?? MyError.unknown))
writeDescriptorCompletion?(.failure(error ?? MyError.unknown))
}
let discoverDescriptorsCompletions = _discoverDescriptorsCompletions.removeValue(forKey: uuidArgs)
if discoverDescriptorsCompletions != nil {
let completions = discoverDescriptorsCompletions!.values
for completion in completions {
completion(.failure(errorNotNil))
}
}
let readCharacteristicCompletions = _readCharacteristicCompletions.removeValue(forKey: uuidArgs)
if readCharacteristicCompletions != nil {
let completions = readCharacteristicCompletions!.values
for completion in completions {
completion(.failure(errorNotNil))
}
}
let writeCharacteristicCompletions = _writeCharacteristicCompletions.removeValue(forKey: uuidArgs)
if writeCharacteristicCompletions != nil {
let completions = writeCharacteristicCompletions!.values
for completion in completions {
completion(.failure(errorNotNil))
}
}
let notifyCharacteristicCompletions = _setCharacteristicNotifyStateCompletions.removeValue(forKey: uuidArgs)
if notifyCharacteristicCompletions != nil {
let completions = notifyCharacteristicCompletions!.values
for completioin in completions {
completioin(.failure(errorNotNil))
}
}
let readDescriptorCompletions = _readDescriptorCompletions.removeValue(forKey: uuidArgs)
if readDescriptorCompletions != nil {
let completions = readDescriptorCompletions!.values
for completioin in completions {
completioin(.failure(errorNotNil))
}
}
let writeDescriptorCompletions = _writeDescriptorCompletions.removeValue(forKey: uuidArgs)
if writeDescriptorCompletions != nil {
let completions = writeDescriptorCompletions!.values
for completion in completions {
completion(.failure(errorNotNil))
}
}
let stateArgs = false
api.onPeripheralStateChanged(peripheralArgs: peripheralArgs, stateArgs: stateArgs) {_ in }
guard let completion = disconnectCompletions.removeValue(forKey: peripheralHashCodeArgs) else {
_api.onConnectionStateChanged(uuidArgs: uuidArgs, stateArgs: stateArgs) {_ in }
guard let completion = _disconnectCompletions.removeValue(forKey: uuidArgs) else {
return
}
if error == nil {
@ -401,13 +363,9 @@ class MyCentralManager: MyCentralManagerHostApi {
}
}
func didReadRSSI(_ peripheral: CBPeripheral, _ rssi: NSNumber, _ error: Error?) {
let peripheralHashCode = peripheral.hash
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
guard let completion = readRssiCompletions.removeValue(forKey: peripheralHashCodeArgs) else {
func didReadRSSI(peripheral: CBPeripheral, rssi: NSNumber, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
guard let completion = _readRssiCompletions.removeValue(forKey: uuidArgs) else {
return
}
if error == nil {
@ -418,157 +376,110 @@ class MyCentralManager: MyCentralManagerHostApi {
}
}
func didDiscoverServices(_ peripheral: CBPeripheral, _ error: Error?) {
let peripheralHashCode = peripheral.hash
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
if error == nil {
var services = peripheral.services ?? []
if services.isEmpty {
didDiscoverGATT(peripheral, error)
} else {
let service = services.removeFirst()
unfinishedServices[peripheralHashCodeArgs] = services
peripheral.discoverCharacteristics(nil, for: service)
}
} else {
didDiscoverGATT(peripheral, error)
}
}
func didDiscoverCharacteristics(_ peripheral: CBPeripheral, _ service: CBService, _ error: Error?) {
let peripheralHashCode = peripheral.hash
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
if error == nil {
var characteristics = service.characteristics ?? []
if characteristics.isEmpty {
var services = unfinishedServices.removeValue(forKey: peripheralHashCodeArgs) ?? []
if services.isEmpty {
didDiscoverGATT(peripheral, error)
} else {
let service = services.removeFirst()
unfinishedServices[peripheralHashCodeArgs] = services
peripheral.discoverCharacteristics(nil, for: service)
}
} else {
let characteristic = characteristics.removeFirst()
unfinishedCharacteristics[peripheralHashCodeArgs] = characteristics
peripheral.discoverDescriptors(for: characteristic)
}
} else {
didDiscoverGATT(peripheral, error)
}
}
func didDiscoverDescriptors(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic, _ error: Error?) {
let peripheralHashCode = peripheral.hash
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
if error == nil {
var characteristics = unfinishedCharacteristics.removeValue(forKey: peripheralHashCodeArgs) ?? []
if (characteristics.isEmpty) {
var services = unfinishedServices.removeValue(forKey: peripheralHashCodeArgs) ?? []
if services.isEmpty {
didDiscoverGATT(peripheral, error)
} else {
let service = services.removeFirst()
unfinishedServices[peripheralHashCodeArgs] = services
peripheral.discoverCharacteristics(nil, for: service)
}
} else {
let characteristic = characteristics.removeFirst()
unfinishedCharacteristics[peripheralHashCodeArgs] = characteristics
peripheral.discoverDescriptors(for: characteristic)
}
} else {
didDiscoverGATT(peripheral, error)
}
}
private func didDiscoverGATT(_ peripheral: CBPeripheral, _ error: Error?) {
let peripheralHashCode = peripheral.hash
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
return
}
let peripheralhashCodeArgs = peripheralArgs.hashCodeArgs
guard let completion = discoverGattCompletions.removeValue(forKey: peripheralhashCodeArgs) else {
func didDiscoverServices(peripheral: CBPeripheral, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
guard let completion = _discoverServicesCompletions.removeValue(forKey: uuidArgs) else {
return
}
if error == nil {
let services = peripheral.services ?? []
var servicesArgs = [MyGattServiceArgs]()
for service in services {
let characteristics = service.characteristics ?? []
var characteristicsArgs = [MyGattCharacteristicArgs]()
for characteristic in characteristics {
let descriptors = characteristic.descriptors ?? []
var descriptorsArgs = [MyGattDescriptorArgs]()
for descriptor in descriptors {
let descriptorArgs = descriptor.toArgs()
let descriptorHashCode = descriptor.hash
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
self.descriptors[descriptorHashCodeArgs] = descriptor
self.descriptorsArgs[descriptorHashCode] = descriptorArgs
descriptorsArgs.append(descriptorArgs)
}
let characteristicArgs = characteristic.toArgs(descriptorsArgs)
let characteristicHashCode = characteristic.hash
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
self.characteristics[characteristicHashCodeArgs] = characteristic
self.characteristicsArgs[characteristicHashCode] = characteristicArgs
characteristicsArgs.append(characteristicArgs)
}
let serviceArgs = service.toArgs(characteristicsArgs)
let serviceHashCode = service.hash
let servcieHashCodeArgs = serviceArgs.hashCodeArgs
self.services[servcieHashCodeArgs] = service
self.servicesArgs[serviceHashCode] = serviceArgs
servicesArgs.append(serviceArgs)
let servicesArgs = services.map { service in service.toArgs() }
let elements = services.flatMap { service in
let hashCodeArgs = service.hash.toInt64()
return [hashCodeArgs: service]
}
servicesArgsOfPeripheralsArgs[peripheralhashCodeArgs] = servicesArgs
completion(.success((servicesArgs)))
var items = _services[uuidArgs]
if items == nil {
_services[uuidArgs] = Dictionary(uniqueKeysWithValues: elements)
} else {
items!.merge(elements) { service1, service2 in service2 }
}
completion(.success(servicesArgs))
} else {
completion(.failure(error!))
unfinishedServices.removeValue(forKey: peripheralhashCodeArgs)
unfinishedCharacteristics.removeValue(forKey: peripheralhashCodeArgs)
}
}
func didUpdateCharacteristicValue(_ characteristic: CBCharacteristic, _ error: Error?) {
let characteristicHashCode = characteristic.hash
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
func didDiscoverCharacteristics(peripheral: CBPeripheral, service: CBService, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
let hashCodeArgs = service.hash.toInt64()
guard var completions = _discoverCharacteristicsCompletions[uuidArgs] else {
return
}
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
guard let completion = readCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) else {
let value = characteristic.value ?? Data()
let valueArgs = FlutterStandardTypedData(bytes: value)
api.onCharacteristicValueChanged(characteristicArgs: characteristicArgs, valueArgs: valueArgs) {_ in }
guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
return
}
if error == nil {
let characteristics = service.characteristics ?? []
let characteristicsArgs = characteristics.map { characteristic in characteristic.toArgs() }
let elements = characteristics.flatMap { characteristic in
let hashCodeArgs = characteristic.hash.toInt64()
return [hashCodeArgs: characteristic]
}
var items = _characteristics[uuidArgs]
if items == nil {
_characteristics[uuidArgs] = Dictionary(uniqueKeysWithValues: elements)
} else {
items!.merge(elements) { characteristic1, characteristic2 in characteristic2 }
}
completion(.success(characteristicsArgs))
} else {
completion(.failure(error!))
}
}
func didDiscoverDescriptors(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
let hashCodeArgs = characteristic.hash.toInt64()
guard var completions = _discoverDescriptorsCompletions[uuidArgs] else {
return
}
guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
return
}
if error == nil {
let descriptors = characteristic.descriptors ?? []
let descriptorsArgs = descriptors.map { descriptor in descriptor.toArgs() }
let elements = descriptors.flatMap { descriptor in
let hashCodeArgs = descriptor.hash.toInt64()
return [hashCodeArgs: descriptor]
}
var items = _descriptors[uuidArgs]
if items == nil {
_descriptors[uuidArgs] = Dictionary(uniqueKeysWithValues: elements)
} else {
items!.merge(elements) { descriptor1, descriptor2 in descriptor2 }
}
completion(.success(descriptorsArgs))
} else {
completion(.failure(error!))
}
}
func didUpdateCharacteristicValue(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
let hashCodeArgs = characteristic.hash.toInt64()
let value = characteristic.value ?? Data()
let valueArgs = FlutterStandardTypedData(bytes: value)
var completions = _readCharacteristicCompletions[uuidArgs]
guard let completion = completions?.removeValue(forKey: hashCodeArgs) else {
_api.onCharacteristicNotified(uuidArgs: uuidArgs, hashCodeArgs: hashCodeArgs, valueArgs: valueArgs) {_ in }
return
}
if error == nil {
let value = characteristic.value ?? Data()
let valueArgs = FlutterStandardTypedData(bytes: value)
completion(.success(valueArgs))
} else {
completion(.failure(error!))
}
}
func didWriteCharacteristicValue(_ characteristic: CBCharacteristic, _ error: Error?) {
let characteristicHashCode = characteristic.hash
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
func didWriteCharacteristicValue(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
let hashCodeArgs = characteristic.hash.toInt64()
guard var completions = _writeCharacteristicCompletions[uuidArgs] else {
return
}
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
guard let completion = writeCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) else {
guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
return
}
if error == nil {
@ -578,13 +489,13 @@ class MyCentralManager: MyCentralManagerHostApi {
}
}
func didUpdateNotificationState(_ characteristic: CBCharacteristic, _ error: Error?) {
let characteristicHashCode = characteristic.hash
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
func didUpdateCharacteristicNotificationState(peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
let hashCodeArgs = characteristic.hash.toInt64()
guard var completions = _setCharacteristicNotifyStateCompletions[uuidArgs] else {
return
}
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
guard let completion = notifyCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) else {
guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
return
}
if error == nil {
@ -594,17 +505,17 @@ class MyCentralManager: MyCentralManagerHostApi {
}
}
func didUpdateDescriptorValue(_ descriptor: CBDescriptor, _ error: Error?) {
let descriptorHashCode = descriptor.hash
guard let descriptorArgs = descriptorsArgs[descriptorHashCode] else {
func didUpdateDescriptorValue(peripheral: CBPeripheral, descriptor: CBDescriptor, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
let hashCodeArgs = descriptor.hash.toInt64()
guard var completions = _readDescriptorCompletions[uuidArgs] else {
return
}
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
guard let completion = readDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs) else {
guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
return
}
if error == nil {
// TODO: Need to confirm wheather the corresponding descriptor type and value is correct.
// TODO: confirm the corresponding descriptor types and values are correct.
let valueArgs: FlutterStandardTypedData
let value = descriptor.value
do {
@ -647,13 +558,13 @@ class MyCentralManager: MyCentralManagerHostApi {
}
}
func didWriteDescriptorValue(_ descriptor: CBDescriptor, _ error: Error?) {
let descriptorHashCode = descriptor.hash
guard let descriptorArgs = descriptorsArgs[descriptorHashCode] else {
func didWriteDescriptorValue(peripheral: CBPeripheral, descriptor: CBDescriptor, error: Error?) {
let uuidArgs = peripheral.identifier.toArgs()
let hashCodeArgs = descriptor.hash.toInt64()
guard var completions = _writeDescriptorCompletions[uuidArgs] else {
return
}
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
guard let completion = writeDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs) else {
guard let completion = completions.removeValue(forKey: hashCodeArgs) else {
return
}
if error == nil {
@ -662,4 +573,60 @@ class MyCentralManager: MyCentralManagerHostApi {
completion(.failure(error!))
}
}
private func _clearState() {
if(_centralManager.isScanning) {
_centralManager.stopScan()
}
for peripheral in _peripherals.values {
if peripheral.state != .disconnected {
_centralManager.cancelPeripheralConnection(peripheral)
}
}
_peripherals.removeAll()
_services.removeAll()
_characteristics.removeAll()
_descriptors.removeAll()
_connectCompletions.removeAll()
_disconnectCompletions.removeAll()
_readRssiCompletions.removeAll()
_discoverServicesCompletions.removeAll()
_discoverCharacteristicsCompletions.removeAll()
_discoverDescriptorsCompletions.removeAll()
_readCharacteristicCompletions.removeAll()
_writeCharacteristicCompletions.removeAll()
_setCharacteristicNotifyStateCompletions.removeAll()
_readDescriptorCompletions.removeAll()
_writeDescriptorCompletions.removeAll()
}
private func _retrieveService(uuidArgs: String, hashCodeArgs: Int64) -> CBService? {
guard let services = _services[uuidArgs] else {
return nil
}
return services[hashCodeArgs]
}
private func _retrieveCharacteristic(uuidArgs: String, hashCodeArgs: Int64) -> CBCharacteristic? {
guard let characteristics = _characteristics[uuidArgs] else {
return nil
}
return characteristics[hashCodeArgs]
}
private func _retrieveDescriptor(uuidArgs: String, hashCodeArgs: Int64) -> CBDescriptor? {
guard let descriptors = _descriptors[uuidArgs] else {
return nil
}
return descriptors[hashCodeArgs]
}
private func _onStateChanged() {
let state = _centralManager.state
let stateArgs = state.toArgs()
let stateNumberArgs = stateArgs.rawValue.toInt64()
_api.onStateChanged(stateNumberArgs: stateNumberArgs) {_ in }
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@ -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(),
),
)
@HostApi()
abstract class MyCentralManagerHostApi {
@async
MyCentralManagerArgs setUp();
void startDiscovery();
void stopDiscovery();
@async
void connect(int peripheralHashCodeArgs);
@async
void disconnect(int peripheralHashCodeArgs);
int getMaximumWriteLength(int peripheralHashCodeArgs, int typeNumberArgs);
@async
int readRSSI(int peripheralHashCodeArgs);
@async
List<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,
);
enum MyBluetoothLowEnergyStateArgs {
unknown,
resetting,
unsupported,
unauthorized,
poweredOff,
poweredOn,
}
@FlutterApi()
abstract class MyCentralManagerFlutterApi {
void onStateChanged(int stateNumberArgs);
void onDiscovered(
MyPeripheralArgs peripheralArgs,
int rssiArgs,
MyAdvertisementArgs advertisementArgs,
);
void onPeripheralStateChanged(
MyPeripheralArgs peripheralArgs,
bool stateArgs,
);
void onCharacteristicValueChanged(
MyGattCharacteristicArgs characteristicArgs,
Uint8List valueArgs,
);
enum MyGattCharacteristicPropertyArgs {
read,
write,
writeWithoutResponse,
notify,
indicate,
}
@HostApi()
abstract class MyPeripheralManagerHostApi {
@async
MyPeripheralManagerArgs setUp();
@async
void addService(MyGattServiceArgs serviceArgs);
void removeService(int serviceHashCodeArgs);
void clearServices();
@async
void startAdvertising(MyAdvertisementArgs advertisementArgs);
void stopAdvertising();
int getMaximumWriteLength(int centralHashCodeArgs);
void sendReadCharacteristicReply(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
int idArgs,
int offsetArgs,
bool statusArgs,
Uint8List valueArgs,
);
void sendWriteCharacteristicReply(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
int idArgs,
int offsetArgs,
bool statusArgs,
);
@async
void notifyCharacteristicValueChanged(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
Uint8List valueArgs,
);
enum MyGattCharacteristicWriteTypeArgs {
withResponse,
withoutResponse,
}
@FlutterApi()
abstract class MyPeripheralManagerFlutterApi {
void onStateChanged(int stateNumberArgs);
void onReadCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
);
void onWriteCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
Uint8List valueArgs,
);
void onNotifyCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
bool stateArgs,
);
enum MyGattErrorArgs {
success,
invalidHandle,
readNotPermitted,
writeNotPermitted,
invalidPDU,
insufficientAuthentication,
requestNotSupported,
invalidOffset,
insufficientAuthorization,
prepareQueueFull,
attributeNotFound,
attributeNotLong,
insufficientEncryptionKeySize,
invalidAttributeValueLength,
unlikelyError,
insufficientEncryption,
unsupportedGroupType,
insufficientResources,
}
class MyCentralManagerArgs {
final int stateNumberArgs;
class MyManufacturerSpecificDataArgs {
final int idArgs;
final Uint8List dataArgs;
MyCentralManagerArgs(this.stateNumberArgs);
}
class MyPeripheralManagerArgs {
final int stateNumberArgs;
MyPeripheralManagerArgs(this.stateNumberArgs);
}
class MyCentralArgs {
final int hashCodeArgs;
final String uuidArgs;
MyCentralArgs(this.hashCodeArgs, this.uuidArgs);
}
class MyPeripheralArgs {
final int hashCodeArgs;
final String uuidArgs;
MyPeripheralArgs(this.hashCodeArgs, this.uuidArgs);
MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs);
}
class MyAdvertisementArgs {
@ -170,22 +72,27 @@ class MyAdvertisementArgs {
);
}
class MyManufacturerSpecificDataArgs {
final int idArgs;
final Uint8List dataArgs;
class MyCentralArgs {
final String uuidArgs;
MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs);
MyCentralArgs(this.uuidArgs);
}
class MyGattServiceArgs {
class MyPeripheralArgs {
final String uuidArgs;
MyPeripheralArgs(this.uuidArgs);
}
class MyGattDescriptorArgs {
final int hashCodeArgs;
final String uuidArgs;
final List<MyGattCharacteristicArgs?> characteristicsArgs;
final Uint8List? valueArgs;
MyGattServiceArgs(
MyGattDescriptorArgs(
this.hashCodeArgs,
this.uuidArgs,
this.characteristicsArgs,
this.valueArgs,
);
}
@ -203,39 +110,118 @@ class MyGattCharacteristicArgs {
);
}
class MyGattDescriptorArgs {
class MyGattServiceArgs {
final int hashCodeArgs;
final String uuidArgs;
final Uint8List? valueArgs;
final List<MyGattCharacteristicArgs?> characteristicsArgs;
MyGattDescriptorArgs(
MyGattServiceArgs(
this.hashCodeArgs,
this.uuidArgs,
this.valueArgs,
this.characteristicsArgs,
);
}
enum MyBluetoothLowEnergyStateArgs {
unknown,
unsupported,
unauthorized,
poweredOff,
poweredOn,
@HostApi()
abstract class MyCentralManagerHostApi {
void setUp();
void startDiscovery();
void stopDiscovery();
@async
void connect(String uuidArgs);
@async
void disconnect(String uuidArgs);
int getMaximumWriteValueLength(String uuidArgs, int typeNumberArgs);
@async
int readRSSI(String uuidArgs);
@async
List<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 {
read,
write,
writeWithoutResponse,
notify,
indicate,
@FlutterApi()
abstract class MyCentralManagerFlutterApi {
void onStateChanged(int stateNumberArgs);
void onDiscovered(
MyPeripheralArgs peripheralArgs,
int rssiArgs,
MyAdvertisementArgs advertisementArgs,
);
void onConnectionStateChanged(String uuidArgs, bool stateArgs);
void onCharacteristicNotified(
String uuidArgs,
int hashCodeArgs,
Uint8List valueArgs,
);
}
enum MyGattCharacteristicWriteTypeArgs {
// Write with response
withResponse,
// Write without response
withoutResponse,
// Write with response and waiting for confirmation
// reliable,
@HostApi()
abstract class MyPeripheralManagerHostApi {
void setUp();
@async
void addService(MyGattServiceArgs serviceArgs);
void removeService(int hashCodeArgs);
void clearServices();
@async
void startAdvertising(MyAdvertisementArgs advertisementArgs);
void stopAdvertising();
int getMaximumUpdateValueLength(String uuidArgs);
void respond(int idArgs, int errorNumberArgs, Uint8List? valueArgs);
@async
void updateCharacteristic(
int hashCodeArgs,
Uint8List valueArgs,
List<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
description: iOS and macOS implementation of the bluetooth_low_energy plugin.
version: 4.0.0
version: 5.0.0
homepage: https://github.com/yanshouwang/bluetooth_low_energy
environment:
@ -10,13 +10,13 @@ environment:
dependencies:
flutter:
sdk: flutter
bluetooth_low_energy_platform_interface: ^4.0.0
bluetooth_low_energy_platform_interface: ^5.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
pigeon: ^12.0.1
pigeon: ^15.0.2
flutter:
plugin:

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
* Remove `BluetoothLowEnergy` class.

View File

@ -8,9 +8,6 @@ import 'package:convert/convert.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
CentralManager get centralManager => CentralManager.instance;
PeripheralManager get peripheralManager => PeripheralManager.instance;
void main() {
runZonedGuarded(onStartUp, onCrashed);
}
@ -18,8 +15,8 @@ void main() {
void onStartUp() async {
Logger.root.onRecord.listen(onLogRecord);
WidgetsFlutterBinding.ensureInitialized();
await centralManager.setUp();
await peripheralManager.setUp();
await CentralManager.instance.setUp();
// await peripheralManager.setUp();
runApp(const MyApp());
}
@ -58,6 +55,8 @@ class _MyAppState extends State<MyApp> {
return MaterialApp(
theme: ThemeData.light(
useMaterial3: true,
).copyWith(
materialTapTargetSize: MaterialTapTargetSize.padded,
),
home: const HomeView(),
routes: {
@ -73,81 +72,18 @@ class _MyAppState extends State<MyApp> {
}
}
class HomeView extends StatefulWidget {
class HomeView extends StatelessWidget {
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
Widget build(BuildContext context) {
return Scaffold(
body: buildBody(context),
bottomNavigationBar: buildBottomNavigationBar(context),
);
}
Widget buildBody(BuildContext context) {
return PageView.builder(
controller: controller,
itemBuilder: (context, i) {
switch (i) {
case 0:
return const ScannerView();
case 1:
return const AdvertiserView();
default:
throw ArgumentError.value(i);
}
},
itemCount: 2,
);
}
Widget buildBottomNavigationBar(BuildContext context) {
return ListenableBuilder(
listenable: controller,
builder: (context, child) {
return BottomNavigationBar(
onTap: (i) {
const duration = Duration(milliseconds: 300);
const curve = Curves.ease;
controller.animateToPage(
i,
duration: duration,
curve: curve,
);
},
currentIndex: controller.page?.toInt() ?? 0,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.radar),
label: 'scanner',
),
BottomNavigationBarItem(
icon: Icon(Icons.sensors),
label: 'advertiser',
),
],
);
},
);
}
@override
void dispose() {
super.dispose();
controller.dispose();
return const ScannerView();
}
}
@ -168,16 +104,20 @@ class _ScannerViewState extends State<ScannerView> {
@override
void initState() {
super.initState();
state = ValueNotifier(centralManager.state);
state = ValueNotifier(BluetoothLowEnergyState.unknown);
discovering = ValueNotifier(false);
discoveredEventArgs = ValueNotifier([]);
stateChangedSubscription = centralManager.stateChanged.listen(
stateChangedSubscription = CentralManager.instance.stateChanged.listen(
(eventArgs) {
state.value = eventArgs.state;
},
);
discoveredSubscription = centralManager.discovered.listen(
discoveredSubscription = CentralManager.instance.discovered.listen(
(eventArgs) {
final name = eventArgs.advertisement.name;
if (name == null || name.isEmpty) {
return;
}
final items = discoveredEventArgs.value;
final i = items.indexWhere(
(item) => item.peripheral == eventArgs.peripheral,
@ -190,6 +130,11 @@ class _ScannerViewState extends State<ScannerView> {
}
},
);
setUp();
}
void setUp() async {
state.value = await CentralManager.instance.getState();
}
@override
@ -233,12 +178,13 @@ class _ScannerViewState extends State<ScannerView> {
}
Future<void> startDiscovery() async {
await centralManager.startDiscovery();
discoveredEventArgs.value = [];
await CentralManager.instance.startDiscovery();
discovering.value = true;
}
Future<void> stopDiscovery() async {
await centralManager.stopDiscovery();
await CentralManager.instance.stopDiscovery();
discovering.value = false;
}
@ -340,7 +286,13 @@ class _ScannerViewState extends State<ScannerView> {
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: RssiWidget(rssi),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
RssiWidget(rssi),
Text('$rssi'),
],
),
);
},
separatorBuilder: (context, i) {
@ -378,44 +330,39 @@ class PeripheralView extends StatefulWidget {
}
class _PeripheralViewState extends State<PeripheralView> {
late final ValueNotifier<bool> state;
late final ValueNotifier<bool> connectionState;
late final DiscoveredEventArgs eventArgs;
late final ValueNotifier<List<GattService>> services;
late final ValueNotifier<List<GattCharacteristic>> characteristics;
late final ValueNotifier<GattService?> service;
late final ValueNotifier<GattCharacteristic?> characteristic;
late final ValueNotifier<GattCharacteristicWriteType> writeType;
late final ValueNotifier<int> maximumWriteLength;
late final ValueNotifier<int> rssi;
late final ValueNotifier<List<Log>> logs;
late final TextEditingController writeController;
late final StreamSubscription stateChangedSubscription;
late final StreamSubscription valueChangedSubscription;
late final StreamSubscription rssiChangedSubscription;
late final Timer rssiTimer;
late final StreamSubscription connectionStateChangedSubscription;
late final StreamSubscription characteristicNotifiedSubscription;
@override
void initState() {
super.initState();
eventArgs = widget.eventArgs;
state = ValueNotifier(false);
connectionState = ValueNotifier(false);
services = ValueNotifier([]);
characteristics = ValueNotifier([]);
service = ValueNotifier(null);
characteristic = ValueNotifier(null);
writeType = ValueNotifier(GattCharacteristicWriteType.withResponse);
maximumWriteLength = ValueNotifier(0);
rssi = ValueNotifier(-100);
logs = ValueNotifier([]);
writeController = TextEditingController();
stateChangedSubscription = centralManager.peripheralStateChanged.listen(
connectionStateChangedSubscription =
CentralManager.instance.connectionStateChanged.listen(
(eventArgs) {
if (eventArgs.peripheral != this.eventArgs.peripheral) {
return;
}
final state = eventArgs.state;
this.state.value = state;
if (!state) {
final connectionState = eventArgs.connectionState;
this.connectionState.value = connectionState;
if (!connectionState) {
services.value = [];
characteristics.value = [];
service.value = null;
@ -424,12 +371,13 @@ class _PeripheralViewState extends State<PeripheralView> {
}
},
);
valueChangedSubscription = centralManager.characteristicValueChanged.listen(
characteristicNotifiedSubscription =
CentralManager.instance.characteristicNotified.listen(
(eventArgs) {
final characteristic = this.characteristic.value;
if (eventArgs.characteristic != characteristic) {
return;
}
// final characteristic = this.characteristic.value;
// if (eventArgs.characteristic != characteristic) {
// return;
// }
const type = LogType.notify;
final log = Log(type, eventArgs.value);
logs.value = [
@ -438,28 +386,16 @@ class _PeripheralViewState extends State<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
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (state.value) {
return PopScope(
onPopInvoked: (didPop) async {
if (connectionState.value) {
final peripheral = eventArgs.peripheral;
await centralManager.disconnect(peripheral);
await CentralManager.instance.disconnect(peripheral);
}
return true;
},
child: Scaffold(
appBar: buildAppBar(context),
@ -474,25 +410,17 @@ class _PeripheralViewState extends State<PeripheralView> {
title: Text(title),
actions: [
ValueListenableBuilder(
valueListenable: state,
valueListenable: connectionState,
builder: (context, state, child) {
return TextButton(
onPressed: () async {
final peripheral = eventArgs.peripheral;
if (state) {
await centralManager.disconnect(peripheral);
maximumWriteLength.value = 0;
rssi.value = 0;
await CentralManager.instance.disconnect(peripheral);
} else {
await centralManager.connect(peripheral);
await CentralManager.instance.connect(peripheral);
services.value =
await centralManager.discoverGATT(peripheral);
maximumWriteLength.value =
await centralManager.getMaximumWriteLength(
peripheral,
type: writeType.value,
);
rssi.value = await centralManager.readRSSI(peripheral);
await CentralManager.instance.discoverGATT(peripheral);
}
},
child: Text(state ? 'DISCONNECT' : 'CONNECT'),
@ -566,7 +494,32 @@ class _PeripheralViewState extends State<PeripheralView> {
hint: const Text('CHOOSE A CHARACTERISTIC'),
value: characteristic,
onChanged: (characteristic) {
if (characteristic == null) {
return;
}
this.characteristic.value = characteristic;
final writeType = this.writeType.value;
final canWrite = characteristic.properties.contains(
GattCharacteristicProperty.write,
);
final canWriteWithoutResponse =
characteristic.properties.contains(
GattCharacteristicProperty.writeWithoutResponse,
);
if (writeType ==
GattCharacteristicWriteType.withResponse &&
!canWrite &&
canWriteWithoutResponse) {
this.writeType.value =
GattCharacteristicWriteType.withoutResponse;
}
if (writeType ==
GattCharacteristicWriteType.withoutResponse &&
!canWriteWithoutResponse &&
canWrite) {
this.writeType.value =
GattCharacteristicWriteType.withResponse;
}
},
);
},
@ -624,129 +577,45 @@ class _PeripheralViewState extends State<PeripheralView> {
},
),
),
Row(
children: [
ValueListenableBuilder(
valueListenable: writeType,
builder: (context, writeType, child) {
return ToggleButtons(
onPressed: (i) async {
final type = GattCharacteristicWriteType.values[i];
this.writeType.value = type;
maximumWriteLength.value =
await centralManager.getMaximumWriteLength(
eventArgs.peripheral,
type: type,
);
},
constraints: const BoxConstraints(
minWidth: 0.0,
minHeight: 0.0,
),
borderRadius: BorderRadius.circular(4.0),
isSelected: GattCharacteristicWriteType.values
.map((type) => type == writeType)
.toList(),
children: GattCharacteristicWriteType.values.map((type) {
return Container(
margin: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 4.0,
),
child: Text(type.name),
);
}).toList(),
);
// final segments =
// GattCharacteristicWriteType.values.map((type) {
// return ButtonSegment(
// value: type,
// label: Text(type.name),
// );
// }).toList();
// return SegmentedButton(
// segments: segments,
// selected: {writeType},
// showSelectedIcon: false,
// style: OutlinedButton.styleFrom(
// tapTargetSize: MaterialTapTargetSize.shrinkWrap,
// padding: EdgeInsets.zero,
// visualDensity: VisualDensity.compact,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(8.0),
// ),
// ),
// );
},
),
const SizedBox(width: 8.0),
ValueListenableBuilder(
valueListenable: state,
builder: (context, state, child) {
return ValueListenableBuilder(
valueListenable: maximumWriteLength,
builder: (context, maximumWriteLength, child) {
return Text('$maximumWriteLength');
},
);
},
),
const Spacer(),
ValueListenableBuilder(
valueListenable: rssi,
builder: (context, rssi, child) {
return RssiWidget(rssi);
},
),
],
),
Container(
margin: const EdgeInsets.only(bottom: 16.0),
height: 160.0,
child: ValueListenableBuilder(
valueListenable: characteristic,
builder: (context, characteristic, child) {
final bool canNotify, canRead, canWrite;
if (characteristic == null) {
canNotify = canRead = canWrite = false;
} else {
final properties = characteristic.properties;
canNotify = properties.contains(
GattCharacteristicProperty.notify,
);
canRead = properties.contains(
GattCharacteristicProperty.read,
);
canWrite = properties.contains(
GattCharacteristicProperty.write,
);
}
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: TextField(
controller: writeController,
enabled: canWrite,
expands: true,
maxLines: null,
textAlignVertical: TextAlignVertical.top,
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
),
),
Column(
mainAxisSize: MainAxisSize.min,
ValueListenableBuilder(
valueListenable: characteristic,
builder: (context, characteristic, chld) {
final bool canNotify, canRead, canWrite, canWriteWithoutResponse;
if (characteristic == null) {
canNotify =
canRead = canWrite = canWriteWithoutResponse = false;
} else {
final properties = characteristic.properties;
canNotify = properties.contains(
GattCharacteristicProperty.notify,
) ||
properties.contains(
GattCharacteristicProperty.indicate,
);
canRead = properties.contains(
GattCharacteristicProperty.read,
);
canWrite = properties.contains(
GattCharacteristicProperty.write,
);
canWriteWithoutResponse = properties.contains(
GattCharacteristicProperty.writeWithoutResponse,
);
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
margin: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
ElevatedButton(
onPressed: characteristic != null && canNotify
? () async {
await centralManager.notifyCharacteristic(
await CentralManager.instance
.setCharacteristicNotifyState(
characteristic,
state: true,
);
@ -754,10 +623,11 @@ class _PeripheralViewState extends State<PeripheralView> {
: null,
child: const Text('NOTIFY'),
),
TextButton(
const SizedBox(width: 8.0),
ElevatedButton(
onPressed: characteristic != null && canRead
? () async {
final value = await centralManager
final value = await CentralManager.instance
.readCharacteristic(characteristic);
const type = LogType.read;
final log = Log(type, value);
@ -765,31 +635,124 @@ class _PeripheralViewState extends State<PeripheralView> {
}
: null,
child: const Text('READ'),
),
TextButton(
onPressed: characteristic != null && canWrite
? () async {
final text = writeController.text;
final elements = utf8.encode(text);
final value = Uint8List.fromList(elements);
final type = writeType.value;
await centralManager.writeCharacteristic(
characteristic,
value: value,
type: type,
);
final log = Log(LogType.write, value);
logs.value = [...logs.value, log];
}
: null,
child: const Text('WRITE'),
),
)
],
),
],
);
},
),
),
SizedBox(
height: 160.0,
child: TextField(
controller: writeController,
enabled: canWrite || canWriteWithoutResponse,
expands: true,
maxLines: null,
textAlignVertical: TextAlignVertical.top,
decoration: const InputDecoration(
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 8.0,
),
),
),
),
Row(
children: [
ValueListenableBuilder(
valueListenable: writeType,
builder: (context, writeType, child) {
return ToggleButtons(
onPressed: canWrite || canWriteWithoutResponse
? (i) {
if (!canWrite || !canWriteWithoutResponse) {
return;
}
final type =
GattCharacteristicWriteType.values[i];
this.writeType.value = type;
}
: null,
constraints: const BoxConstraints(
minWidth: 0.0,
minHeight: 0.0,
),
borderRadius: BorderRadius.circular(4.0),
isSelected: GattCharacteristicWriteType.values
.map((type) => type == writeType)
.toList(),
children: GattCharacteristicWriteType.values.map(
(type) {
return Container(
margin: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 4.0,
),
child: Text(type.name),
);
},
).toList(),
);
// final segments =
// GattCharacteristicWriteType.values.map((type) {
// return ButtonSegment(
// value: type,
// label: Text(type.name),
// );
// }).toList();
// return SegmentedButton(
// segments: segments,
// selected: {writeType},
// showSelectedIcon: false,
// style: OutlinedButton.styleFrom(
// tapTargetSize: MaterialTapTargetSize.shrinkWrap,
// padding: EdgeInsets.zero,
// visualDensity: VisualDensity.compact,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(8.0),
// ),
// ),
// );
},
),
const Spacer(),
ElevatedButton(
onPressed: characteristic != null && canWrite
? () async {
final text = writeController.text;
final elements = utf8.encode(text);
final value = Uint8List.fromList(elements);
final type = writeType.value;
// Fragments the value by 512 bytes.
const fragmentSize = 512;
var start = 0;
while (start < value.length) {
final end = start + fragmentSize;
final fragmentedValue = end < value.length
? value.sublist(start, end)
: value.sublist(start);
await CentralManager.instance
.writeCharacteristic(
characteristic,
value: fragmentedValue,
type: type,
);
final log = Log(
LogType.write,
fragmentedValue,
);
logs.value = [...logs.value, log];
start = end;
}
}
: null,
child: const Text('WRITE'),
),
],
),
const SizedBox(height: 16.0),
],
);
},
),
],
),
@ -799,290 +762,19 @@ class _PeripheralViewState extends State<PeripheralView> {
@override
void dispose() {
super.dispose();
rssiTimer.cancel();
stateChangedSubscription.cancel();
valueChangedSubscription.cancel();
state.dispose();
connectionStateChangedSubscription.cancel();
characteristicNotifiedSubscription.cancel();
connectionState.dispose();
services.dispose();
characteristics.dispose();
service.dispose();
characteristic.dispose();
writeType.dispose();
maximumWriteLength.dispose();
rssi.dispose();
logs.dispose();
writeController.dispose();
}
}
class AdvertiserView extends StatefulWidget {
const AdvertiserView({super.key});
@override
State<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 {
final DateTime time;
final LogType type;

View File

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

View File

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

View File

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

View File

@ -7,81 +7,6 @@ import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart';
import 'my_gatt_service2.dart';
extension BlueZAdapterX on BlueZAdapter {
BluetoothLowEnergyState get myState {
return powered
? BluetoothLowEnergyState.poweredOn
: BluetoothLowEnergyState.poweredOff;
}
}
extension BlueZDeviceX on BlueZDevice {
BlueZUUID get uuid {
final node = address.replaceAll(':', '');
// We don't know the timestamp of the bluetooth device, use nil UUID as prefix.
return BlueZUUID.fromString("00000000-0000-0000-0000-$node");
}
UUID get myUUID => uuid.toMyUUID();
List<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 {
GattCharacteristicProperty? toMyProperty() {
switch (this) {
@ -113,3 +38,75 @@ extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType {
}
}
}
extension BlueZAdapterX on BlueZAdapter {
BluetoothLowEnergyState get myState {
return powered
? BluetoothLowEnergyState.poweredOn
: BluetoothLowEnergyState.poweredOff;
}
}
extension BlueZDeviceX on BlueZDevice {
UUID get myUUID => UUID.fromAddress(address);
List<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 'my_bluez.dart';
import 'my_gatt_descriptor2.dart';
class MyGattCharacteristic2 extends MyGattCharacteristic {
final BlueZGattCharacteristic characteristic;
final BlueZGattCharacteristic blueZCharacteristic;
MyGattCharacteristic2(this.characteristic)
MyGattCharacteristic2(this.blueZCharacteristic)
: super(
hashCode: characteristic.hashCode,
uuid: characteristic.myUUID,
properties: characteristic.myProperties,
descriptors: characteristic.myDescriptors,
uuid: blueZCharacteristic.myUUID,
properties: blueZCharacteristic.myProperties,
descriptors: blueZCharacteristic.myDescriptors,
);
@override
List<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';
class MyGattDescriptor2 extends MyGattDescriptor {
final BlueZGattDescriptor descriptor;
final BlueZGattDescriptor blueZDescriptor;
MyGattDescriptor2(this.descriptor)
MyGattDescriptor2(this.blueZDescriptor)
: super(
hashCode: descriptor.hashCode,
uuid: descriptor.myUUID,
uuid: blueZDescriptor.myUUID,
);
@override
int get hashCode => blueZDescriptor.hashCode;
@override
bool operator ==(Object other) {
return other is MyGattDescriptor2 &&
other.blueZDescriptor == blueZDescriptor;
}
}

View File

@ -2,14 +2,26 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla
import 'package:bluez/bluez.dart';
import 'my_bluez.dart';
import 'my_gatt_characteristic2.dart';
class MyGattService2 extends MyGattService {
final BlueZGattService service;
final BlueZGattService blueZService;
MyGattService2(this.service)
MyGattService2(this.blueZService)
: super(
hashCode: service.hashCode,
uuid: service.myUUID,
characteristics: service.myCharacteristics,
uuid: blueZService.myUUID,
characteristics: blueZService.myCharacteristics,
);
@override
List<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';
class MyPeripheral2 extends MyPeripheral {
final BlueZDevice device;
final BlueZDevice blueZDevice;
MyPeripheral2(this.device)
MyPeripheral2(this.blueZDevice)
: super(
hashCode: device.hashCode,
uuid: device.myUUID,
uuid: blueZDevice.myUUID,
);
}

View File

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

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
* Remove `BluetoothLowEnergy` class.

View File

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

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_manager.dart';
export 'src/bluetooth_low_energy_peer.dart';
export 'src/central_manager_event_args.dart';
export 'src/central_event_args.dart';
export 'src/central_manager.dart';
export 'src/central.dart';
export 'src/peripheral_manager_event_args.dart';
export 'src/peripheral_event_args.dart';
export 'src/peripheral_manager.dart';
export 'src/peripheral.dart';
export 'src/uuid.dart';
@ -28,11 +28,10 @@ export 'src/gatt_characteristic.dart';
export 'src/gatt_characteristic_property.dart';
export 'src/gatt_characteristic_write_type.dart';
export 'src/gatt_descriptor.dart';
export 'src/my_central_manager.dart';
export 'src/my_peripheral_manager.dart';
export 'src/my_object.dart';
export 'src/my_bluetooth_low_energy_peer.dart';
export 'src/my_central.dart';
export 'src/my_peripheral.dart';
export 'src/my_gatt_attribute.dart';
export 'src/my_gatt_service.dart';
export 'src/my_gatt_characteristic.dart';
export 'src/my_gatt_descriptor.dart';

View File

@ -5,12 +5,12 @@ import 'bluetooth_low_energy_state.dart';
/// The abstract base class that manages central and peripheral objects.
abstract class BluetoothLowEnergyManager implements LogController {
/// The current state of the manager.
BluetoothLowEnergyState get state;
/// Tells the managers state updated.
/// Tells the manager's state updated.
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged;
/// Sets up the manager.
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 'package:log_service/log_service.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'bluetooth_low_energy_manager.dart';
import 'central_manager_event_args.dart';
import 'central_event_args.dart';
import 'gatt_characteristic.dart';
import 'gatt_characteristic_write_type.dart';
import 'gatt_descriptor.dart';
import 'gatt_service.dart';
import 'my_central_manager.dart';
import 'peripheral.dart';
/// An object that scans for, discovers, connects to, and manages peripherals.
abstract class CentralManager extends BluetoothLowEnergyManager {
/// The instance of [CentralManager] to use.
static CentralManager get instance => MyCentralManager.instance;
///
/// Platform-specific implementations should implement this class to support [CentralManager].
abstract class CentralManager extends PlatformInterface
with LoggerProvider, LoggerController
implements BluetoothLowEnergyManager {
static final Object _token = Object();
static CentralManager? _instance;
/// The default instance of [CentralManager] to use.
static CentralManager get instance {
final instance = _instance;
if (instance == null) {
throw UnimplementedError(
'CentralManager is not implemented on this platform.');
}
return instance;
}
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [CentralManager] when
/// they register themselves.
static set instance(CentralManager instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
/// Constructs a [CentralManager].
CentralManager() : super(token: _token);
/// Tells the central manager discovered a peripheral while scanning for devices.
Stream<DiscoveredEventArgs> get discovered;
/// Tells that retrieving the specified peripheral's state changed.
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged;
/// Tells that retrieving the specified peripheral's connection lost.
Stream<ConnectionStateChangedEventArgs> get connectionStateChanged;
/// Tells that retrieving the specified characteristics value changed.
Stream<GattCharacteristicValueChangedEventArgs>
get characteristicValueChanged;
Stream<GattCharacteristicNotifiedEventArgs> get characteristicNotified;
/// Scans for peripherals that are advertising services.
Future<void> startDiscovery();
@ -36,12 +63,6 @@ abstract class CentralManager extends BluetoothLowEnergyManager {
/// Cancels an active or pending local connection to a 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.
Future<int> readRSSI(Peripheral peripheral);
@ -52,6 +73,8 @@ abstract class CentralManager extends BluetoothLowEnergyManager {
Future<Uint8List> readCharacteristic(GattCharacteristic characteristic);
/// Writes the value of a characteristic.
///
/// The maximum size of the value is 512, all bytes that exceed this size will be discarded.
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
@ -59,7 +82,7 @@ abstract class CentralManager extends BluetoothLowEnergyManager {
});
/// Sets notifications or indications for the value of a specified characteristic.
Future<void> notifyCharacteristic(
Future<void> setCharacteristicNotifyState(
GattCharacteristic characteristic, {
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_characteristic_property.dart';
import 'gatt_descriptor.dart';
@ -17,11 +19,13 @@ abstract class GattCharacteristic extends GattAttribute {
factory GattCharacteristic({
required UUID uuid,
required List<GattCharacteristicProperty> properties,
required Uint8List value,
required List<GattDescriptor> descriptors,
}) =>
MyGattCharacteristic(
uuid: uuid,
properties: properties,
value: value,
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 'my_object.dart';
import 'uuid.dart';
class MyCentral extends MyObject implements Central {
@override
final UUID uuid;
import 'my_bluetooth_low_energy_peer.dart';
class MyCentral extends MyBluetoothLowEnergyPeer implements Central {
MyCentral({
required super.hashCode,
required this.uuid,
required super.uuid,
});
@override
int get hashCode => uuid.hashCode;
@override
bool operator ==(Object other) {
return other is Central && other.uuid == uuid;
}
}

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_property.dart';
import 'my_gatt_attribute.dart';
import 'my_gatt_descriptor.dart';
import 'my_object.dart';
import 'uuid.dart';
class MyGattCharacteristic extends MyObject implements GattCharacteristic {
@override
final UUID uuid;
class MyGattCharacteristic extends MyGattAttribute
implements GattCharacteristic {
@override
final List<GattCharacteristicProperty> properties;
Uint8List? _value;
@override
final List<MyGattDescriptor> descriptors;
MyGattCharacteristic({
super.hashCode,
required this.uuid,
required super.uuid,
required this.properties,
Uint8List? value,
required this.descriptors,
});
}) : _value = value?.trimGATT();
Uint8List get value => _value ?? Uint8List.fromList([]);
set value(Uint8List value) {
_value = value.trimGATT();
}
}

View File

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

View File

@ -1,17 +1,13 @@
import 'gatt_service.dart';
import 'my_gatt_attribute.dart';
import 'my_gatt_characteristic.dart';
import 'my_object.dart';
import 'uuid.dart';
class MyGattService extends MyObject implements GattService {
@override
final UUID uuid;
class MyGattService extends MyGattAttribute implements GattService {
@override
final List<MyGattCharacteristic> characteristics;
MyGattService({
super.hashCode,
required this.uuid,
required super.uuid,
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 'uuid.dart';
class MyPeripheral extends MyObject implements Peripheral {
@override
final UUID uuid;
class MyPeripheral extends MyBluetoothLowEnergyPeer implements Peripheral {
MyPeripheral({
required super.hashCode,
required this.uuid,
required super.uuid,
});
@override
int get hashCode => uuid.hashCode;
@override
bool operator ==(Object other) {
return other is Peripheral && other.uuid == uuid;
}
}

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