feat: 支持外围设备接口,优化中心设备接口 (#18)
* 临时提交 * 临时提交 * 临时提交 * fix: 调整接口 * fix: 修复问题 * fix: 调整 iOS 实现 * fix: 添加注释 * fix: 修改预览版本号 * fix: 修复已知问题 * fix: 优化接口 * fix: 解决 32 位 UUID 报错问题 * fix: 修复问题 * fix: 修复依赖项 * fix: 移除多余代码 * fix: 修复已知问题 * fix: 修复问题 * fix: 修改版本号 * fix: 修复问题 * fix: 发布正式版本
This commit is contained in:
@ -1,3 +1,42 @@
|
||||
## 3.0.0
|
||||
|
||||
* Add `PeripheralManager` api.
|
||||
* Add `CentralManager#readRSSI` method.
|
||||
* Add `CentralManager.instance` api.
|
||||
* Add `PeripheralManager.instance` api.
|
||||
* Move `CentralController` to `CentralManager`.
|
||||
* Move `CentralState` to `BluetoothLowEnergyState`.
|
||||
* Move `CentralDiscoveredEventArgs` to `DiscoveredEventArgs`.
|
||||
* Move `Advertisement` class to `AdvertiseData` class.
|
||||
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
|
||||
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
|
||||
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
|
||||
* Fix known issues.
|
||||
|
||||
## 3.0.0-dev.4
|
||||
|
||||
* Move `Advertisement` class to `AdvertiseData` class.
|
||||
* Fix known issues.
|
||||
|
||||
## 3.0.0-dev.3
|
||||
|
||||
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
|
||||
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
|
||||
|
||||
## 3.0.0-dev.2
|
||||
|
||||
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
|
||||
* Add `CentralManager.instance` api.
|
||||
* Add `PeripheralManager.instance` api.
|
||||
|
||||
## 3.0.0-dev.1
|
||||
|
||||
* Add `PeripheralManager` api.
|
||||
* Add `CentralManager#readRSSI` method.
|
||||
* Move `CentralController` to `CentralManager`.
|
||||
* Move `CentralState` to `BluetoothLowEnergyState`.
|
||||
* Move `CentralDiscoveredEventArgs` to `DiscoveredEventArgs`.
|
||||
|
||||
## 2.2.1
|
||||
|
||||
* `Android` Fix the issue that `CentralController#getMaximumWriteLength` may throw.
|
||||
|
@ -4,12 +4,14 @@ A Flutter plugin for controlling the bluetooth low energy.
|
||||
|
||||
## Features
|
||||
|
||||
### CentralController
|
||||
### CentralManager
|
||||
|
||||
- [x] SetUp/TearDown central controller.
|
||||
- [x] Get/Listen central state.
|
||||
- [x] Set up the central manager.
|
||||
- [x] Get/Listen the state of the central manager.
|
||||
- [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.
|
||||
@ -17,6 +19,17 @@ A Flutter plugin for controlling the bluetooth low energy.
|
||||
- [x] Read/Write/Notify GATT characteristics.
|
||||
- [x] Read/Write GATT descriptors.
|
||||
|
||||
### PeripheralManager
|
||||
|
||||
- [x] Set up the peripheral manager.
|
||||
- [x] Get/Listen the state of the peripheral manager.
|
||||
- [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.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Add `bluetooth_low_energy` as a [dependency in your pubspec.yaml file](https://flutter.dev/using-packages/).
|
||||
@ -28,20 +41,26 @@ dependencies:
|
||||
|
||||
Remember to call `await CentralController.setUp()` before use any apis of this plugin.
|
||||
|
||||
*Note*: Bluetooth Low Energy doesn't work on emulators, so use physical devices which has bluetooth features for development.
|
||||
*Note:* Bluetooth Low Energy doesn't work on emulators, so use physical devices which has bluetooth features for development.
|
||||
|
||||
### Android
|
||||
|
||||
Make sure you have a `miniSdkVersion` with 21 or higher in your `android/app/build.gradle` file.
|
||||
|
||||
*Note:* Don't call `getMaximumWriteLength` immediately when connected to a peripheral after Android 13, the `onMtuChanged` callback maybe called with connection events after Android 13, and `getMaximumWriteLength` will call `requestMtu` will also triggered `onMtuChanged`, if you called this before the connection `onMtuChanged`, you will get a fake completion and cause all methods you called before the real `onMtuChanged` triggered will never complete!
|
||||
|
||||
### iOS and macOS
|
||||
|
||||
According to Apple's [documents](https://developer.apple.com/documentation/corebluetooth/), you must include the [`NSBluetoothAlwaysUsageDescription`](https://developer.apple.com/documentation/bundleresources/information_property_list/nsbluetoothalwaysusagedescription) on or after iOS 13, and include the [`NSBluetoothPeripheralUsageDescription`](https://developer.apple.com/documentation/bundleresources/information_property_list/nsbluetoothperipheralusagedescription) key before iOS 13.
|
||||
|
||||
The `PeripheralManager#startAdvertising` only support `name` and `serviceUUIDs`, see [the startAdvertising document](https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising)
|
||||
|
||||
### 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)
|
||||
|
||||
### 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...
|
||||
|
@ -14,6 +14,9 @@ void main() {
|
||||
}
|
||||
|
||||
void onStartUp() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await CentralManager.instance.setUp();
|
||||
await PeripheralManager.instance.setUp();
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
@ -48,8 +51,7 @@ class _MyAppState extends State<MyApp> {
|
||||
routes: {
|
||||
'peripheral': (context) {
|
||||
final route = ModalRoute.of(context);
|
||||
final eventArgs =
|
||||
route!.settings.arguments as CentralDiscoveredEventArgs;
|
||||
final eventArgs = route!.settings.arguments as DiscoveredEventArgs;
|
||||
return PeripheralView(
|
||||
eventArgs: eventArgs,
|
||||
);
|
||||
@ -67,26 +69,104 @@ class HomeView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _HomeViewState extends State<HomeView> {
|
||||
CentralController get centralController => CentralController.instance;
|
||||
late final ValueNotifier<CentralState> state;
|
||||
late final PageController controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = PageController();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: buildBody(context),
|
||||
bottomNavigationBar: buildBottomNavigationBar(context),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBody(BuildContext context) {
|
||||
return PageView.builder(
|
||||
controller: controller,
|
||||
itemBuilder: (context, i) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
return const ScannerView();
|
||||
case 1:
|
||||
return const AdvertiserView();
|
||||
default:
|
||||
throw ArgumentError.value(i);
|
||||
}
|
||||
},
|
||||
itemCount: 2,
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBottomNavigationBar(BuildContext context) {
|
||||
return ListenableBuilder(
|
||||
listenable: controller,
|
||||
builder: (context, child) {
|
||||
return BottomNavigationBar(
|
||||
onTap: (i) {
|
||||
const duration = Duration(milliseconds: 300);
|
||||
const curve = Curves.ease;
|
||||
controller.animateToPage(
|
||||
i,
|
||||
duration: duration,
|
||||
curve: curve,
|
||||
);
|
||||
},
|
||||
currentIndex: controller.page?.toInt() ?? 0,
|
||||
items: const [
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.radar),
|
||||
label: 'scanner',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.sensors),
|
||||
label: 'advertiser',
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
controller.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
CentralManager get centralManager => CentralManager.instance;
|
||||
|
||||
class ScannerView extends StatefulWidget {
|
||||
const ScannerView({super.key});
|
||||
|
||||
@override
|
||||
State<ScannerView> createState() => _ScannerViewState();
|
||||
}
|
||||
|
||||
class _ScannerViewState extends State<ScannerView> {
|
||||
late final ValueNotifier<BluetoothLowEnergyState> state;
|
||||
late final ValueNotifier<bool> discovering;
|
||||
late final ValueNotifier<List<CentralDiscoveredEventArgs>>
|
||||
discoveredEventArgs;
|
||||
late final ValueNotifier<List<DiscoveredEventArgs>> discoveredEventArgs;
|
||||
late final StreamSubscription stateChangedSubscription;
|
||||
late final StreamSubscription discoveredSubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
state = ValueNotifier(centralController.state);
|
||||
state = ValueNotifier(centralManager.state);
|
||||
discovering = ValueNotifier(false);
|
||||
discoveredEventArgs = ValueNotifier([]);
|
||||
stateChangedSubscription = centralController.stateChanged.listen(
|
||||
stateChangedSubscription = centralManager.stateChanged.listen(
|
||||
(eventArgs) {
|
||||
state.value = eventArgs.state;
|
||||
},
|
||||
);
|
||||
discoveredSubscription = centralController.discovered.listen(
|
||||
discoveredSubscription = centralManager.discovered.listen(
|
||||
(eventArgs) {
|
||||
final items = discoveredEventArgs.value;
|
||||
final i = items.indexWhere(
|
||||
@ -100,22 +180,6 @@ class _HomeViewState extends State<HomeView> {
|
||||
}
|
||||
},
|
||||
);
|
||||
setUp();
|
||||
}
|
||||
|
||||
void setUp() async {
|
||||
await centralController.setUp();
|
||||
state.value = centralController.state;
|
||||
}
|
||||
|
||||
Future<void> startDiscovery() async {
|
||||
await centralController.startDiscovery();
|
||||
discovering.value = true;
|
||||
}
|
||||
|
||||
Future<void> stopDiscovery() async {
|
||||
await centralController.stopDiscovery();
|
||||
discovering.value = false;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -128,7 +192,7 @@ class _HomeViewState extends State<HomeView> {
|
||||
|
||||
PreferredSizeWidget buildAppBar(BuildContext context) {
|
||||
return AppBar(
|
||||
title: const Text('Bluetooth LowEnergy'),
|
||||
title: const Text('Scanner'),
|
||||
actions: [
|
||||
ValueListenableBuilder(
|
||||
valueListenable: state,
|
||||
@ -137,7 +201,7 @@ class _HomeViewState extends State<HomeView> {
|
||||
valueListenable: discovering,
|
||||
builder: (context, discovering, child) {
|
||||
return TextButton(
|
||||
onPressed: state == CentralState.poweredOn
|
||||
onPressed: state == BluetoothLowEnergyState.poweredOn
|
||||
? () async {
|
||||
if (discovering) {
|
||||
await stopDiscovery();
|
||||
@ -158,13 +222,23 @@ class _HomeViewState extends State<HomeView> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> startDiscovery() async {
|
||||
await centralManager.startDiscovery();
|
||||
discovering.value = true;
|
||||
}
|
||||
|
||||
Future<void> stopDiscovery() async {
|
||||
await centralManager.stopDiscovery();
|
||||
discovering.value = false;
|
||||
}
|
||||
|
||||
Widget buildBody(BuildContext context) {
|
||||
return ValueListenableBuilder(
|
||||
valueListenable: discoveredEventArgs,
|
||||
builder: (context, discoveredEventArgs, child) {
|
||||
// final items = discoveredEventArgs;
|
||||
final items = discoveredEventArgs
|
||||
.where((eventArgs) => eventArgs.advertisement.name != null)
|
||||
.where((eventArgs) => eventArgs.advertiseData.name != null)
|
||||
.toList();
|
||||
return ListView.separated(
|
||||
itemBuilder: (context, i) {
|
||||
@ -172,8 +246,8 @@ class _HomeViewState extends State<HomeView> {
|
||||
final item = items[i];
|
||||
final uuid = item.peripheral.uuid;
|
||||
final rssi = item.rssi;
|
||||
final advertisement = item.advertisement;
|
||||
final name = advertisement.name;
|
||||
final advertiseData = item.advertiseData;
|
||||
final name = advertiseData.name;
|
||||
return ListTile(
|
||||
onTap: () async {
|
||||
final discovering = this.discovering.value;
|
||||
@ -200,7 +274,7 @@ class _HomeViewState extends State<HomeView> {
|
||||
clipBehavior: Clip.antiAlias,
|
||||
builder: (context) {
|
||||
final manufacturerSpecificData =
|
||||
advertisement.manufacturerSpecificData;
|
||||
advertiseData.manufacturerSpecificData;
|
||||
return ListView.separated(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
@ -221,11 +295,10 @@ class _HomeViewState extends State<HomeView> {
|
||||
],
|
||||
);
|
||||
} else {
|
||||
final entry = manufacturerSpecificData.entries
|
||||
.elementAt(i - 1);
|
||||
final id =
|
||||
'0x${entry.key.toRadixString(16).padLeft(4, '0')}';
|
||||
final value = hex.encode(entry.value);
|
||||
'0x${manufacturerSpecificData!.id.toRadixString(16).padLeft(4, '0')}';
|
||||
final value =
|
||||
hex.encode(manufacturerSpecificData.data);
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
@ -242,7 +315,7 @@ class _HomeViewState extends State<HomeView> {
|
||||
separatorBuilder: (context, i) {
|
||||
return const Divider();
|
||||
},
|
||||
itemCount: manufacturerSpecificData.length + 1,
|
||||
itemCount: manufacturerSpecificData == null ? 1 : 2,
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -274,7 +347,6 @@ class _HomeViewState extends State<HomeView> {
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
centralController.tearDown().ignore();
|
||||
stateChangedSubscription.cancel();
|
||||
discoveredSubscription.cancel();
|
||||
state.dispose();
|
||||
@ -284,7 +356,7 @@ class _HomeViewState extends State<HomeView> {
|
||||
}
|
||||
|
||||
class PeripheralView extends StatefulWidget {
|
||||
final CentralDiscoveredEventArgs eventArgs;
|
||||
final DiscoveredEventArgs eventArgs;
|
||||
|
||||
const PeripheralView({
|
||||
super.key,
|
||||
@ -296,9 +368,8 @@ class PeripheralView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _PeripheralViewState extends State<PeripheralView> {
|
||||
CentralController get centralController => CentralController.instance;
|
||||
late final ValueNotifier<bool> state;
|
||||
late final CentralDiscoveredEventArgs eventArgs;
|
||||
late final DiscoveredEventArgs eventArgs;
|
||||
late final ValueNotifier<List<GattService>> services;
|
||||
late final ValueNotifier<List<GattCharacteristic>> characteristics;
|
||||
late final ValueNotifier<GattService?> service;
|
||||
@ -323,7 +394,7 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
maximumWriteLength = ValueNotifier(20);
|
||||
logs = ValueNotifier([]);
|
||||
writeController = TextEditingController();
|
||||
stateChangedSubscription = centralController.peripheralStateChanged.listen(
|
||||
stateChangedSubscription = centralManager.peripheralStateChanged.listen(
|
||||
(eventArgs) {
|
||||
if (eventArgs.peripheral != this.eventArgs.peripheral) {
|
||||
return;
|
||||
@ -339,8 +410,7 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
}
|
||||
},
|
||||
);
|
||||
valueChangedSubscription =
|
||||
centralController.characteristicValueChanged.listen(
|
||||
valueChangedSubscription = centralManager.characteristicValueChanged.listen(
|
||||
(eventArgs) {
|
||||
final characteristic = this.characteristic.value;
|
||||
if (eventArgs.characteristic != characteristic) {
|
||||
@ -362,7 +432,7 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
onWillPop: () async {
|
||||
if (state.value) {
|
||||
final peripheral = eventArgs.peripheral;
|
||||
await centralController.disconnect(peripheral);
|
||||
await centralManager.disconnect(peripheral);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
@ -374,7 +444,7 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
}
|
||||
|
||||
PreferredSizeWidget buildAppBar(BuildContext context) {
|
||||
final title = eventArgs.advertisement.name ?? '<EMPTY NAME>';
|
||||
final title = eventArgs.advertiseData.name ?? '<EMPTY NAME>';
|
||||
return AppBar(
|
||||
title: Text(title),
|
||||
actions: [
|
||||
@ -385,12 +455,11 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
onPressed: () async {
|
||||
final peripheral = eventArgs.peripheral;
|
||||
if (state) {
|
||||
await centralController.disconnect(peripheral);
|
||||
await centralManager.disconnect(peripheral);
|
||||
} else {
|
||||
await centralController.connect(peripheral);
|
||||
await centralController.discoverGATT(peripheral);
|
||||
await centralManager.connect(peripheral);
|
||||
services.value =
|
||||
await centralController.getServices(peripheral);
|
||||
await centralManager.discoverGATT(peripheral);
|
||||
}
|
||||
},
|
||||
child: Text(state ? 'DISCONNECT' : 'CONNECT'),
|
||||
@ -436,8 +505,7 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
characteristics.value =
|
||||
await centralController.getCharacteristics(service);
|
||||
characteristics.value = service.characteristics;
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -563,7 +631,7 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
onPressed: state
|
||||
? () async {
|
||||
maximumWriteLength.value =
|
||||
await centralController.getMaximumWriteLength(
|
||||
await centralManager.getMaximumWriteLength(
|
||||
eventArgs.peripheral,
|
||||
type: writeType.value,
|
||||
);
|
||||
@ -579,6 +647,14 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
},
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
final rssi =
|
||||
await centralManager.readRSSI(eventArgs.peripheral);
|
||||
print('RSSI: $rssi');
|
||||
},
|
||||
icon: const Icon(Icons.signal_wifi_4_bar),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
@ -627,7 +703,7 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
TextButton(
|
||||
onPressed: characteristic != null && canNotify
|
||||
? () async {
|
||||
await centralController.notifyCharacteristic(
|
||||
await centralManager.notifyCharacteristic(
|
||||
characteristic,
|
||||
state: true,
|
||||
);
|
||||
@ -638,7 +714,7 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
TextButton(
|
||||
onPressed: characteristic != null && canRead
|
||||
? () async {
|
||||
final value = await centralController
|
||||
final value = await centralManager
|
||||
.readCharacteristic(characteristic);
|
||||
const type = LogType.read;
|
||||
final log = Log(type, value);
|
||||
@ -653,7 +729,7 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
final text = writeController.text;
|
||||
final elements = utf8.encode(text);
|
||||
final value = Uint8List.fromList(elements);
|
||||
await centralController.writeCharacteristic(
|
||||
await centralManager.writeCharacteristic(
|
||||
characteristic,
|
||||
value: value,
|
||||
type: GattCharacteristicWriteType
|
||||
@ -694,12 +770,286 @@ class _PeripheralViewState extends State<PeripheralView> {
|
||||
}
|
||||
}
|
||||
|
||||
PeripheralManager get peripheralManager => PeripheralManager.instance;
|
||||
|
||||
class AdvertiserView extends StatefulWidget {
|
||||
const AdvertiserView({super.key});
|
||||
|
||||
@override
|
||||
State<AdvertiserView> createState() => _AdvertiserViewState();
|
||||
}
|
||||
|
||||
class _AdvertiserViewState extends State<AdvertiserView> {
|
||||
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,
|
||||
id,
|
||||
offset,
|
||||
status,
|
||||
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,
|
||||
id,
|
||||
offset,
|
||||
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,
|
||||
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 advertiseData = AdvertiseData(
|
||||
name: 'flutter',
|
||||
manufacturerSpecificData: ManufacturerSpecificData(
|
||||
id: 0x2e19,
|
||||
data: Uint8List.fromList([0x01, 0x02, 0x03]),
|
||||
),
|
||||
);
|
||||
await peripheralManager.startAdvertising(advertiseData);
|
||||
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;
|
||||
final Uint8List value;
|
||||
final String? detail;
|
||||
|
||||
Log(this.type, this.value) : time = DateTime.now();
|
||||
Log(
|
||||
this.type,
|
||||
this.value, [
|
||||
this.detail,
|
||||
]) : time = DateTime.now();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
@ -707,7 +1057,11 @@ class Log {
|
||||
final formatter = DateFormat.Hms();
|
||||
final time = formatter.format(this.time);
|
||||
final message = hex.encode(value);
|
||||
return '[$type]$time: $message';
|
||||
if (detail == null) {
|
||||
return '[$type]$time: $message';
|
||||
} else {
|
||||
return '[$type]$time: $message /* $detail */';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,47 +23,39 @@ packages:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "2.2.1"
|
||||
version: "3.0.0"
|
||||
bluetooth_low_energy_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bluetooth_low_energy_android
|
||||
sha256: "2098167f30a05cff40313fd49c459d326590b54dc6ea953cbe7ff145ce940e04"
|
||||
sha256: "227468652811e769d7ff8836aa5c3f7bd0455246785c3eaf0fab2b3c31daed16"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
version: "3.0.0"
|
||||
bluetooth_low_energy_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bluetooth_low_energy_darwin
|
||||
sha256: "876f2d4a288739091296bc22ea574e64451f592154e4201b5999f174120a8b55"
|
||||
sha256: f19903e1ad14da6a16de57996ca808064cb690add8b0aecc6ffecdf8da6f7eab
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "3.0.0"
|
||||
bluetooth_low_energy_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bluetooth_low_energy_linux
|
||||
sha256: dc3062991e0a408941829326f0a3b2e1244a8138dca7f6c4c6beadbc26deecb2
|
||||
sha256: "31a23704a4b34e7f6cea61d94c24b3e1c6698928645aaeaf1af947e4870996d7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "3.0.0"
|
||||
bluetooth_low_energy_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bluetooth_low_energy_platform_interface
|
||||
sha256: eecaa2c37ebd536339c292d5566cf4360c9ea3861188342791eb3a0cf65cc64c
|
||||
sha256: "200e686247808591b6d3e355642ba296f0f651466c72efdd701be34116971473"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
bluetooth_low_energy_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bluetooth_low_energy_windows
|
||||
sha256: a1b5d5d5ad935f15618e6a86334c1ec682c40ffbe6a78173c3f19b67ff0edcfb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "3.0.0"
|
||||
bluez:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -366,14 +358,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "9e82a402b7f3d518fb9c02d0e9ae45952df31b9bf34d77baf19da2de03fc2aaa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.7"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1 +1,8 @@
|
||||
export 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
export 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart'
|
||||
hide
|
||||
MyObject,
|
||||
MyCentral,
|
||||
MyPeripheral,
|
||||
MyGattService,
|
||||
MyGattCharacteristic,
|
||||
MyGattDescriptor;
|
||||
|
@ -1,20 +1,19 @@
|
||||
name: bluetooth_low_energy
|
||||
description: A Flutter plugin for controlling the bluetooth low energy.
|
||||
version: 2.2.1
|
||||
version: 3.0.0
|
||||
homepage: https://github.com/yanshouwang/bluetooth_low_energy
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
flutter: ">=3.3.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
bluetooth_low_energy_platform_interface: ^2.2.0
|
||||
bluetooth_low_energy_android: ^2.2.1
|
||||
bluetooth_low_energy_darwin: ^2.2.0
|
||||
bluetooth_low_energy_linux: ^2.2.0
|
||||
bluetooth_low_energy_windows: ^2.2.0
|
||||
bluetooth_low_energy_platform_interface: ^3.0.0
|
||||
bluetooth_low_energy_android: ^3.0.0
|
||||
bluetooth_low_energy_darwin: ^3.0.0
|
||||
bluetooth_low_energy_linux: ^3.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@ -32,5 +31,3 @@ flutter:
|
||||
default_package: bluetooth_low_energy_darwin
|
||||
linux:
|
||||
default_package: bluetooth_low_energy_linux
|
||||
windows:
|
||||
default_package: bluetooth_low_energy_windows
|
||||
|
@ -1,3 +1,48 @@
|
||||
## 3.0.0
|
||||
|
||||
* Add `PeripheralManager` api.
|
||||
* Add `CentralManager#readRSSI` method.
|
||||
* Add `CentralManager.instance` api.
|
||||
* Add `PeripheralManager.instance` api.
|
||||
* Move `CentralController` to `CentralManager`.
|
||||
* Move `CentralState` to `BluetoothLowEnergyState`.
|
||||
* Move `CentralDiscoveredEventArgs` to `DiscoveredEventArgs`.
|
||||
* Move `Advertisement` class to `AdvertiseData` class.
|
||||
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
|
||||
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
|
||||
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
|
||||
* Fix known issues.
|
||||
|
||||
## 3.0.0-dev.6
|
||||
|
||||
* Add default `CCCD` to GATT characteristic for notify and indicate.
|
||||
* Fix the issue that callbacks must run on ui thread.
|
||||
* Change requested MTU from 512 to 517 when get the maximum write length of characteristic.
|
||||
|
||||
## 3.0.0-dev.5
|
||||
|
||||
* Fix the issue that the `BLUETOOTH_ADVERTISE` permission is not requested.
|
||||
|
||||
## 3.0.0-dev.4
|
||||
|
||||
* Move `Advertisement` class to `AdvertiseData` class.
|
||||
* Fix known issues.
|
||||
|
||||
## 3.0.0-dev.3
|
||||
|
||||
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
|
||||
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
|
||||
|
||||
## 3.0.0-dev.2
|
||||
|
||||
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
|
||||
* Add `CentralManager.instance` api.
|
||||
* Add `PeripheralManager.instance` api.
|
||||
|
||||
## 3.0.0-dev.1
|
||||
|
||||
* Implement new api.
|
||||
|
||||
## 2.2.1
|
||||
|
||||
* Fix the issue that `CentralController#getMaximumWriteLength` may throw.
|
||||
|
@ -9,6 +9,7 @@
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
|
||||
android:maxSdkVersion="30" />
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
</manifest>
|
@ -6,33 +6,39 @@ import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||
|
||||
/** BluetoothLowEnergyAndroid */
|
||||
class BluetoothLowEnergyAndroid : FlutterPlugin, ActivityAware {
|
||||
private lateinit var centralController: MyCentralController
|
||||
private lateinit var centralManager: MyCentralManager
|
||||
private lateinit var peripheralManager: MyPeripheralManager
|
||||
|
||||
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
val context = binding.applicationContext
|
||||
val binaryMessenger = binding.binaryMessenger
|
||||
centralController = MyCentralController(context, binaryMessenger)
|
||||
MyCentralControllerHostApi.setUp(binaryMessenger, centralController)
|
||||
centralManager = MyCentralManager(context, binaryMessenger)
|
||||
peripheralManager = MyPeripheralManager(context, binaryMessenger)
|
||||
MyCentralManagerHostApi.setUp(binaryMessenger, centralManager)
|
||||
MyPeripheralManagerHostApi.setUp(binaryMessenger, peripheralManager)
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
val binaryMessenger = binding.binaryMessenger
|
||||
MyCentralControllerHostApi.setUp(binaryMessenger, null)
|
||||
MyCentralManagerHostApi.setUp(binaryMessenger, null)
|
||||
MyPeripheralManagerHostApi.setUp(binaryMessenger, null)
|
||||
}
|
||||
|
||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
centralController.onAttachedToActivity(binding)
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {
|
||||
centralController.onDetachedFromActivity()
|
||||
}
|
||||
|
||||
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
||||
centralController.onAttachedToActivity(binding)
|
||||
centralManager.onAttachedToActivity(binding)
|
||||
peripheralManager.onAttachedToActivity(binding)
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivity() {
|
||||
centralController.onDetachedFromActivity()
|
||||
centralManager.onDetachedFromActivity()
|
||||
peripheralManager.onDetachedFromActivity()
|
||||
}
|
||||
|
||||
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
||||
onAttachedToActivity(binding)
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {
|
||||
onDetachedFromActivity()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package dev.yanshouwang.bluetooth_low_energy_android
|
||||
|
||||
import android.bluetooth.le.AdvertiseCallback
|
||||
import android.bluetooth.le.AdvertiseSettings
|
||||
|
||||
class MyAdvertiseCallback(private val peripheralManager: MyPeripheralManager) : AdvertiseCallback() {
|
||||
override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
|
||||
super.onStartSuccess(settingsInEffect)
|
||||
peripheralManager.onStartSuccess(settingsInEffect)
|
||||
}
|
||||
|
||||
override fun onStartFailure(errorCode: Int) {
|
||||
super.onStartFailure(errorCode)
|
||||
peripheralManager.onStartFailure(errorCode)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,233 @@
|
||||
package dev.yanshouwang.bluetooth_low_energy_android
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
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 {
|
||||
return when (this) {
|
||||
BluetoothAdapter.STATE_ON -> MyBluetoothLowEnergyStateArgs.POWEREDON
|
||||
else -> MyBluetoothLowEnergyStateArgs.POWEREDOFF
|
||||
}
|
||||
}
|
||||
|
||||
fun BluetoothDevice.toPeripheralArgs(): MyPeripheralArgs {
|
||||
val hashCodeArgs = hashCode().toLong()
|
||||
val uuid = this.uuid.toString()
|
||||
return MyPeripheralArgs(hashCodeArgs, uuid)
|
||||
}
|
||||
|
||||
fun BluetoothDevice.toCentralArgs(): MyCentralArgs {
|
||||
val hashCodeArgs = hashCode().toLong()
|
||||
val uuid = this.uuid.toString()
|
||||
return MyCentralArgs(hashCodeArgs, uuid)
|
||||
}
|
||||
|
||||
val BluetoothDevice.uuid: UUID
|
||||
get() {
|
||||
val node = address.filter { char -> char != ':' }
|
||||
// We don't know the timestamp of the bluetooth device, use nil UUID as prefix.
|
||||
return UUID.fromString("00000000-0000-0000-0000-$node")
|
||||
}
|
||||
|
||||
val ScanResult.advertiseDataArgs: MyAdvertiseDataArgs
|
||||
get() {
|
||||
val record = scanRecord
|
||||
return if (record == null) {
|
||||
val nameArgs = null
|
||||
val serviceUUIDsArgs = emptyList<String?>()
|
||||
val serviceDataArgs = emptyMap<String?, ByteArray>()
|
||||
val manufacturerSpecificDataArgs = null
|
||||
MyAdvertiseDataArgs(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()
|
||||
MyAdvertiseDataArgs(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 MyAdvertiseDataArgs.toAdvertiseData(adapter: BluetoothAdapter): AdvertiseData {
|
||||
val advertiseDataBuilder = AdvertiseData.Builder()
|
||||
if (nameArgs == null) {
|
||||
advertiseDataBuilder.setIncludeDeviceName(false)
|
||||
} else {
|
||||
adapter.name = nameArgs
|
||||
advertiseDataBuilder.setIncludeDeviceName(true)
|
||||
}
|
||||
for (serviceUuidArgs in serviceUUIDsArgs) {
|
||||
val serviceUUID = ParcelUuid.fromString(serviceUuidArgs)
|
||||
advertiseDataBuilder.addServiceUuid(serviceUUID)
|
||||
}
|
||||
for (entry in serviceDataArgs) {
|
||||
val serviceDataUUID = ParcelUuid.fromString(entry.key as String)
|
||||
val serviceData = entry.value as ByteArray
|
||||
advertiseDataBuilder.addServiceData(serviceDataUUID, serviceData)
|
||||
}
|
||||
if (manufacturerSpecificDataArgs != null) {
|
||||
val manufacturerId = manufacturerSpecificDataArgs.idArgs.toInt()
|
||||
val manufacturerSpecificData = manufacturerSpecificDataArgs.dataArgs
|
||||
advertiseDataBuilder.addManufacturerData(manufacturerId, manufacturerSpecificData)
|
||||
}
|
||||
return advertiseDataBuilder.build()
|
||||
}
|
||||
|
||||
fun BluetoothGattService.toManufacturerSpecificDataArgs(characteristicsArgs: List<MyGattCharacteristicArgs>): MyGattServiceArgs {
|
||||
val hashCodeArgs = hashCode().toLong()
|
||||
val uuidArgs = this.uuid.toString()
|
||||
return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs)
|
||||
}
|
||||
|
||||
fun BluetoothGattCharacteristic.toManufacturerSpecificDataArgs(descriptorsArgs: List<MyGattDescriptorArgs>): MyGattCharacteristicArgs {
|
||||
val hashCodeArgs = hashCode().toLong()
|
||||
val uuidArgs = this.uuid.toString()
|
||||
return MyGattCharacteristicArgs(hashCodeArgs, uuidArgs, propertyNumbersArgs, descriptorsArgs)
|
||||
}
|
||||
|
||||
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 BluetoothGattDescriptor.toManufacturerSpecificDataArgs(): MyGattDescriptorArgs {
|
||||
val hashCodeArgs = hashCode().toLong()
|
||||
val uuidArgs = this.uuid.toString()
|
||||
return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, null)
|
||||
}
|
||||
|
||||
fun MyGattServiceArgs.toService(): BluetoothGattService {
|
||||
val uuid = UUID.fromString(uuidArgs)
|
||||
val serviceType = BluetoothGattService.SERVICE_TYPE_PRIMARY
|
||||
return BluetoothGattService(uuid, serviceType)
|
||||
}
|
||||
|
||||
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 {
|
||||
return when (this) {
|
||||
MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
|
||||
MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
|
||||
}
|
||||
}
|
@ -7,32 +7,39 @@ import android.bluetooth.BluetoothGattDescriptor
|
||||
import android.os.Build
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
class MyBluetoothGattCallback(private val myCentralController: MyCentralController, private val executor: Executor) : BluetoothGattCallback() {
|
||||
class MyBluetoothGattCallback(private val centralManager: MyCentralManager, private val executor: Executor) : BluetoothGattCallback() {
|
||||
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
||||
super.onConnectionStateChange(gatt, status, newState)
|
||||
executor.execute {
|
||||
myCentralController.onConnectionStateChange(gatt, status, newState)
|
||||
centralManager.onConnectionStateChange(gatt, status, newState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
|
||||
super.onMtuChanged(gatt, mtu, status)
|
||||
executor.execute {
|
||||
myCentralController.onMtuChanged(gatt, mtu, status)
|
||||
centralManager.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)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
||||
super.onServicesDiscovered(gatt, status)
|
||||
executor.execute {
|
||||
myCentralController.onServicesDiscovered(gatt, status)
|
||||
centralManager.onServicesDiscovered(gatt, status)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray, status: Int) {
|
||||
super.onCharacteristicRead(gatt, characteristic, value, status)
|
||||
executor.execute {
|
||||
myCentralController.onCharacteristicRead(characteristic, status, value)
|
||||
centralManager.onCharacteristicRead(characteristic, status, value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,21 +51,21 @@ class MyBluetoothGattCallback(private val myCentralController: MyCentralControll
|
||||
}
|
||||
val value = characteristic.value
|
||||
executor.execute {
|
||||
myCentralController.onCharacteristicRead(characteristic, status, value)
|
||||
centralManager.onCharacteristicRead(characteristic, status, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
|
||||
super.onCharacteristicWrite(gatt, characteristic, status)
|
||||
executor.execute {
|
||||
myCentralController.onCharacteristicWrite(characteristic, status)
|
||||
centralManager.onCharacteristicWrite(characteristic, status)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) {
|
||||
super.onCharacteristicChanged(gatt, characteristic, value)
|
||||
executor.execute {
|
||||
myCentralController.onCharacteristicChanged(characteristic, value)
|
||||
centralManager.onCharacteristicChanged(characteristic, value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,14 +77,14 @@ class MyBluetoothGattCallback(private val myCentralController: MyCentralControll
|
||||
}
|
||||
val value = characteristic.value
|
||||
executor.execute {
|
||||
myCentralController.onCharacteristicChanged(characteristic, value)
|
||||
centralManager.onCharacteristicChanged(characteristic, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) {
|
||||
super.onDescriptorRead(gatt, descriptor, status, value)
|
||||
executor.execute {
|
||||
myCentralController.onDescriptorRead(descriptor, status, value)
|
||||
centralManager.onDescriptorRead(descriptor, status, value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,14 +96,14 @@ class MyBluetoothGattCallback(private val myCentralController: MyCentralControll
|
||||
}
|
||||
val value = descriptor.value
|
||||
executor.execute {
|
||||
myCentralController.onDescriptorRead(descriptor, status, value)
|
||||
centralManager.onDescriptorRead(descriptor, status, value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
|
||||
super.onDescriptorWrite(gatt, descriptor, status)
|
||||
executor.execute {
|
||||
myCentralController.onDescriptorWrite(descriptor, status)
|
||||
centralManager.onDescriptorWrite(descriptor, status)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package dev.yanshouwang.bluetooth_low_energy_android
|
||||
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import android.bluetooth.BluetoothGattDescriptor
|
||||
import android.bluetooth.BluetoothGattServerCallback
|
||||
import android.bluetooth.BluetoothGattService
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
class MyBluetoothGattServerCallback(private val peripheralManager: MyPeripheralManager, private val executor: Executor) : BluetoothGattServerCallback() {
|
||||
override fun onServiceAdded(status: Int, service: BluetoothGattService) {
|
||||
super.onServiceAdded(status, service)
|
||||
executor.execute {
|
||||
peripheralManager.onServiceAdded(status, service)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMtuChanged(device: BluetoothDevice, mtu: Int) {
|
||||
super.onMtuChanged(device, mtu)
|
||||
executor.execute {
|
||||
peripheralManager.onMtuChanged(device, mtu)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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 onNotificationSent(device: BluetoothDevice, status: Int) {
|
||||
super.onNotificationSent(device, status)
|
||||
executor.execute {
|
||||
peripheralManager.onNotificationSent(device, status)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package dev.yanshouwang.bluetooth_low_energy_android
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothManager
|
||||
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 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
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityPluginBinding
|
||||
|
||||
protected val unsupported = !context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
|
||||
protected val executor = ContextCompat.getMainExecutor(context) as Executor
|
||||
|
||||
protected val manager get() = ContextCompat.getSystemService(context, 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) }
|
||||
|
||||
fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
binding.addRequestPermissionsResultListener(listener)
|
||||
this.binding = binding
|
||||
}
|
||||
|
||||
fun onDetachedFromActivity() {
|
||||
binding.removeRequestPermissionsResultListener(listener)
|
||||
}
|
||||
|
||||
protected fun authorize(permissions: Array<String>) {
|
||||
val activity = binding.activity
|
||||
ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE)
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun register() {
|
||||
val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
|
||||
context.registerReceiver(receiver, filter)
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
protected open fun unregister() {
|
||||
context.unregisterReceiver(receiver)
|
||||
}
|
||||
|
||||
abstract fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean
|
||||
abstract fun onReceive(intent: Intent)
|
||||
}
|
@ -4,8 +4,8 @@ import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
class MyBroadcastReceiver(private val myCentralController: MyCentralController) : BroadcastReceiver() {
|
||||
class MyBroadcastReceiver(private val bluetoothLowEnergyManager: MyBluetoothLowEnergyManager) : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
myCentralController.onReceive(intent)
|
||||
bluetoothLowEnergyManager.onReceive(intent)
|
||||
}
|
||||
}
|
||||
|
@ -1,745 +0,0 @@
|
||||
package dev.yanshouwang.bluetooth_low_energy_android
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.bluetooth.BluetoothGatt
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import android.bluetooth.BluetoothGattDescriptor
|
||||
import android.bluetooth.BluetoothGattService
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.bluetooth.BluetoothProfile
|
||||
import android.bluetooth.BluetoothStatusCodes
|
||||
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.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.util.SparseArray
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import java.util.UUID
|
||||
|
||||
class MyCentralController(private val context: Context, binaryMessenger: BinaryMessenger) : MyCentralControllerHostApi {
|
||||
companion object {
|
||||
// const val DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xff.toByte()
|
||||
private const val REQUEST_CODE = 443
|
||||
|
||||
// private val UUID_HEART_RATE_MEASUREMENT = UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb")
|
||||
private val UUID_CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityPluginBinding
|
||||
|
||||
private val manager = ContextCompat.getSystemService(context, BluetoothManager::class.java) as BluetoothManager
|
||||
private val adapter = manager.adapter
|
||||
private val scanner = adapter.bluetoothLeScanner
|
||||
private val executor = ContextCompat.getMainExecutor(context)
|
||||
|
||||
private val myApi = MyCentralControllerFlutterApi(binaryMessenger)
|
||||
private val myRequestPermissionResultListener = MyRequestPermissionResultListener(this)
|
||||
private val myBroadcastReceiver = MyBroadcastReceiver(this)
|
||||
private val myScanCallback = MyScanCallback(this)
|
||||
private val myBluetoothGattCallback = MyBluetoothGattCallback(this, executor)
|
||||
|
||||
private val cachedDevices = mutableMapOf<Int, BluetoothDevice>()
|
||||
private val cachedGATTs = mutableMapOf<Int, BluetoothGatt>()
|
||||
private val cachedServices = mutableMapOf<Int, Map<Int, BluetoothGattService>>()
|
||||
private val cachedCharacteristics = mutableMapOf<Int, Map<Int, BluetoothGattCharacteristic>>()
|
||||
private val cachedDescriptors = mutableMapOf<Int, Map<Int, BluetoothGattDescriptor>>()
|
||||
|
||||
private var registered = false
|
||||
private var discovering = false
|
||||
|
||||
private var setUpCallback: ((Result<MyCentralControllerArgs>) -> Unit)? = null
|
||||
private var startDiscoveryCallback: ((Result<Unit>) -> Unit)? = null
|
||||
private val connectCallbacks = mutableMapOf<Int, (Result<Unit>) -> Unit>()
|
||||
private val disconnectCallbacks = mutableMapOf<Int, (Result<Unit>) -> Unit>()
|
||||
private val getMaximumWriteLengthCallbacks = mutableMapOf<Int, (Result<Long>) -> Unit>()
|
||||
private val discoverGattCallbacks = mutableMapOf<Int, (Result<Unit>) -> Unit>()
|
||||
private val readCharacteristicCallbacks = mutableMapOf<Int, (Result<ByteArray>) -> Unit>()
|
||||
private val writeCharacteristicCallbacks = mutableMapOf<Int, (Result<Unit>) -> Unit>()
|
||||
private val readDescriptorCallbacks = mutableMapOf<Int, (Result<ByteArray>) -> Unit>()
|
||||
private val writeDescriptorCallbacks = mutableMapOf<Int, (Result<Unit>) -> Unit>()
|
||||
|
||||
override fun setUp(callback: (Result<MyCentralControllerArgs>) -> Unit) {
|
||||
try {
|
||||
val available = context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
|
||||
if (!available) {
|
||||
val stateNumber = MyCentralStateArgs.UNSUPPORTED.raw.toLong()
|
||||
val args = MyCentralControllerArgs(stateNumber)
|
||||
callback(Result.success(args))
|
||||
}
|
||||
val unfinishedCallback = setUpCallback
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
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)
|
||||
}
|
||||
val activity = binding.activity
|
||||
ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE)
|
||||
setUpCallback = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
if (registered) {
|
||||
unregister()
|
||||
}
|
||||
if (discovering) {
|
||||
stopDiscovery()
|
||||
}
|
||||
for (gatt in cachedGATTs.values) {
|
||||
gatt.disconnect()
|
||||
}
|
||||
cachedDevices.clear()
|
||||
cachedGATTs.clear()
|
||||
cachedServices.clear()
|
||||
cachedCharacteristics.clear()
|
||||
cachedDescriptors.clear()
|
||||
}
|
||||
|
||||
private fun register() {
|
||||
val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
|
||||
context.registerReceiver(myBroadcastReceiver, filter)
|
||||
registered = true
|
||||
}
|
||||
|
||||
private fun unregister() {
|
||||
context.unregisterReceiver(myBroadcastReceiver)
|
||||
registered = false
|
||||
}
|
||||
|
||||
override fun startDiscovery(callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
val unfinishedCallback = startDiscoveryCallback
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val filters = emptyList<ScanFilter>()
|
||||
val settings = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build()
|
||||
scanner.startScan(filters, settings, myScanCallback)
|
||||
executor.execute {
|
||||
val finishedCallback = startDiscoveryCallback ?: return@execute
|
||||
startDiscoveryCallback = null
|
||||
finishedCallback(Result.success(Unit))
|
||||
}
|
||||
startDiscoveryCallback = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopDiscovery() {
|
||||
scanner.stopScan(myScanCallback)
|
||||
discovering = false
|
||||
}
|
||||
|
||||
override fun connect(myPeripheralKey: Long, callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
val deviceKey = myPeripheralKey.toInt()
|
||||
val unfinishedCallback = connectCallbacks[deviceKey]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val device = cachedDevices[deviceKey] as BluetoothDevice
|
||||
val autoConnect = false
|
||||
cachedGATTs[deviceKey] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val transport = BluetoothDevice.TRANSPORT_LE
|
||||
device.connectGatt(context, autoConnect, myBluetoothGattCallback, transport)
|
||||
} else {
|
||||
device.connectGatt(context, autoConnect, myBluetoothGattCallback)
|
||||
}
|
||||
connectCallbacks[deviceKey] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun disconnect(myPeripheralKey: Long, callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
val deviceKey = myPeripheralKey.toInt()
|
||||
val unfinishedCallback = disconnectCallbacks[deviceKey]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
|
||||
gatt.disconnect()
|
||||
disconnectCallbacks[deviceKey] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMaximumWriteLength(myPeripheralKey: Long, callback: (Result<Long>) -> Unit) {
|
||||
try {
|
||||
val deviceKey = myPeripheralKey.toInt()
|
||||
val unfinishedCallback = getMaximumWriteLengthCallbacks[deviceKey]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
|
||||
val requesting = gatt.requestMtu(512)
|
||||
if (!requesting) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
getMaximumWriteLengthCallbacks[deviceKey] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun discoverGATT(myPeripheralKey: Long, callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
val deviceKey = myPeripheralKey.toInt()
|
||||
val unfinishedCallback = discoverGattCallbacks[deviceKey]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
|
||||
val discovering = gatt.discoverServices()
|
||||
if (!discovering) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
discoverGattCallbacks[deviceKey] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getServices(myPeripheralKey: Long): List<MyGattServiceArgs> {
|
||||
val deviceKey = myPeripheralKey.toInt()
|
||||
val services = cachedServices[deviceKey] ?: throw IllegalStateException()
|
||||
return services.values.map { service -> service.toMyArgs() }
|
||||
}
|
||||
|
||||
override fun getCharacteristics(myServiceKey: Long): List<MyGattCharacteristicArgs> {
|
||||
val serviceKey = myServiceKey.toInt()
|
||||
val characteristics = cachedCharacteristics[serviceKey] ?: throw IllegalStateException()
|
||||
return characteristics.values.map { characteristic -> characteristic.toMyArgs() }
|
||||
}
|
||||
|
||||
override fun getDescriptors(myCharacteristicKey: Long): List<MyGattDescriptorArgs> {
|
||||
val characteristicKey = myCharacteristicKey.toInt()
|
||||
val descriptors = cachedDescriptors[characteristicKey] ?: throw IllegalStateException()
|
||||
return descriptors.values.map { descriptor -> descriptor.toMyArgs() }
|
||||
}
|
||||
|
||||
override fun readCharacteristic(myPeripheralKey: Long, myServiceKey: Long, myCharacteristicKey: Long, callback: (Result<ByteArray>) -> Unit) {
|
||||
try {
|
||||
val deviceKey = myPeripheralKey.toInt()
|
||||
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
|
||||
val serviceKey = myServiceKey.toInt()
|
||||
val characteristics = cachedCharacteristics[serviceKey]
|
||||
?: throw IllegalArgumentException()
|
||||
val characteristicKey = myCharacteristicKey.toInt()
|
||||
val characteristic = characteristics[characteristicKey] as BluetoothGattCharacteristic
|
||||
val unfinishedCallback = readCharacteristicCallbacks[characteristicKey]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val reading = gatt.readCharacteristic(characteristic)
|
||||
if (!reading) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
readCharacteristicCallbacks[characteristicKey] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeCharacteristic(myPeripheralKey: Long, myServiceKey: Long, myCharacteristicKey: Long, value: ByteArray, myTypeNumber: Long, callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
val deviceKey = myPeripheralKey.toInt()
|
||||
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
|
||||
val serviceKey = myServiceKey.toInt()
|
||||
val characteristics = cachedCharacteristics[serviceKey]
|
||||
?: throw IllegalArgumentException()
|
||||
val characteristicKey = myCharacteristicKey.toInt()
|
||||
val characteristic = characteristics[characteristicKey] as BluetoothGattCharacteristic
|
||||
val unfinishedCallback = writeCharacteristicCallbacks[characteristicKey]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val myTypeArgs = myTypeNumber.toMyGattCharacteristicTypeArgs()
|
||||
val writeType = myTypeArgs.toType()
|
||||
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val writeCode = gatt.writeCharacteristic(characteristic, value, writeType)
|
||||
writeCode == BluetoothStatusCodes.SUCCESS
|
||||
} else {
|
||||
// TODO: remove this when minSdkVersion >= 33
|
||||
characteristic.value = value
|
||||
characteristic.writeType = writeType
|
||||
gatt.writeCharacteristic(characteristic)
|
||||
}
|
||||
if (!writing) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
writeCharacteristicCallbacks[characteristicKey] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun notifyCharacteristic(myPeripheralKey: Long, myServiceKey: Long, myCharacteristicKey: Long, state: Boolean, callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
val deviceKey = myPeripheralKey.toInt()
|
||||
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
|
||||
val serviceKey = myServiceKey.toInt()
|
||||
val characteristics = cachedCharacteristics[serviceKey]
|
||||
?: throw IllegalArgumentException()
|
||||
val characteristicKey = myCharacteristicKey.toInt()
|
||||
val characteristic = characteristics[characteristicKey] as BluetoothGattCharacteristic
|
||||
val notifying = gatt.setCharacteristicNotification(characteristic, state)
|
||||
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(UUID_CLIENT_CHARACTERISTIC_CONFIG)
|
||||
val descriptorKey = descriptor.hashCode()
|
||||
val unfinishedCallback = writeDescriptorCallbacks[descriptorKey]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val value = if (state) BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
|
||||
else BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
|
||||
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val writeCode = gatt.writeDescriptor(descriptor, value)
|
||||
writeCode == BluetoothStatusCodes.SUCCESS
|
||||
} else {
|
||||
// TODO: remove this when minSdkVersion >= 33
|
||||
descriptor.value = value
|
||||
gatt.writeDescriptor(descriptor)
|
||||
}
|
||||
if (!writing) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
writeDescriptorCallbacks[descriptorKey] = callback
|
||||
// } else {
|
||||
// callback(Result.success(Unit))
|
||||
// }
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun readDescriptor(myPeripheralKey: Long, myCharacteristicKey: Long, myDescriptorKey: Long, callback: (Result<ByteArray>) -> Unit) {
|
||||
try {
|
||||
val deviceKey = myPeripheralKey.toInt()
|
||||
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
|
||||
val characteristicKey = myCharacteristicKey.toInt()
|
||||
val descriptors = cachedDescriptors[characteristicKey]
|
||||
?: throw IllegalArgumentException()
|
||||
val descriptorKey = myDescriptorKey.toInt()
|
||||
val descriptor = descriptors[descriptorKey] as BluetoothGattDescriptor
|
||||
val unfinishedCallback = readDescriptorCallbacks[descriptorKey]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val reading = gatt.readDescriptor(descriptor)
|
||||
if (!reading) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
readDescriptorCallbacks[descriptorKey] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeDescriptor(myPeripheralKey: Long, myCharacteristicKey: Long, myDescriptorKey: Long, value: ByteArray, callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
val deviceKey = myPeripheralKey.toInt()
|
||||
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
|
||||
val characteristicKey = myCharacteristicKey.toInt()
|
||||
val descriptors = cachedDescriptors[characteristicKey]
|
||||
?: throw IllegalArgumentException()
|
||||
val descriptorKey = myDescriptorKey.toInt()
|
||||
val descriptor = descriptors[descriptorKey] as BluetoothGattDescriptor
|
||||
val unfinishedCallback = writeDescriptorCallbacks[descriptorKey]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val writeCode = gatt.writeDescriptor(descriptor, value)
|
||||
writeCode == BluetoothStatusCodes.SUCCESS
|
||||
} else {
|
||||
// TODO: remove this when minSdkVersion >= 33
|
||||
descriptor.value = value
|
||||
gatt.writeDescriptor(descriptor)
|
||||
}
|
||||
if (!writing) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
writeDescriptorCallbacks[descriptorKey] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
binding.addRequestPermissionsResultListener(myRequestPermissionResultListener)
|
||||
this.binding = binding
|
||||
}
|
||||
|
||||
fun onDetachedFromActivity() {
|
||||
binding.removeRequestPermissionsResultListener(myRequestPermissionResultListener)
|
||||
}
|
||||
|
||||
fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean {
|
||||
if (requestCode != REQUEST_CODE) {
|
||||
return false
|
||||
}
|
||||
val callback = setUpCallback ?: return false
|
||||
val isGranted = results.all { r -> r == PackageManager.PERMISSION_GRANTED }
|
||||
if (isGranted) {
|
||||
register()
|
||||
}
|
||||
val myStateArgs = if (isGranted) adapter.myStateArgs
|
||||
else MyCentralStateArgs.UNAUTHORIZED
|
||||
val myStateNumber = myStateArgs.raw.toLong()
|
||||
val myArgs = MyCentralControllerArgs(myStateNumber)
|
||||
callback(Result.success(myArgs))
|
||||
return true
|
||||
}
|
||||
|
||||
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 myStateArgs = state.toMyCentralStateArgs()
|
||||
val myStateNumber = myStateArgs.raw.toLong()
|
||||
myApi.onStateChanged(myStateNumber) {}
|
||||
}
|
||||
|
||||
fun onScanFailed(errorCode: Int) {
|
||||
val callback = startDiscoveryCallback ?: return
|
||||
startDiscoveryCallback = null
|
||||
val error = IllegalStateException("Start discovery failed with error code: $errorCode")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
|
||||
fun onScanResult(result: ScanResult) {
|
||||
val device = result.device
|
||||
val deviceKey = device.hashCode()
|
||||
cachedDevices[deviceKey] = device
|
||||
val myPeripheralArgs = device.toMyArgs()
|
||||
val rssi = result.rssi.toLong()
|
||||
val myAdvertisementArgs = result.myAdvertisementArgs
|
||||
myApi.onDiscovered(myPeripheralArgs, rssi, myAdvertisementArgs) {}
|
||||
}
|
||||
|
||||
fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
||||
val device = gatt.device
|
||||
val deviceKey = device.hashCode()
|
||||
val myPeripheralKey = deviceKey.toLong()
|
||||
if (newState != BluetoothProfile.STATE_CONNECTED) {
|
||||
gatt.close()
|
||||
cachedGATTs.remove(deviceKey)
|
||||
val error = IllegalStateException("GATT is disconnected with status: $status")
|
||||
val getMaximumWriteLengthCallback = getMaximumWriteLengthCallbacks.remove(deviceKey)
|
||||
if (getMaximumWriteLengthCallback != null) {
|
||||
getMaximumWriteLengthCallback(Result.failure(error))
|
||||
}
|
||||
val discoverGattCallback = discoverGattCallbacks.remove(deviceKey)
|
||||
if (discoverGattCallback != null) {
|
||||
discoverGattCallback(Result.failure(error))
|
||||
}
|
||||
val services = cachedServices[deviceKey] ?: emptyMap()
|
||||
for (service in services) {
|
||||
val characteristics = cachedCharacteristics[service.key] ?: emptyMap()
|
||||
for (characteristic in characteristics) {
|
||||
val readCharacteristicCallback = readCharacteristicCallbacks.remove(characteristic.key)
|
||||
val writeCharacteristicCallback = writeCharacteristicCallbacks.remove(characteristic.key)
|
||||
if (readCharacteristicCallback != null) {
|
||||
readCharacteristicCallback(Result.failure(error))
|
||||
}
|
||||
if (writeCharacteristicCallback != null) {
|
||||
writeCharacteristicCallback(Result.failure(error))
|
||||
}
|
||||
val descriptors = cachedDescriptors[characteristic.key] ?: emptyMap()
|
||||
for (descriptor in descriptors) {
|
||||
val readDescriptorCallback = readDescriptorCallbacks.remove(descriptor.key)
|
||||
val writeDescriptorCallback = writeDescriptorCallbacks.remove(descriptor.key)
|
||||
if (readDescriptorCallback != null) {
|
||||
readDescriptorCallback(Result.failure(error))
|
||||
}
|
||||
if (writeDescriptorCallback != null) {
|
||||
writeDescriptorCallback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val connectCallback = connectCallbacks.remove(deviceKey)
|
||||
val disconnectCallback = disconnectCallbacks.remove(deviceKey)
|
||||
if (connectCallback == null && disconnectCallback == null) {
|
||||
// State changed.
|
||||
val state = newState == BluetoothProfile.STATE_CONNECTED
|
||||
myApi.onPeripheralStateChanged(myPeripheralKey, state) {}
|
||||
} else {
|
||||
if (connectCallback != null) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
// Connect succeed.
|
||||
connectCallback(Result.success(Unit))
|
||||
myApi.onPeripheralStateChanged(myPeripheralKey, true) {}
|
||||
} else {
|
||||
// Connect failed.
|
||||
val error = IllegalStateException("Connect failed with status: $status")
|
||||
connectCallback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
if (disconnectCallback != null) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
// Disconnect succeed.
|
||||
disconnectCallback(Result.success(Unit))
|
||||
myApi.onPeripheralStateChanged(myPeripheralKey, false) {}
|
||||
} else {
|
||||
// Disconnect failed.
|
||||
val error = IllegalStateException("Connect failed with status: $status")
|
||||
disconnectCallback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
|
||||
val device = gatt.device
|
||||
val deviceKey = device.hashCode()
|
||||
val callback = getMaximumWriteLengthCallbacks.remove(deviceKey) ?: return
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
val maximumWriteLength = (mtu - 3).toLong()
|
||||
callback(Result.success(maximumWriteLength))
|
||||
} else {
|
||||
val error = IllegalStateException("Get maximum write length failed with status: $status")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
|
||||
val device = gatt.device
|
||||
val deviceKey = device.hashCode()
|
||||
val callback = discoverGattCallbacks.remove(deviceKey) ?: return
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
val cachedServices = mutableMapOf<Int, BluetoothGattService>()
|
||||
for (service in gatt.services) {
|
||||
val serviceKey = service.hashCode()
|
||||
cachedServices[serviceKey] = service
|
||||
val cachedCharacteristics = mutableMapOf<Int, BluetoothGattCharacteristic>()
|
||||
for (characteristic in service.characteristics) {
|
||||
val characteristicKey = characteristic.hashCode()
|
||||
cachedCharacteristics[characteristicKey] = characteristic
|
||||
val cachedDescriptors = mutableMapOf<Int, BluetoothGattDescriptor>()
|
||||
for (descriptor in characteristic.descriptors) {
|
||||
val descriptorKey = descriptor.hashCode()
|
||||
cachedDescriptors[descriptorKey] = descriptor
|
||||
}
|
||||
this.cachedDescriptors[characteristicKey] = cachedDescriptors
|
||||
}
|
||||
this.cachedCharacteristics[serviceKey] = cachedCharacteristics
|
||||
}
|
||||
this.cachedServices[deviceKey] = cachedServices
|
||||
callback(Result.success(Unit))
|
||||
} else {
|
||||
val error = IllegalStateException("Discover GATT failed with status: $status")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
fun onCharacteristicRead(characteristic: BluetoothGattCharacteristic, status: Int, value: ByteArray) {
|
||||
val characteristicKey = characteristic.hashCode()
|
||||
val callback = readCharacteristicCallbacks.remove(characteristicKey) ?: return
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
callback(Result.success(value))
|
||||
} else {
|
||||
val error = IllegalStateException("Read characteristic failed with status: $status.")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
fun onCharacteristicWrite(characteristic: BluetoothGattCharacteristic, status: Int) {
|
||||
val characteristicKey = characteristic.hashCode()
|
||||
val callback = writeCharacteristicCallbacks.remove(characteristicKey) ?: return
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
callback(Result.success(Unit))
|
||||
} else {
|
||||
val error = IllegalStateException("Write characteristic failed with status: $status.")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
fun onCharacteristicChanged(characteristic: BluetoothGattCharacteristic, value: ByteArray) {
|
||||
val characteristicKey = characteristic.hashCode()
|
||||
val myCharacteristicKey = characteristicKey.toLong()
|
||||
myApi.onCharacteristicValueChanged(myCharacteristicKey, value) {}
|
||||
}
|
||||
|
||||
fun onDescriptorRead(descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) {
|
||||
val descriptorKey = descriptor.hashCode()
|
||||
val callback = readDescriptorCallbacks.remove(descriptorKey) ?: return
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
callback(Result.success(value))
|
||||
} else {
|
||||
val error = IllegalStateException("Read descriptor failed with status: $status.")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
fun onDescriptorWrite(descriptor: BluetoothGattDescriptor, status: Int) {
|
||||
val descriptorKey = descriptor.hashCode()
|
||||
val callback = writeDescriptorCallbacks.remove(descriptorKey) ?: return
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
callback(Result.success(Unit))
|
||||
} else {
|
||||
val error = IllegalStateException("Write descriptor failed with status: $status.")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val BluetoothAdapter.myStateArgs: MyCentralStateArgs
|
||||
get() = state.toMyCentralStateArgs()
|
||||
|
||||
private fun Int.toMyCentralStateArgs(): MyCentralStateArgs {
|
||||
return when (this) {
|
||||
BluetoothAdapter.STATE_ON -> MyCentralStateArgs.POWEREDON
|
||||
else -> MyCentralStateArgs.POWEREDOFF
|
||||
}
|
||||
}
|
||||
|
||||
private fun BluetoothDevice.toMyArgs(): MyPeripheralArgs {
|
||||
val key = hashCode().toLong()
|
||||
val uuid = this.uuid.toString()
|
||||
return MyPeripheralArgs(key, uuid)
|
||||
}
|
||||
|
||||
private 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")
|
||||
}
|
||||
|
||||
private val ScanResult.myAdvertisementArgs: MyAdvertisementArgs
|
||||
get() {
|
||||
val record = scanRecord
|
||||
return if (record == null) {
|
||||
val name = null
|
||||
val manufacturerSpecificData = emptyMap<Long?, ByteArray?>()
|
||||
val serviceUUIDs = emptyList<String?>()
|
||||
val serviceData = emptyMap<String?, ByteArray>()
|
||||
MyAdvertisementArgs(name, manufacturerSpecificData, serviceUUIDs, serviceData)
|
||||
} else {
|
||||
val name = record.deviceName
|
||||
val manufacturerSpecificData = record.manufacturerSpecificData.toMyArgs()
|
||||
val serviceUUIDs = record.serviceUuids?.map { uuid -> uuid.toString() }
|
||||
?: emptyList()
|
||||
val pairs = record.serviceData.entries.map { (uuid, value) ->
|
||||
val key = uuid.toString()
|
||||
return@map Pair(key, value)
|
||||
}.toTypedArray()
|
||||
val serviceData = mapOf<String?, ByteArray?>(*pairs)
|
||||
MyAdvertisementArgs(name, manufacturerSpecificData, serviceUUIDs, serviceData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SparseArray<ByteArray>.toMyArgs(): Map<Long?, ByteArray?> {
|
||||
var index = 0
|
||||
val size = size()
|
||||
val values = mutableMapOf<Long?, ByteArray>()
|
||||
while (index < size) {
|
||||
val key = keyAt(index).toLong()
|
||||
val value = valueAt(index)
|
||||
values[key] = value
|
||||
index++
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
//private 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()
|
||||
// }
|
||||
|
||||
private fun BluetoothGattService.toMyArgs(): MyGattServiceArgs {
|
||||
val key = hashCode().toLong()
|
||||
val uuid = this.uuid.toString()
|
||||
return MyGattServiceArgs(key, uuid)
|
||||
}
|
||||
|
||||
private fun BluetoothGattCharacteristic.toMyArgs(): MyGattCharacteristicArgs {
|
||||
val key = hashCode().toLong()
|
||||
val uuid = this.uuid.toString()
|
||||
return MyGattCharacteristicArgs(key, uuid, myPropertyNumbers)
|
||||
}
|
||||
|
||||
private val BluetoothGattCharacteristic.myPropertyNumbers: List<Long>
|
||||
get() {
|
||||
val numbers = mutableListOf<Long>()
|
||||
if (properties and BluetoothGattCharacteristic.PROPERTY_READ != 0) {
|
||||
val number = MyGattCharacteristicPropertyArgs.READ.raw.toLong()
|
||||
numbers.add(number)
|
||||
}
|
||||
if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE != 0) {
|
||||
val number = MyGattCharacteristicPropertyArgs.WRITE.raw.toLong()
|
||||
numbers.add(number)
|
||||
}
|
||||
if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE != 0) {
|
||||
val number = MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE.raw.toLong()
|
||||
numbers.add(number)
|
||||
}
|
||||
if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) {
|
||||
val number = MyGattCharacteristicPropertyArgs.NOTIFY.raw.toLong()
|
||||
numbers.add(number)
|
||||
}
|
||||
if (properties and BluetoothGattCharacteristic.PROPERTY_INDICATE != 0) {
|
||||
val number = MyGattCharacteristicPropertyArgs.INDICATE.raw.toLong()
|
||||
numbers.add(number)
|
||||
}
|
||||
return numbers
|
||||
}
|
||||
|
||||
private fun BluetoothGattDescriptor.toMyArgs(): MyGattDescriptorArgs {
|
||||
val key = hashCode().toLong()
|
||||
val uuid = this.uuid.toString()
|
||||
return MyGattDescriptorArgs(key, uuid)
|
||||
}
|
||||
|
||||
private fun Long.toMyGattCharacteristicTypeArgs(): MyGattCharacteristicWriteTypeArgs {
|
||||
val raw = toInt()
|
||||
return MyGattCharacteristicWriteTypeArgs.ofRaw(raw) ?: throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
private fun MyGattCharacteristicWriteTypeArgs.toType(): Int {
|
||||
return when (this) {
|
||||
MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
|
||||
MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
|
||||
}
|
||||
}
|
@ -0,0 +1,627 @@
|
||||
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.BluetoothProfile
|
||||
import android.bluetooth.BluetoothStatusCodes
|
||||
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 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 getMaximumWriteLengthCallbacks = 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))
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
peripheralsArgs.clear()
|
||||
servicesArgsOfPeripherals.clear()
|
||||
servicesArgs.clear()
|
||||
characteristicsArgs.clear()
|
||||
descriptorsArgs.clear()
|
||||
setUpCallback = null
|
||||
startDiscoveryCallback = null
|
||||
connectCallbacks.clear()
|
||||
disconnectCallbacks.clear()
|
||||
getMaximumWriteLengthCallbacks.clear()
|
||||
readRssiCallbacks.clear()
|
||||
discoverGattCallbacks.clear()
|
||||
readCharacteristicCallbacks.clear()
|
||||
writeCharacteristicCallbacks.clear()
|
||||
readDescriptorCallbacks.clear()
|
||||
writeDescriptorCallbacks.clear()
|
||||
}
|
||||
|
||||
override fun register() {
|
||||
super.register()
|
||||
registered = true
|
||||
}
|
||||
|
||||
override fun unregister() {
|
||||
super.unregister()
|
||||
registered = false
|
||||
}
|
||||
|
||||
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
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopDiscovery() {
|
||||
scanner.stopScan(scanCallback)
|
||||
discovering = false
|
||||
}
|
||||
|
||||
override fun connect(peripheralHashCodeArgs: Long, callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
val unfinishedCallback = connectCallbacks[peripheralHashCodeArgs]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val device = devices[peripheralHashCodeArgs] as BluetoothDevice
|
||||
val autoConnect = false
|
||||
// Add to bluetoothGATTs cache.
|
||||
bluetoothGATTs[peripheralHashCodeArgs] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
val transport = BluetoothDevice.TRANSPORT_LE
|
||||
device.connectGatt(context, autoConnect, bluetoothGattCallback, transport)
|
||||
} else {
|
||||
device.connectGatt(context, autoConnect, bluetoothGattCallback)
|
||||
}
|
||||
connectCallbacks[peripheralHashCodeArgs] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun disconnect(peripheralHashCodeArgs: Long, callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
val unfinishedCallback = disconnectCallbacks[peripheralHashCodeArgs]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
|
||||
gatt.disconnect()
|
||||
disconnectCallbacks[peripheralHashCodeArgs] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMaximumWriteLength(peripheralHashCodeArgs: Long, callback: (Result<Long>) -> Unit) {
|
||||
try {
|
||||
val unfinishedCallback = getMaximumWriteLengthCallbacks[peripheralHashCodeArgs]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
|
||||
val requesting = gatt.requestMtu(517)
|
||||
if (!requesting) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
getMaximumWriteLengthCallbacks[peripheralHashCodeArgs] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun readRSSI(peripheralHashCodeArgs: Long, callback: (Result<Long>) -> Unit) {
|
||||
try {
|
||||
val unfinishedCallback = readRssiCallbacks[peripheralHashCodeArgs]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
|
||||
val reading = gatt.readRemoteRssi()
|
||||
if (!reading) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
readRssiCallbacks[peripheralHashCodeArgs] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun discoverGATT(peripheralHashCodeArgs: Long, callback: (Result<List<MyGattServiceArgs>>) -> Unit) {
|
||||
try {
|
||||
val unfinishedCallback = discoverGattCallbacks[peripheralHashCodeArgs]
|
||||
if (unfinishedCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
|
||||
val discovering = gatt.discoverServices()
|
||||
if (!discovering) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
discoverGattCallbacks[peripheralHashCodeArgs] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun readCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: 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 reading = gatt.readCharacteristic(characteristic)
|
||||
if (!reading) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
readCharacteristicCallbacks[characteristicHashCodeArgs] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: 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 type = typeArgs.toType()
|
||||
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val code = gatt.writeCharacteristic(characteristic, valueArgs, type)
|
||||
code == BluetoothStatusCodes.SUCCESS
|
||||
} else {
|
||||
// TODO: remove this when minSdkVersion >= 33
|
||||
characteristic.value = valueArgs
|
||||
characteristic.writeType = type
|
||||
gatt.writeCharacteristic(characteristic)
|
||||
}
|
||||
if (!writing) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
writeCharacteristicCallbacks[characteristicHashCodeArgs] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun notifyCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, stateArgs: Boolean, callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
|
||||
val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic
|
||||
val notifying = gatt.setCharacteristicNotification(characteristic, stateArgs)
|
||||
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))
|
||||
// }
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun readDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: 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 reading = gatt.readDescriptor(descriptor)
|
||||
if (!reading) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
readDescriptorCallbacks[descriptorHashCodeArgs] = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: 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 writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val code = gatt.writeDescriptor(descriptor, valueArgs)
|
||||
code == BluetoothStatusCodes.SUCCESS
|
||||
} else {
|
||||
// TODO: remove this when minSdkVersion >= 33
|
||||
descriptor.value = valueArgs
|
||||
gatt.writeDescriptor(descriptor)
|
||||
}
|
||||
if (!writing) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
writeDescriptorCallbacks[descriptorHashCodeArgs] = 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) {}
|
||||
}
|
||||
|
||||
private fun onScanSucceed() {
|
||||
discovering = true
|
||||
val callback = startDiscoveryCallback ?: return
|
||||
startDiscoveryCallback = null
|
||||
callback(Result.success(Unit))
|
||||
}
|
||||
|
||||
fun onScanFailed(errorCode: Int) {
|
||||
val callback = startDiscoveryCallback ?: return
|
||||
startDiscoveryCallback = null
|
||||
val error = IllegalStateException("Start discovery failed with error code: $errorCode")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
|
||||
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 rssiArgs = result.rssi.toLong()
|
||||
val advertiseDataArgs = result.advertiseDataArgs
|
||||
api.onDiscovered(peripheralArgs, rssiArgs, advertiseDataArgs) {}
|
||||
}
|
||||
|
||||
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
|
||||
// Check callbacks
|
||||
if (newState != BluetoothProfile.STATE_CONNECTED) {
|
||||
gatt.close()
|
||||
bluetoothGATTs.remove(peripheralHashCodeArgs)
|
||||
val error = IllegalStateException("GATT is disconnected with status: $status")
|
||||
val getMaximumWriteLengthCallback = getMaximumWriteLengthCallbacks.remove(peripheralHashCodeArgs)
|
||||
if (getMaximumWriteLengthCallback != null) {
|
||||
getMaximumWriteLengthCallback(Result.failure(error))
|
||||
}
|
||||
val readRssiCallback = readRssiCallbacks.remove(peripheralHashCodeArgs)
|
||||
if (readRssiCallback != null) {
|
||||
readRssiCallback(Result.failure(error))
|
||||
}
|
||||
val discoverGattCallback = discoverGattCallbacks.remove(peripheralHashCodeArgs)
|
||||
if (discoverGattCallback != null) {
|
||||
discoverGattCallback(Result.failure(error))
|
||||
}
|
||||
val servicesArgs = servicesArgsOfPeripherals[peripheralHashCodeArgs] ?: emptyList()
|
||||
for (serviceArgs in servicesArgs) {
|
||||
val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull()
|
||||
for (characteristicArgs in characteristicsArgs) {
|
||||
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
|
||||
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 readDescriptorCallback = readDescriptorCallbacks.remove(descriptorHashCodeArgs)
|
||||
val writeDescriptorCallback = writeDescriptorCallbacks.remove(descriptorHashCodeArgs)
|
||||
if (readDescriptorCallback != null) {
|
||||
readDescriptorCallback(Result.failure(error))
|
||||
}
|
||||
if (writeDescriptorCallback != null) {
|
||||
writeDescriptorCallback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check state
|
||||
val connectCallback = connectCallbacks.remove(peripheralHashCodeArgs)
|
||||
val disconnectCallback = disconnectCallbacks.remove(peripheralHashCodeArgs)
|
||||
if (connectCallback == null && disconnectCallback == null) {
|
||||
// State changed.
|
||||
val stateArgs = newState == BluetoothProfile.STATE_CONNECTED
|
||||
api.onPeripheralStateChanged(peripheralArgs, stateArgs) {}
|
||||
} else {
|
||||
if (connectCallback != null) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
// Connect succeed.
|
||||
connectCallback(Result.success(Unit))
|
||||
api.onPeripheralStateChanged(peripheralArgs, true) {}
|
||||
} else {
|
||||
// Connect failed.
|
||||
val error = IllegalStateException("Connect failed with status: $status")
|
||||
connectCallback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
if (disconnectCallback != null) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
// Disconnect succeed.
|
||||
disconnectCallback(Result.success(Unit))
|
||||
api.onPeripheralStateChanged(peripheralArgs, false) {}
|
||||
} else {
|
||||
// Disconnect failed.
|
||||
val error = IllegalStateException("Connect failed with status: $status")
|
||||
disconnectCallback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
val callback = getMaximumWriteLengthCallbacks.remove(peripheralHashCodeArgs) ?: return
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
val maximumWriteLengthArgs = (mtu - 3).toLong()
|
||||
callback(Result.success(maximumWriteLengthArgs))
|
||||
} else {
|
||||
val error = IllegalStateException("Get maximum write length failed with status: $status")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
val rssiArgs = rssi.toLong()
|
||||
callback(Result.success(rssiArgs))
|
||||
} else {
|
||||
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
|
||||
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
|
||||
callback(Result.success(servicesArgs))
|
||||
} else {
|
||||
val error = IllegalStateException("Discover GATT failed with status: $status")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
callback(Result.success(value))
|
||||
} else {
|
||||
val error = IllegalStateException("Read characteristic failed with status: $status.")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
callback(Result.success(Unit))
|
||||
} else {
|
||||
val error = IllegalStateException("Write characteristic failed with status: $status.")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
fun onCharacteristicChanged(characteristic: BluetoothGattCharacteristic, value: ByteArray) {
|
||||
val hashCode = characteristic.hashCode()
|
||||
val characteristicArgs = characteristicsArgs[hashCode] as MyGattCharacteristicArgs
|
||||
api.onCharacteristicValueChanged(characteristicArgs, 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
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
callback(Result.success(value))
|
||||
} else {
|
||||
val error = IllegalStateException("Read descriptor failed with status: $status.")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
callback(Result.success(Unit))
|
||||
} else {
|
||||
val error = IllegalStateException("Write descriptor failed with status: $status.")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,423 @@
|
||||
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.BluetoothStatusCodes
|
||||
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 {
|
||||
private val server by lazy { manager.openGattServer(context, bluetoothGattServerCallback) }
|
||||
private val advertiser get() = adapter.bluetoothLeAdvertiser
|
||||
|
||||
private val api = MyPeripheralManagerFlutterApi(binaryMessenger)
|
||||
private val bluetoothGattServerCallback = MyBluetoothGattServerCallback(this, executor)
|
||||
private val advertiseCallback = MyAdvertiseCallback(this)
|
||||
|
||||
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 centralsArgs = mutableMapOf<Int, MyCentralArgs>()
|
||||
private val servicesArgs = mutableMapOf<Int, MyGattServiceArgs>()
|
||||
private val characteristicsArgs = mutableMapOf<Int, MyGattCharacteristicArgs>()
|
||||
private val descriptorsArgs = mutableMapOf<Int, MyGattDescriptorArgs>()
|
||||
|
||||
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 fun tearDown() {
|
||||
if (registered) {
|
||||
unregister()
|
||||
}
|
||||
if (advertising) {
|
||||
stopAdvertising()
|
||||
}
|
||||
devices.clear()
|
||||
services.clear()
|
||||
characteristics.clear()
|
||||
descriptors.clear()
|
||||
mtus.clear()
|
||||
confirms.clear()
|
||||
centralsArgs.clear()
|
||||
servicesArgs.clear()
|
||||
characteristicsArgs.clear()
|
||||
descriptorsArgs.clear()
|
||||
setUpCallback = null
|
||||
addServiceCallback = null
|
||||
startAdvertisingCallback = null
|
||||
notifyCharacteristicValueChangedCallbacks.clear()
|
||||
}
|
||||
|
||||
override fun register() {
|
||||
super.register()
|
||||
registered = true
|
||||
}
|
||||
|
||||
override fun unregister() {
|
||||
super.unregister()
|
||||
registered = false
|
||||
}
|
||||
|
||||
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 cccDescriptorAdded = characteristic.addDescriptor(cccDescriptor)
|
||||
if (!cccDescriptorAdded) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull()
|
||||
for (descriptorArgs in descriptorsArgs) {
|
||||
val descriptor = descriptorArgs.toDescriptor()
|
||||
if (descriptor.uuid == CLIENT_CHARACTERISTIC_CONFIG_UUID) {
|
||||
// Already added.
|
||||
continue
|
||||
}
|
||||
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 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
|
||||
} catch (e: Throwable) {
|
||||
freeService(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)
|
||||
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)
|
||||
}
|
||||
|
||||
override fun clearServices() {
|
||||
server.clearServices()
|
||||
val servicesArgs = this.servicesArgs.values
|
||||
for (serviceArgs in servicesArgs) {
|
||||
freeService(serviceArgs)
|
||||
}
|
||||
}
|
||||
|
||||
override fun startAdvertising(advertiseDataArgs: MyAdvertiseDataArgs, callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
if (startAdvertisingCallback != null) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
val settings = AdvertiseSettings.Builder().setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED).setConnectable(true).build()
|
||||
val advertiseData = advertiseDataArgs.toAdvertiseData(adapter)
|
||||
advertiser.startAdvertising(settings, advertiseData, advertiseCallback)
|
||||
startAdvertisingCallback = callback
|
||||
} catch (e: Throwable) {
|
||||
callback(Result.failure(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopAdvertising() {
|
||||
advertiser.stopAdvertising(advertiseCallback)
|
||||
advertising = false
|
||||
}
|
||||
|
||||
override fun getMaximumWriteLength(centralHashCodeArgs: Long): Long {
|
||||
val mtu = mtus[centralHashCodeArgs] ?: 23
|
||||
return (mtu - 3).toLong()
|
||||
}
|
||||
|
||||
override fun sendReadCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean, valueArgs: ByteArray) {
|
||||
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 sent = server.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 = null
|
||||
val sent = server.sendResponse(device, requestId, status, offset, value)
|
||||
if (!sent) {
|
||||
throw IllegalStateException("Send write characteristic reply failed.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun notifyCharacteristicValueChanged(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result<Unit>) -> Unit) {
|
||||
try {
|
||||
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 notifying = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val statusCode = server.notifyCharacteristicChanged(device, characteristic, confirm, valueArgs)
|
||||
statusCode == BluetoothStatusCodes.SUCCESS
|
||||
} else {
|
||||
characteristic.value = valueArgs
|
||||
server.notifyCharacteristicChanged(device, characteristic, confirm)
|
||||
}
|
||||
if (!notifying) {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
notifyCharacteristicValueChangedCallbacks[centralHashCodeArgs] = 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 = MyPeripheralManagerArgs(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) {}
|
||||
}
|
||||
|
||||
fun onServiceAdded(status: Int, service: BluetoothGattService) {
|
||||
val callback = addServiceCallback ?: return
|
||||
addServiceCallback = 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))
|
||||
}
|
||||
}
|
||||
|
||||
fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
|
||||
advertising = true
|
||||
val callback = startAdvertisingCallback ?: return
|
||||
startAdvertisingCallback = null
|
||||
callback(Result.success(Unit))
|
||||
}
|
||||
|
||||
fun onStartFailure(errorCode: Int) {
|
||||
val callback = startAdvertisingCallback ?: return
|
||||
startAdvertisingCallback = null
|
||||
val error = IllegalStateException("Start advertising failed with error code: $errorCode")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
val idArgs = requestId.toLong()
|
||||
val offsetArgs = offset.toLong()
|
||||
api.onReadCharacteristicCommandReceived(centralArgs, characteristicArgs, 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
|
||||
val characteristicHashCode = characteristic.hashCode()
|
||||
val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs
|
||||
val idArgs = requestId.toLong()
|
||||
val offsetArgs = offset.toLong()
|
||||
api.onWriteCharacteristicCommandReceived(centralArgs, characteristicArgs, idArgs, offsetArgs, value) {}
|
||||
}
|
||||
|
||||
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 value = descriptorArgs.valueArgs
|
||||
val sent = server.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
|
||||
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
|
||||
}
|
||||
} 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
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
callback(Result.success(Unit))
|
||||
} else {
|
||||
val error = IllegalStateException("Notify characteristic value changed failed with status: $status")
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
@ -2,8 +2,8 @@ package dev.yanshouwang.bluetooth_low_energy_android
|
||||
|
||||
import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener
|
||||
|
||||
class MyRequestPermissionResultListener(private val myCentralController: MyCentralController) : RequestPermissionsResultListener {
|
||||
class MyRequestPermissionResultListener(private val bluetoothLowEnergyManager: MyBluetoothLowEnergyManager) : RequestPermissionsResultListener {
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, results: IntArray): Boolean {
|
||||
return myCentralController.onRequestPermissionsResult(requestCode, results)
|
||||
return bluetoothLowEnergyManager.onRequestPermissionsResult(requestCode, results)
|
||||
}
|
||||
}
|
@ -3,14 +3,14 @@ package dev.yanshouwang.bluetooth_low_energy_android
|
||||
import android.bluetooth.le.ScanCallback
|
||||
import android.bluetooth.le.ScanResult
|
||||
|
||||
class MyScanCallback(private val myCentralController: MyCentralController) : ScanCallback() {
|
||||
class MyScanCallback(private val centralManager: MyCentralManager) : ScanCallback() {
|
||||
override fun onScanFailed(errorCode: Int) {
|
||||
super.onScanFailed(errorCode)
|
||||
myCentralController.onScanFailed(errorCode)
|
||||
centralManager.onScanFailed(errorCode)
|
||||
}
|
||||
|
||||
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
||||
super.onScanResult(callbackType, result)
|
||||
myCentralController.onScanResult(result)
|
||||
centralManager.onScanResult(result)
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'src/my_central_controller.dart';
|
||||
import 'src/my_bluetooth_low_energy.dart';
|
||||
|
||||
abstract class BluetoothLowEnergyAndroid {
|
||||
static void registerWith() {
|
||||
CentralController.instance = MyCentralController();
|
||||
BluetoothLowEnergy.instance = MyBluetoothLowEnergy();
|
||||
}
|
||||
}
|
||||
|
224
bluetooth_low_energy_android/lib/src/my_api.dart
Normal file
224
bluetooth_low_energy_android/lib/src/my_api.dart
Normal file
@ -0,0 +1,224 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_gatt_characteristic2.dart';
|
||||
import 'my_gatt_descriptor2.dart';
|
||||
import 'my_gatt_service2.dart';
|
||||
|
||||
export 'my_api.g.dart';
|
||||
|
||||
extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs {
|
||||
BluetoothLowEnergyState toState() {
|
||||
return BluetoothLowEnergyState.values[index];
|
||||
}
|
||||
}
|
||||
|
||||
extension MyAdvertiseDataArgsX on MyAdvertiseDataArgs {
|
||||
AdvertiseData toAdvertiseData() {
|
||||
final name = nameArgs;
|
||||
final serviceUUIDs = serviceUUIDsArgs
|
||||
.cast<String>()
|
||||
.map((args) => UUID.fromString(args))
|
||||
.toList();
|
||||
final serviceData = serviceDataArgs.cast<String, Uint8List>().map(
|
||||
(uuidArgs, dataArgs) {
|
||||
final uuid = UUID.fromString(uuidArgs);
|
||||
final data = dataArgs;
|
||||
return MapEntry(uuid, data);
|
||||
},
|
||||
);
|
||||
final manufacturerSpecificData =
|
||||
manufacturerSpecificDataArgs?.toManufacturerSpecificData();
|
||||
return AdvertiseData(
|
||||
name: name,
|
||||
serviceUUIDs: serviceUUIDs,
|
||||
serviceData: serviceData,
|
||||
manufacturerSpecificData: manufacturerSpecificData,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs {
|
||||
ManufacturerSpecificData toManufacturerSpecificData() {
|
||||
final id = idArgs;
|
||||
final data = dataArgs;
|
||||
return ManufacturerSpecificData(
|
||||
id: id,
|
||||
data: data,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattCharacteristicPropertyArgsX
|
||||
on MyGattCharacteristicPropertyArgs {
|
||||
GattCharacteristicProperty toProperty() {
|
||||
return GattCharacteristicProperty.values[index];
|
||||
}
|
||||
}
|
||||
|
||||
extension 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattServiceArgsX on MyGattServiceArgs {
|
||||
MyGattService2 toService2() {
|
||||
final hashCode = hashCodeArgs;
|
||||
final uuid = UUID.fromString(uuidArgs);
|
||||
final characteristics = characteristicsArgs
|
||||
.cast<MyGattCharacteristicArgs>()
|
||||
.map((args) => args.toCharacteristic2())
|
||||
.toList();
|
||||
return MyGattService2(
|
||||
hashCode: hashCode,
|
||||
uuid: uuid,
|
||||
characteristics: characteristics,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs {
|
||||
MyGattCharacteristic2 toCharacteristic2() {
|
||||
final hashCode = hashCodeArgs;
|
||||
final uuid = UUID.fromString(uuidArgs);
|
||||
final properties = propertyNumbersArgs.cast<int>().map(
|
||||
(args) {
|
||||
final propertyArgs = MyGattCharacteristicPropertyArgs.values[args];
|
||||
return propertyArgs.toProperty();
|
||||
},
|
||||
).toList();
|
||||
final descriptors = descriptorsArgs
|
||||
.cast<MyGattDescriptorArgs>()
|
||||
.map((args) => args.toDescriptor2())
|
||||
.toList();
|
||||
return MyGattCharacteristic2(
|
||||
hashCode: hashCode,
|
||||
uuid: uuid,
|
||||
properties: properties,
|
||||
descriptors: descriptors,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattDescriptorArgsX on MyGattDescriptorArgs {
|
||||
MyGattDescriptor2 toDescriptor2() {
|
||||
final hashCode = hashCodeArgs;
|
||||
final uuid = UUID.fromString(uuidArgs);
|
||||
return MyGattDescriptor2(
|
||||
hashCode: hashCode,
|
||||
uuid: uuid,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyCentralArgsX on MyCentralArgs {
|
||||
MyCentral toCentral() {
|
||||
final hashCode = hashCodeArgs;
|
||||
final uuid = UUID.fromString(uuidArgs);
|
||||
return MyCentral(
|
||||
hashCode: hashCode,
|
||||
uuid: uuid,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension AdvertiseDataX on AdvertiseData {
|
||||
MyAdvertiseDataArgs 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 MyAdvertiseDataArgs(
|
||||
nameArgs: nameArgs,
|
||||
serviceUUIDsArgs: serviceUUIDsArgs,
|
||||
serviceDataArgs: serviceDataArgs,
|
||||
manufacturerSpecificDataArgs: manufacturerSpecificDataArgs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension ManufacturerSpecificDataX on ManufacturerSpecificData {
|
||||
MyManufacturerSpecificDataArgs toArgs() {
|
||||
final idArgs = id;
|
||||
final dataArgs = data;
|
||||
return MyManufacturerSpecificDataArgs(
|
||||
idArgs: idArgs,
|
||||
dataArgs: dataArgs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattServiceX on MyGattService {
|
||||
MyGattServiceArgs toArgs() {
|
||||
final hashCodeArgs = hashCode;
|
||||
final uuidArgs = uuid.toString();
|
||||
final characteristicsArgs = characteristics
|
||||
.cast<MyGattCharacteristic>()
|
||||
.map((characteristic) => characteristic.toArgs())
|
||||
.toList();
|
||||
return MyGattServiceArgs(
|
||||
hashCodeArgs: hashCodeArgs,
|
||||
uuidArgs: uuidArgs,
|
||||
characteristicsArgs: characteristicsArgs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattCharacteristicX on MyGattCharacteristic {
|
||||
MyGattCharacteristicArgs toArgs() {
|
||||
final hashCodeArgs = hashCode;
|
||||
final uuidArgs = uuid.toString();
|
||||
final propertyNumbersArgs = properties.map((property) {
|
||||
final propertyArgs = property.toArgs();
|
||||
return propertyArgs.index;
|
||||
}).toList();
|
||||
final descriptorsArgs = descriptors
|
||||
.cast<MyGattDescriptor>()
|
||||
.map((descriptor) => descriptor.toArgs())
|
||||
.toList();
|
||||
return MyGattCharacteristicArgs(
|
||||
hashCodeArgs: hashCodeArgs,
|
||||
uuidArgs: uuidArgs,
|
||||
propertyNumbersArgs: propertyNumbersArgs,
|
||||
descriptorsArgs: descriptorsArgs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattDescriptorX on MyGattDescriptor {
|
||||
MyGattDescriptorArgs toArgs() {
|
||||
final hashCodeArgs = hashCode;
|
||||
final uuidArgs = uuid.toString();
|
||||
final valueArgs = value;
|
||||
return MyGattDescriptorArgs(
|
||||
hashCodeArgs: hashCodeArgs,
|
||||
uuidArgs: uuidArgs,
|
||||
valueArgs: valueArgs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension GattCharacteristicPropertyX on GattCharacteristicProperty {
|
||||
MyGattCharacteristicPropertyArgs toArgs() {
|
||||
return MyGattCharacteristicPropertyArgs.values[index];
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,15 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_central_manager.dart';
|
||||
import 'my_peripheral_manager.dart';
|
||||
|
||||
class MyBluetoothLowEnergy extends BluetoothLowEnergy {
|
||||
@override
|
||||
final MyCentralManager centralManager;
|
||||
@override
|
||||
final MyPeripheralManager peripheralManager;
|
||||
|
||||
MyBluetoothLowEnergy()
|
||||
: centralManager = MyCentralManager(),
|
||||
peripheralManager = MyPeripheralManager();
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
abstract class MyBluetoothLowEnergyManager extends BluetoothLowEnergyManager {
|
||||
MyBluetoothLowEnergyManager()
|
||||
: _state = BluetoothLowEnergyState.unknown,
|
||||
_stateChangedController = StreamController.broadcast();
|
||||
|
||||
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
|
||||
_stateChangedController;
|
||||
|
||||
BluetoothLowEnergyState _state;
|
||||
@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;
|
||||
|
||||
@protected
|
||||
Future<void> throwWithoutState(BluetoothLowEnergyState state) async {
|
||||
if (this.state != state) {
|
||||
throw BluetoothLowEnergyError(
|
||||
'$state is expected, but current state is ${this.state}.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,366 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_gatt_characteristic.dart';
|
||||
import 'my_gatt_descriptor.dart';
|
||||
import 'my_gatt_service.dart';
|
||||
import 'my_peripheral.dart';
|
||||
|
||||
class MyCentralController extends CentralController
|
||||
implements MyCentralControllerFlutterApi {
|
||||
MyCentralController()
|
||||
: _myApi = MyCentralControllerHostApi(),
|
||||
_stateChangedController = StreamController.broadcast(),
|
||||
_discoveredController = StreamController.broadcast(),
|
||||
_peripheralStateChangedController = StreamController.broadcast(),
|
||||
_characteristicValueChangedController = StreamController.broadcast(),
|
||||
_myPeripherals = {},
|
||||
_myServices = {},
|
||||
_myCharacteristics = {},
|
||||
_myDescriptors = {},
|
||||
_state = CentralState.unknown;
|
||||
|
||||
final MyCentralControllerHostApi _myApi;
|
||||
final StreamController<CentralStateChangedEventArgs> _stateChangedController;
|
||||
final StreamController<CentralDiscoveredEventArgs> _discoveredController;
|
||||
final StreamController<PeripheralStateChangedEventArgs>
|
||||
_peripheralStateChangedController;
|
||||
final StreamController<GattCharacteristicValueChangedEventArgs>
|
||||
_characteristicValueChangedController;
|
||||
final Map<int, MyPeripheral> _myPeripherals;
|
||||
final Map<int, MyGattService> _myServices;
|
||||
final Map<int, MyGattCharacteristic> _myCharacteristics;
|
||||
final Map<int, MyGattDescriptor> _myDescriptors;
|
||||
|
||||
CentralState _state;
|
||||
@override
|
||||
CentralState get state => _state;
|
||||
|
||||
@override
|
||||
Stream<CentralStateChangedEventArgs> get stateChanged =>
|
||||
_stateChangedController.stream;
|
||||
@override
|
||||
Stream<CentralDiscoveredEventArgs> get discovered =>
|
||||
_discoveredController.stream;
|
||||
@override
|
||||
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
|
||||
_peripheralStateChangedController.stream;
|
||||
@override
|
||||
Stream<GattCharacteristicValueChangedEventArgs>
|
||||
get characteristicValueChanged =>
|
||||
_characteristicValueChangedController.stream;
|
||||
|
||||
Future<void> _throwWithState(CentralState state) async {
|
||||
if (this.state == state) {
|
||||
throw BluetoothLowEnergyError('$state is unexpected.');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _throwWithoutState(CentralState state) async {
|
||||
if (this.state != state) {
|
||||
throw BluetoothLowEnergyError(
|
||||
'$state is expected, but current state is ${this.state}.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setUp() async {
|
||||
await _throwWithoutState(CentralState.unknown);
|
||||
final args = await _myApi.setUp();
|
||||
final myStateArgs = MyCentralStateArgs.values[args.myStateNumber];
|
||||
_state = myStateArgs.toState();
|
||||
MyCentralControllerFlutterApi.setup(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> tearDown() async {
|
||||
await _throwWithState(CentralState.unknown);
|
||||
await _myApi.tearDown();
|
||||
MyCentralControllerFlutterApi.setup(null);
|
||||
_myPeripherals.clear();
|
||||
_myServices.clear();
|
||||
_myCharacteristics.clear();
|
||||
_myDescriptors.clear();
|
||||
_state = CentralState.unknown;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startDiscovery() async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
await _myApi.startDiscovery();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopDiscovery() async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
await _myApi.stopDiscovery();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> connect(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
await _myApi.connect(myPeripheral.hashCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> disconnect(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
await _myApi.disconnect(myPeripheral.hashCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getMaximumWriteLength(
|
||||
Peripheral peripheral, {
|
||||
required GattCharacteristicWriteType type,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
final maximumWriteLength = await _myApi.getMaximumWriteLength(
|
||||
myPeripheral.hashCode,
|
||||
);
|
||||
return maximumWriteLength;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> discoverGATT(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
await _myApi.discoverGATT(myPeripheral.hashCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattService>> getServices(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
final myServiceArgses = await _myApi.getServices(myPeripheral.hashCode);
|
||||
return myServiceArgses.cast<MyGattServiceArgs>().map(
|
||||
(myServiceArgs) {
|
||||
final myService = MyGattService.fromMyArgs(
|
||||
myPeripheral,
|
||||
myServiceArgs,
|
||||
);
|
||||
_myServices[myService.hashCode] = myService;
|
||||
return myService;
|
||||
},
|
||||
).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattCharacteristic>> getCharacteristics(
|
||||
GattService service,
|
||||
) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myService = service as MyGattService;
|
||||
final myCharactersiticArgses = await _myApi.getCharacteristics(
|
||||
myService.hashCode,
|
||||
);
|
||||
return myCharactersiticArgses.cast<MyGattCharacteristicArgs>().map(
|
||||
(myCharacteristicArgs) {
|
||||
final myCharacteristic = MyGattCharacteristic.fromMyArgs(
|
||||
myService,
|
||||
myCharacteristicArgs,
|
||||
);
|
||||
_myCharacteristics[myCharacteristic.hashCode] = myCharacteristic;
|
||||
return myCharacteristic;
|
||||
},
|
||||
).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattDescriptor>> getDescriptors(
|
||||
GattCharacteristic characteristic,
|
||||
) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myDescriptorArgses = await _myApi.getDescriptors(
|
||||
myCharacteristic.hashCode,
|
||||
);
|
||||
return myDescriptorArgses.cast<MyGattDescriptorArgs>().map(
|
||||
(myDescriptorArgs) {
|
||||
final myDescriptor = MyGattDescriptor.fromMyArgs(
|
||||
myCharacteristic,
|
||||
myDescriptorArgs,
|
||||
);
|
||||
_myDescriptors[myDescriptor.hashCode] = myDescriptor;
|
||||
return myDescriptor;
|
||||
},
|
||||
).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> readCharacteristic(
|
||||
GattCharacteristic characteristic) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
final value = await _myApi.readCharacteristic(
|
||||
myPeripheral.hashCode,
|
||||
myService.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> writeCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
required Uint8List value,
|
||||
required GattCharacteristicWriteType type,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
final myTypeArgs = type.toMyArgs();
|
||||
final myTypeNumber = myTypeArgs.index;
|
||||
await _myApi.writeCharacteristic(
|
||||
myPeripheral.hashCode,
|
||||
myService.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
value,
|
||||
myTypeNumber,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> notifyCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
required bool state,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
await _myApi.notifyCharacteristic(
|
||||
myPeripheral.hashCode,
|
||||
myService.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
state,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myDescriptor = descriptor as MyGattDescriptor;
|
||||
final myCharacteristic = myDescriptor.myCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
final value = await _myApi.readDescriptor(
|
||||
myPeripheral.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
myDescriptor.hashCode,
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> writeDescriptor(
|
||||
GattDescriptor descriptor, {
|
||||
required Uint8List value,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myDescriptor = descriptor as MyGattDescriptor;
|
||||
final myCharacteristic = myDescriptor.myCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
await _myApi.writeDescriptor(
|
||||
myPeripheral.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
myDescriptor.hashCode,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onStateChanged(int myStateNumber) {
|
||||
final myStateArgs = MyCentralStateArgs.values[myStateNumber];
|
||||
final state = myStateArgs.toState();
|
||||
if (_state == state) {
|
||||
return;
|
||||
}
|
||||
_state = state;
|
||||
final eventArgs = CentralStateChangedEventArgs(state);
|
||||
_stateChangedController.add(eventArgs);
|
||||
}
|
||||
|
||||
@override
|
||||
void onDiscovered(
|
||||
MyPeripheralArgs myPeripheralArgs,
|
||||
int rssi,
|
||||
MyAdvertisementArgs myAdvertisementArgs,
|
||||
) {
|
||||
final myPeripheral = MyPeripheral.fromMyArgs(myPeripheralArgs);
|
||||
_myPeripherals[myPeripheral.hashCode] = myPeripheral;
|
||||
final advertisement = myAdvertisementArgs.toAdvertisement();
|
||||
final eventArgs = CentralDiscoveredEventArgs(
|
||||
myPeripheral,
|
||||
rssi,
|
||||
advertisement,
|
||||
);
|
||||
_discoveredController.add(eventArgs);
|
||||
}
|
||||
|
||||
@override
|
||||
void onPeripheralStateChanged(int myPeripheralKey, bool state) {
|
||||
final myPeripheral = _myPeripherals[myPeripheralKey];
|
||||
if (myPeripheral == null) {
|
||||
return;
|
||||
}
|
||||
final eventArgs = PeripheralStateChangedEventArgs(myPeripheral, state);
|
||||
_peripheralStateChangedController.add(eventArgs);
|
||||
}
|
||||
|
||||
@override
|
||||
void onCharacteristicValueChanged(int myCharacteristicKey, Uint8List value) {
|
||||
final myCharacteristic =
|
||||
_myCharacteristics[myCharacteristicKey] as MyGattCharacteristic;
|
||||
final eventArgs = GattCharacteristicValueChangedEventArgs(
|
||||
myCharacteristic,
|
||||
value,
|
||||
);
|
||||
_characteristicValueChangedController.add(eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
extension on MyAdvertisementArgs {
|
||||
Advertisement toAdvertisement() {
|
||||
final serviceUUIDs = this
|
||||
.serviceUUIDs
|
||||
.cast<String>()
|
||||
.map((uuid) => UUID.fromString(uuid))
|
||||
.toList();
|
||||
final serviceData = this.serviceData.cast<String, Uint8List>().map(
|
||||
(uuid, data) {
|
||||
final key = UUID.fromString(uuid);
|
||||
final value = data;
|
||||
return MapEntry(key, value);
|
||||
},
|
||||
);
|
||||
return Advertisement(
|
||||
name: name,
|
||||
manufacturerSpecificData: manufacturerSpecificData.cast<int, Uint8List>(),
|
||||
serviceUUIDs: serviceUUIDs,
|
||||
serviceData: serviceData,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on MyCentralStateArgs {
|
||||
CentralState toState() {
|
||||
return CentralState.values[index];
|
||||
}
|
||||
}
|
||||
|
||||
extension on GattCharacteristicWriteType {
|
||||
MyGattCharacteristicWriteTypeArgs toMyArgs() {
|
||||
return MyGattCharacteristicWriteTypeArgs.values[index];
|
||||
}
|
||||
}
|
267
bluetooth_low_energy_android/lib/src/my_central_manager.dart
Normal file
267
bluetooth_low_energy_android/lib/src/my_central_manager.dart
Normal file
@ -0,0 +1,267 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.dart';
|
||||
import 'my_bluetooth_low_energy_manager.dart';
|
||||
import 'my_gatt_characteristic2.dart';
|
||||
import 'my_gatt_descriptor2.dart';
|
||||
|
||||
class MyCentralManager extends MyBluetoothLowEnergyManager
|
||||
implements CentralManager, MyCentralManagerFlutterApi {
|
||||
final MyCentralManagerHostApi _api;
|
||||
final StreamController<DiscoveredEventArgs> _discoveredController;
|
||||
final StreamController<PeripheralStateChangedEventArgs>
|
||||
_peripheralStateChangedController;
|
||||
final StreamController<GattCharacteristicValueChangedEventArgs>
|
||||
_characteristicValueChangedController;
|
||||
|
||||
MyCentralManager()
|
||||
: _api = MyCentralManagerHostApi(),
|
||||
_discoveredController = StreamController.broadcast(),
|
||||
_peripheralStateChangedController = StreamController.broadcast(),
|
||||
_characteristicValueChangedController = StreamController.broadcast();
|
||||
|
||||
@override
|
||||
Stream<DiscoveredEventArgs> get discovered => _discoveredController.stream;
|
||||
@override
|
||||
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
|
||||
_peripheralStateChangedController.stream;
|
||||
@override
|
||||
Stream<GattCharacteristicValueChangedEventArgs>
|
||||
get characteristicValueChanged =>
|
||||
_characteristicValueChangedController.stream;
|
||||
|
||||
@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 maximumWriteLength =
|
||||
await _api.getMaximumWriteLength(peripheralHashCodeArgs);
|
||||
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 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,
|
||||
MyAdvertiseDataArgs advertiseDataArgs,
|
||||
) {
|
||||
final peripheral = peripheralArgs.toPeripheral();
|
||||
final rssi = rssiArgs;
|
||||
final advertiseData = advertiseDataArgs.toAdvertiseData();
|
||||
final eventArgs = DiscoveredEventArgs(
|
||||
peripheral,
|
||||
rssi,
|
||||
advertiseData,
|
||||
);
|
||||
_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);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_gatt_service.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyGattCharacteristic extends MyObject implements GattCharacteristic {
|
||||
final MyGattService myService;
|
||||
@override
|
||||
final UUID uuid;
|
||||
@override
|
||||
final List<GattCharacteristicProperty> properties;
|
||||
|
||||
MyGattCharacteristic(
|
||||
super.hashCode,
|
||||
this.myService,
|
||||
this.uuid,
|
||||
this.properties,
|
||||
);
|
||||
|
||||
factory MyGattCharacteristic.fromMyArgs(
|
||||
MyGattService myService,
|
||||
MyGattCharacteristicArgs myArgs,
|
||||
) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
final properties = myArgs.myPropertyNumbers.cast<int>().map(
|
||||
(myPropertyNumber) {
|
||||
final myPropertyArgs =
|
||||
MyGattCharacteristicPropertyArgs.values[myPropertyNumber];
|
||||
return myPropertyArgs.toProperty();
|
||||
},
|
||||
).toList();
|
||||
return MyGattCharacteristic(hashCode, myService, uuid, properties);
|
||||
}
|
||||
}
|
||||
|
||||
extension on MyGattCharacteristicPropertyArgs {
|
||||
GattCharacteristicProperty toProperty() {
|
||||
return GattCharacteristicProperty.values[index];
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
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;
|
||||
|
||||
MyGattCharacteristic2({
|
||||
super.hashCode,
|
||||
required super.uuid,
|
||||
required super.properties,
|
||||
required List<MyGattDescriptor2> descriptors,
|
||||
}) : super(descriptors: descriptors);
|
||||
|
||||
@override
|
||||
List<MyGattDescriptor2> get descriptors =>
|
||||
super.descriptors.cast<MyGattDescriptor2>();
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_gatt_characteristic.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyGattDescriptor extends MyObject implements GattDescriptor {
|
||||
final MyGattCharacteristic myCharacteristic;
|
||||
@override
|
||||
final UUID uuid;
|
||||
|
||||
MyGattDescriptor(super.hashCode, this.myCharacteristic, this.uuid);
|
||||
|
||||
factory MyGattDescriptor.fromMyArgs(
|
||||
MyGattCharacteristic myCharacteristic,
|
||||
MyGattDescriptorArgs myArgs,
|
||||
) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
return MyGattDescriptor(hashCode, myCharacteristic, uuid);
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
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;
|
||||
|
||||
MyGattDescriptor2({
|
||||
super.hashCode,
|
||||
required super.uuid,
|
||||
});
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_object.dart';
|
||||
import 'my_peripheral.dart';
|
||||
|
||||
class MyGattService extends MyObject implements GattService {
|
||||
final MyPeripheral myPeripheral;
|
||||
@override
|
||||
final UUID uuid;
|
||||
|
||||
MyGattService(super.hashCode, this.myPeripheral, this.uuid);
|
||||
|
||||
factory MyGattService.fromMyArgs(
|
||||
MyPeripheral myPeripheral,
|
||||
MyGattServiceArgs myArgs,
|
||||
) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
return MyGattService(hashCode, myPeripheral, uuid);
|
||||
}
|
||||
}
|
17
bluetooth_low_energy_android/lib/src/my_gatt_service2.dart
Normal file
17
bluetooth_low_energy_android/lib/src/my_gatt_service2.dart
Normal file
@ -0,0 +1,17 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_gatt_characteristic2.dart';
|
||||
|
||||
class MyGattService2 extends MyGattService {
|
||||
late final MyPeripheral peripheral;
|
||||
|
||||
MyGattService2({
|
||||
super.hashCode,
|
||||
required super.uuid,
|
||||
required List<MyGattCharacteristic2> characteristics,
|
||||
}) : super(characteristics: characteristics);
|
||||
|
||||
@override
|
||||
List<MyGattCharacteristic2> get characteristics =>
|
||||
super.characteristics.cast<MyGattCharacteristic2>();
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyPeripheral extends MyObject implements Peripheral {
|
||||
@override
|
||||
final UUID uuid;
|
||||
|
||||
MyPeripheral(super.hashCode, this.uuid);
|
||||
|
||||
factory MyPeripheral.fromMyArgs(MyPeripheralArgs myArgs) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
return MyPeripheral(hashCode, uuid);
|
||||
}
|
||||
}
|
228
bluetooth_low_energy_android/lib/src/my_peripheral_manager.dart
Normal file
228
bluetooth_low_energy_android/lib/src/my_peripheral_manager.dart
Normal file
@ -0,0 +1,228 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.dart';
|
||||
import 'my_bluetooth_low_energy_manager.dart';
|
||||
|
||||
class MyPeripheralManager extends MyBluetoothLowEnergyManager
|
||||
implements PeripheralManager, MyPeripheralManagerFlutterApi {
|
||||
final MyPeripheralManagerHostApi _api;
|
||||
final StreamController<ReadGattCharacteristicCommandEventArgs>
|
||||
_readCharacteristicCommandReceivedController;
|
||||
final StreamController<WriteGattCharacteristicCommandEventArgs>
|
||||
_writeCharacteristicCommandReceivedController;
|
||||
final StreamController<NotifyGattCharacteristicCommandEventArgs>
|
||||
_notifyCharacteristicCommandReceivedController;
|
||||
|
||||
MyPeripheralManager()
|
||||
: _api = MyPeripheralManagerHostApi(),
|
||||
_readCharacteristicCommandReceivedController =
|
||||
StreamController.broadcast(),
|
||||
_writeCharacteristicCommandReceivedController =
|
||||
StreamController.broadcast(),
|
||||
_notifyCharacteristicCommandReceivedController =
|
||||
StreamController.broadcast();
|
||||
|
||||
@override
|
||||
Stream<ReadGattCharacteristicCommandEventArgs>
|
||||
get readCharacteristicCommandReceived =>
|
||||
_readCharacteristicCommandReceivedController.stream;
|
||||
|
||||
@override
|
||||
Stream<WriteGattCharacteristicCommandEventArgs>
|
||||
get writeCharacteristicCommandReceived =>
|
||||
_writeCharacteristicCommandReceivedController.stream;
|
||||
|
||||
@override
|
||||
Stream<NotifyGattCharacteristicCommandEventArgs>
|
||||
get notifyCharacteristicCommandReceived =>
|
||||
_notifyCharacteristicCommandReceivedController.stream;
|
||||
|
||||
@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(AdvertiseData advertiseData) async {
|
||||
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
|
||||
final advertiseDataArgs = advertiseData.toArgs();
|
||||
await _api.startAdvertising(advertiseDataArgs);
|
||||
}
|
||||
|
||||
@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,
|
||||
GattCharacteristic characteristic,
|
||||
int id,
|
||||
int offset,
|
||||
bool status,
|
||||
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,
|
||||
GattCharacteristic characteristic,
|
||||
int id,
|
||||
int offset,
|
||||
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,
|
||||
GattCharacteristic characteristic,
|
||||
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);
|
||||
}
|
||||
}
|
@ -12,126 +12,215 @@ import 'package:pigeon/pigeon.dart';
|
||||
),
|
||||
)
|
||||
@HostApi()
|
||||
abstract class MyCentralControllerHostApi {
|
||||
abstract class MyCentralManagerHostApi {
|
||||
@async
|
||||
MyCentralControllerArgs setUp();
|
||||
void tearDown();
|
||||
MyCentralManagerArgs setUp();
|
||||
@async
|
||||
void startDiscovery();
|
||||
void stopDiscovery();
|
||||
@async
|
||||
void connect(int myPeripheralKey);
|
||||
void connect(int peripheralHashCodeArgs);
|
||||
@async
|
||||
void disconnect(int myPeripheralKey);
|
||||
void disconnect(int peripheralHashCodeArgs);
|
||||
@async
|
||||
int getMaximumWriteLength(int myPeripheralKey);
|
||||
int getMaximumWriteLength(int peripheralHashCodeArgs);
|
||||
@async
|
||||
void discoverGATT(int myPeripheralKey);
|
||||
List<MyGattServiceArgs> getServices(int myPeripheralKey);
|
||||
List<MyGattCharacteristicArgs> getCharacteristics(int myServiceKey);
|
||||
List<MyGattDescriptorArgs> getDescriptors(int myCharacteristicKey);
|
||||
int readRSSI(int peripheralHashCodeArgs);
|
||||
@async
|
||||
List<MyGattServiceArgs> discoverGATT(int peripheralHashCodeArgs);
|
||||
@async
|
||||
Uint8List readCharacteristic(
|
||||
int myPeripheralKey,
|
||||
int myServiceKey,
|
||||
int myCharacteristicKey,
|
||||
int peripheralHashCodeArgs,
|
||||
int characteristicHashCodeArgs,
|
||||
);
|
||||
@async
|
||||
void writeCharacteristic(
|
||||
int myPeripheralKey,
|
||||
int myServiceKey,
|
||||
int myCharacteristicKey,
|
||||
Uint8List value,
|
||||
int myTypeNumber,
|
||||
int peripheralHashCodeArgs,
|
||||
int characteristicHashCodeArgs,
|
||||
Uint8List valueArgs,
|
||||
int typeNumberArgs,
|
||||
);
|
||||
@async
|
||||
void notifyCharacteristic(
|
||||
int myPeripheralKey,
|
||||
int myServiceKey,
|
||||
int myCharacteristicKey,
|
||||
bool state,
|
||||
int peripheralHashCodeArgs,
|
||||
int characteristicHashCodeArgs,
|
||||
bool stateArgs,
|
||||
);
|
||||
@async
|
||||
Uint8List readDescriptor(
|
||||
int myPeripheralKey,
|
||||
int myCharacteristicKey,
|
||||
int myDescriptorKey,
|
||||
int peripheralHashCodeArgs,
|
||||
int descriptorHashCodeArgs,
|
||||
);
|
||||
@async
|
||||
void writeDescriptor(
|
||||
int myPeripheralKey,
|
||||
int myCharacteristicKey,
|
||||
int myDescriptorKey,
|
||||
Uint8List value,
|
||||
int peripheralHashCodeArgs,
|
||||
int descriptorHashCodeArgs,
|
||||
Uint8List valueArgs,
|
||||
);
|
||||
}
|
||||
|
||||
@FlutterApi()
|
||||
abstract class MyCentralControllerFlutterApi {
|
||||
void onStateChanged(int myStateNumber);
|
||||
abstract class MyCentralManagerFlutterApi {
|
||||
void onStateChanged(int stateNumberArgs);
|
||||
void onDiscovered(
|
||||
MyPeripheralArgs myPeripheralArgs,
|
||||
int rssi,
|
||||
MyAdvertisementArgs myAdvertisementArgs,
|
||||
MyPeripheralArgs peripheralArgs,
|
||||
int rssiArgs,
|
||||
MyAdvertiseDataArgs advertiseDataArgs,
|
||||
);
|
||||
void onPeripheralStateChanged(
|
||||
MyPeripheralArgs peripheralArgs,
|
||||
bool stateArgs,
|
||||
);
|
||||
void onCharacteristicValueChanged(
|
||||
MyGattCharacteristicArgs characteristicArgs,
|
||||
Uint8List valueArgs,
|
||||
);
|
||||
void onPeripheralStateChanged(int myPeripheralKey, bool state);
|
||||
void onCharacteristicValueChanged(int myCharacteristicKey, Uint8List value);
|
||||
}
|
||||
|
||||
class MyCentralControllerArgs {
|
||||
final int myStateNumber;
|
||||
@HostApi()
|
||||
abstract class MyPeripheralManagerHostApi {
|
||||
@async
|
||||
MyPeripheralManagerArgs setUp();
|
||||
@async
|
||||
void addService(MyGattServiceArgs serviceArgs);
|
||||
void removeService(int serviceHashCodeArgs);
|
||||
void clearServices();
|
||||
@async
|
||||
void startAdvertising(MyAdvertiseDataArgs advertiseDataArgs);
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
MyCentralControllerArgs(this.myStateNumber);
|
||||
@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 key;
|
||||
final String uuid;
|
||||
final int hashCodeArgs;
|
||||
final String uuidArgs;
|
||||
|
||||
MyPeripheralArgs(this.key, this.uuid);
|
||||
MyPeripheralArgs(this.hashCodeArgs, this.uuidArgs);
|
||||
}
|
||||
|
||||
class MyAdvertisementArgs {
|
||||
final String? name;
|
||||
final Map<int?, Uint8List?> manufacturerSpecificData;
|
||||
final List<String?> serviceUUIDs;
|
||||
final Map<String?, Uint8List?> serviceData;
|
||||
class MyAdvertiseDataArgs {
|
||||
final String? nameArgs;
|
||||
final List<String?> serviceUUIDsArgs;
|
||||
final Map<String?, Uint8List?> serviceDataArgs;
|
||||
final MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs;
|
||||
|
||||
MyAdvertisementArgs(
|
||||
this.name,
|
||||
this.manufacturerSpecificData,
|
||||
this.serviceUUIDs,
|
||||
this.serviceData,
|
||||
MyAdvertiseDataArgs(
|
||||
this.nameArgs,
|
||||
this.serviceUUIDsArgs,
|
||||
this.serviceDataArgs,
|
||||
this.manufacturerSpecificDataArgs,
|
||||
);
|
||||
}
|
||||
|
||||
class MyGattServiceArgs {
|
||||
final int key;
|
||||
final String uuid;
|
||||
class MyManufacturerSpecificDataArgs {
|
||||
final int idArgs;
|
||||
final Uint8List dataArgs;
|
||||
|
||||
MyGattServiceArgs(this.key, this.uuid);
|
||||
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 key;
|
||||
final String uuid;
|
||||
final List<int?> myPropertyNumbers;
|
||||
final int hashCodeArgs;
|
||||
final String uuidArgs;
|
||||
final List<int?> propertyNumbersArgs;
|
||||
final List<MyGattDescriptorArgs?> descriptorsArgs;
|
||||
|
||||
MyGattCharacteristicArgs(
|
||||
this.key,
|
||||
this.uuid,
|
||||
this.myPropertyNumbers,
|
||||
this.hashCodeArgs,
|
||||
this.uuidArgs,
|
||||
this.propertyNumbersArgs,
|
||||
this.descriptorsArgs,
|
||||
);
|
||||
}
|
||||
|
||||
class MyGattDescriptorArgs {
|
||||
final int key;
|
||||
final String uuid;
|
||||
final int hashCodeArgs;
|
||||
final String uuidArgs;
|
||||
final Uint8List? valueArgs;
|
||||
|
||||
MyGattDescriptorArgs(this.key, this.uuid);
|
||||
MyGattDescriptorArgs(
|
||||
this.hashCodeArgs,
|
||||
this.uuidArgs,
|
||||
this.valueArgs,
|
||||
);
|
||||
}
|
||||
|
||||
enum MyCentralStateArgs {
|
||||
enum MyBluetoothLowEnergyStateArgs {
|
||||
unknown,
|
||||
unsupported,
|
||||
unauthorized,
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: bluetooth_low_energy_android
|
||||
description: Android implementation of the bluetooth_low_energy plugin.
|
||||
version: 2.2.1
|
||||
version: 3.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: ^2.2.0
|
||||
bluetooth_low_energy_platform_interface: ^3.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
pigeon: ^10.1.6
|
||||
pigeon: ^11.0.1
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
|
@ -1,3 +1,37 @@
|
||||
## 3.0.0
|
||||
|
||||
* Add `PeripheralManager` api.
|
||||
* Add `CentralManager#readRSSI` method.
|
||||
* Add `CentralManager.instance` api.
|
||||
* Add `PeripheralManager.instance` api.
|
||||
* Move `CentralController` to `CentralManager`.
|
||||
* Move `CentralState` to `BluetoothLowEnergyState`.
|
||||
* Move `CentralDiscoveredEventArgs` to `DiscoveredEventArgs`.
|
||||
* Move `Advertisement` class to `AdvertiseData` class.
|
||||
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
|
||||
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
|
||||
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
|
||||
* Fix known issues.
|
||||
|
||||
## 3.0.0-dev.4
|
||||
|
||||
* Move `Advertisement` class to `AdvertiseData` class.
|
||||
|
||||
## 3.0.0-dev.3
|
||||
|
||||
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
|
||||
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
|
||||
|
||||
## 3.0.0-dev.2
|
||||
|
||||
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
|
||||
* Add `CentralManager.instance` api.
|
||||
* Add `PeripheralManager.instance` api.
|
||||
|
||||
## 3.0.0-dev.1
|
||||
|
||||
* Implement new api.
|
||||
|
||||
## 2.2.0
|
||||
|
||||
* Add `CentralController#getMaximumWriteLength` method.
|
||||
|
@ -17,7 +17,9 @@ public class BluetoothLowEnergyDarwin: NSObject, FlutterPlugin {
|
||||
#else
|
||||
#error("Unsupported platform.")
|
||||
#endif
|
||||
let centralController = MyCentralController(binaryMessenger)
|
||||
MyCentralControllerHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: centralController)
|
||||
let centralManager = MyCentralManager(binaryMessenger)
|
||||
let peripheralManager = MyPeripheralManager(binaryMessenger)
|
||||
MyCentralManagerHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: centralManager)
|
||||
MyPeripheralManagerHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: peripheralManager)
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
286
bluetooth_low_energy_darwin/darwin/Classes/MyApi.swift
Normal file
286
bluetooth_low_energy_darwin/darwin/Classes/MyApi.swift
Normal file
@ -0,0 +1,286 @@
|
||||
//
|
||||
// MyApi.swift
|
||||
// bluetooth_low_energy_darwin
|
||||
//
|
||||
// Created by 闫守旺 on 2023/9/28.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreBluetooth
|
||||
|
||||
#if os(iOS)
|
||||
import Flutter
|
||||
#elseif os(macOS)
|
||||
import FlutterMacOS
|
||||
#else
|
||||
#error("Unsupported platform.")
|
||||
#endif
|
||||
|
||||
extension CBManagerState {
|
||||
func toArgs() -> MyBluetoothLowEnergyStateArgs {
|
||||
switch self {
|
||||
case .unauthorized:
|
||||
return .unauthorized
|
||||
case .poweredOff:
|
||||
return .poweredOff
|
||||
case .poweredOn:
|
||||
return .poweredOn
|
||||
default:
|
||||
return .unsupported
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CBPeer {
|
||||
var uuidArgs: String { identifier.uuidString }
|
||||
}
|
||||
|
||||
extension CBCentral {
|
||||
func toArgs() -> MyCentralArgs {
|
||||
let hashCodeArgs = Int64(hash)
|
||||
return MyCentralArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs)
|
||||
}
|
||||
}
|
||||
|
||||
extension CBPeripheral {
|
||||
func toArgs() -> MyPeripheralArgs {
|
||||
let hashCodeArgs = Int64(hash)
|
||||
return MyPeripheralArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs)
|
||||
}
|
||||
}
|
||||
|
||||
extension CBAttribute {
|
||||
var uuidArgs: String { uuid.uuidString }
|
||||
}
|
||||
|
||||
extension CBService {
|
||||
func toArgs(_ characteristicsArgs: [MyGattCharacteristicArgs]) -> MyGattServiceArgs {
|
||||
let hashCodeArgs = Int64(hash)
|
||||
return MyGattServiceArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, characteristicsArgs: characteristicsArgs)
|
||||
}
|
||||
}
|
||||
|
||||
extension CBCharacteristic {
|
||||
func toArgs(_ descriptorsArgs: [MyGattDescriptorArgs]) -> MyGattCharacteristicArgs {
|
||||
let hashCodeArgs = Int64(hash)
|
||||
return MyGattCharacteristicArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs, propertyNumbersArgs: propertyNumbersArgs, descriptorsArgs: descriptorsArgs)
|
||||
}
|
||||
|
||||
var propertyNumbersArgs: [Int64] {
|
||||
var propertiesArgs = [MyGattCharacteristicPropertyArgs]()
|
||||
let properties = self.properties
|
||||
if properties.contains(.read) {
|
||||
propertiesArgs.append(.read)
|
||||
}
|
||||
if properties.contains(.write) {
|
||||
propertiesArgs.append(.write)
|
||||
}
|
||||
if properties.contains(.writeWithoutResponse) {
|
||||
propertiesArgs.append(.writeWithoutResponse)
|
||||
}
|
||||
if properties.contains(.notify) {
|
||||
propertiesArgs.append(.notify)
|
||||
}
|
||||
if properties.contains(.indicate) {
|
||||
propertiesArgs.append(.indicate)
|
||||
}
|
||||
return propertiesArgs.map { args in Int64(args.rawValue) }
|
||||
}
|
||||
}
|
||||
|
||||
extension CBDescriptor {
|
||||
func toArgs() -> MyGattDescriptorArgs {
|
||||
let hashCodeArgs = Int64(hash)
|
||||
return MyGattDescriptorArgs(hashCodeArgs: hashCodeArgs, uuidArgs: uuidArgs)
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattCharacteristicWriteTypeArgs {
|
||||
func toWriteType() -> CBCharacteristicWriteType {
|
||||
switch self {
|
||||
case .withResponse:
|
||||
return .withResponse
|
||||
case .withoutResponse:
|
||||
return .withoutResponse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 UInt16 {
|
||||
var data: Data {
|
||||
var source = self
|
||||
return Data(bytes: &source, count: MemoryLayout<UInt16>.size)
|
||||
}
|
||||
}
|
||||
|
||||
extension [String: Any] {
|
||||
func toAdvertiseDataArgs() -> MyAdvertiseDataArgs {
|
||||
let nameArgs = self[CBAdvertisementDataLocalNameKey] as? String
|
||||
let serviceUUIDs = self[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID] ?? []
|
||||
let serviceUUIDsArgs = serviceUUIDs.map { uuid in uuid.uuidString }
|
||||
let serviceData = self[CBAdvertisementDataServiceDataKey] as? [CBUUID: Data] ?? [:]
|
||||
let serviceDataArgsKeyWithValues = serviceData.map { (uuid, data) in
|
||||
let uuidArgs = uuid.uuidString
|
||||
let dataArgs = FlutterStandardTypedData(bytes: data)
|
||||
return (uuidArgs, dataArgs)
|
||||
}
|
||||
let serviceDataArgs = [String?: FlutterStandardTypedData?](uniqueKeysWithValues: serviceDataArgsKeyWithValues)
|
||||
let manufacturerSpecificData = self[CBAdvertisementDataManufacturerDataKey] as? Data
|
||||
let manufacturerSpecificDataArgs = manufacturerSpecificData?.toManufacturerSpecificDataArgs()
|
||||
return MyAdvertiseDataArgs(nameArgs: nameArgs, serviceUUIDsArgs: serviceUUIDsArgs, serviceDataArgs: serviceDataArgs, manufacturerSpecificDataArgs: manufacturerSpecificDataArgs)
|
||||
}
|
||||
}
|
||||
|
||||
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 MyAdvertiseDataArgs {
|
||||
func toAdvertiseData() throws -> [String : Any] {
|
||||
// CoreBluetooth only support `CBAdvertisementDataLocalNameKey` and `CBAdvertisementDataServiceUUIDsKey`, see https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising
|
||||
var advertiseData = [String: Any]()
|
||||
if nameArgs != nil {
|
||||
let name = nameArgs!
|
||||
advertiseData[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)
|
||||
}
|
||||
advertiseData[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
|
||||
// }
|
||||
// advertiseData[CBAdvertisementDataServiceDataKey] = serviceData
|
||||
// }
|
||||
// if manufacturerSpecificDataArgs != nil {
|
||||
// let manufacturerSpecificData = manufacturerSpecificDataArgs!.toManufacturerSpecificData()
|
||||
// advertiseData[CBAdvertisementDataManufacturerDataKey] = manufacturerSpecificData
|
||||
// }
|
||||
return advertiseData
|
||||
}
|
||||
}
|
||||
|
||||
//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 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 MyGattDescriptorArgs {
|
||||
func toDescriptor() -> CBMutableDescriptor {
|
||||
let type = CBUUID(string: uuidArgs)
|
||||
let value = valueArgs?.data
|
||||
return CBMutableDescriptor(type: type, value: value)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
@ -1,737 +0,0 @@
|
||||
//
|
||||
// MyCentralController.swift
|
||||
// bluetooth_low_energy_ios
|
||||
//
|
||||
// Created by 闫守旺 on 2023/8/13.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreBluetooth
|
||||
#if os(iOS)
|
||||
import Flutter
|
||||
#elseif os(macOS)
|
||||
import FlutterMacOS
|
||||
#else
|
||||
#error("Unsupported platform.")
|
||||
#endif
|
||||
|
||||
class MyCentralController: MyCentralControllerHostApi {
|
||||
init(_ binaryMessenger: FlutterBinaryMessenger) {
|
||||
myApi = MyCentralControllerFlutterApi(binaryMessenger: binaryMessenger)
|
||||
}
|
||||
|
||||
private let myApi: MyCentralControllerFlutterApi
|
||||
private lazy var myCentralManagerDelegate = MyCentralManagerDelegate(self)
|
||||
private lazy var myPeripheralDelegate = MyPeripheralDelegate(self)
|
||||
private let centralManager = CBCentralManager()
|
||||
|
||||
private var cachedPeripherals = [Int: CBPeripheral]()
|
||||
private var cachedServices = [Int: [Int: CBService]]()
|
||||
private var cachedCharacteristics = [Int: [Int: CBCharacteristic]]()
|
||||
private var cachedDescriptors = [Int: [Int: CBDescriptor]]()
|
||||
|
||||
var setUpCompletion: ((Result<MyCentralControllerArgs, Error>) -> Void)?
|
||||
var connectCompletions = [Int: (Result<Void, Error>) -> Void]()
|
||||
var disconnectCompletions = [Int: (Result<Void, Error>) -> Void]()
|
||||
var discoverGattCompletions = [Int: (Result<Void, Error>) -> Void]()
|
||||
var unfinishedServices = [Int: [CBService]]()
|
||||
var unfinishedCharacteristics = [Int: [CBCharacteristic]]()
|
||||
var readCharacteristicCompletions = [Int: (Result<FlutterStandardTypedData, Error>) -> Void]()
|
||||
var writeCharacteristicCompletions = [Int: (Result<Void, Error>) -> Void]()
|
||||
var notifyCharacteristicCompletions = [Int: (Result<Void, Error>) -> Void]()
|
||||
var readDescriptorCompletions = [Int: (Result<FlutterStandardTypedData, Error>) -> Void]()
|
||||
var writeDescriptorCompletions = [Int: (Result<Void, Error>) -> Void]()
|
||||
|
||||
func setUp(completion: @escaping (Result<MyCentralControllerArgs, Error>) -> Void) {
|
||||
do {
|
||||
let unfinishedCompletion = setUpCompletion
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
centralManager.delegate = myCentralManagerDelegate
|
||||
if centralManager.state == .unknown {
|
||||
setUpCompletion = completion
|
||||
} else {
|
||||
let myStateArgs = centralManager.state.toMyArgs()
|
||||
let myStateNumber = Int64(myStateArgs.rawValue)
|
||||
let myArgs = MyCentralControllerArgs(myStateNumber: myStateNumber)
|
||||
completion(.success(myArgs))
|
||||
}
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func tearDown() throws {
|
||||
centralManager.delegate = nil
|
||||
if(centralManager.isScanning) {
|
||||
centralManager.stopScan()
|
||||
}
|
||||
for peripheral in cachedPeripherals.values {
|
||||
peripheral.delegate = nil
|
||||
if peripheral.state != .disconnected {
|
||||
centralManager.cancelPeripheralConnection(peripheral)
|
||||
}
|
||||
}
|
||||
cachedPeripherals.removeAll()
|
||||
cachedServices.removeAll()
|
||||
cachedCharacteristics.removeAll()
|
||||
cachedDescriptors.removeAll()
|
||||
}
|
||||
|
||||
func startDiscovery() throws {
|
||||
let options = [CBCentralManagerScanOptionAllowDuplicatesKey: true]
|
||||
centralManager.scanForPeripherals(withServices: nil, options: options)
|
||||
}
|
||||
|
||||
func stopDiscovery() throws {
|
||||
centralManager.stopScan()
|
||||
}
|
||||
|
||||
func connect(myPeripheralKey: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
do {
|
||||
let peripheralKey = Int(myPeripheralKey)
|
||||
let unfinishedCompletion = connectCompletions[peripheralKey]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
guard let peripheral = cachedPeripherals[peripheralKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
centralManager.connect(peripheral)
|
||||
connectCompletions[peripheralKey] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func disconnect(myPeripheralKey: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
do {
|
||||
let peripheralKey = Int(myPeripheralKey)
|
||||
let unfinishedCompletion = disconnectCompletions[peripheralKey]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
guard let peripheral = cachedPeripherals[peripheralKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
centralManager.cancelPeripheralConnection(peripheral)
|
||||
disconnectCompletions[peripheralKey] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func getMaximumWriteLength(myPeripheralKey: Int64, myTypeNumber: Int64) throws -> Int64 {
|
||||
let peripheralKey = Int(myPeripheralKey)
|
||||
guard let peripheral = cachedPeripherals[peripheralKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let myTypeRawValue = Int(myTypeNumber)
|
||||
guard let myTypeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: myTypeRawValue) else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let type = myTypeArgs.toType()
|
||||
let maximumWriteLength32 = peripheral.maximumWriteValueLength(for: type)
|
||||
let maximumWriteLength = Int64(maximumWriteLength32)
|
||||
return maximumWriteLength
|
||||
}
|
||||
|
||||
func discoverGATT(myPeripheralKey: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
do {
|
||||
let peripheralKey = Int(myPeripheralKey)
|
||||
let unfinishedCompletion = discoverGattCompletions[peripheralKey]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
guard let peripheral = cachedPeripherals[peripheralKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
peripheral.discoverServices(nil)
|
||||
discoverGattCompletions[peripheralKey] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func getServices(myPeripheralKey: Int64) throws -> [MyGattServiceArgs] {
|
||||
let peripheralKey = Int(myPeripheralKey)
|
||||
guard let services = cachedServices[peripheralKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
return services.map { (key, service) in
|
||||
return service.toMyArgs()
|
||||
}
|
||||
}
|
||||
|
||||
func getCharacteristics(myServiceKey: Int64) throws -> [MyGattCharacteristicArgs] {
|
||||
let serviceKey = Int(myServiceKey)
|
||||
guard let characteristics = cachedCharacteristics[serviceKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
return characteristics.map { (key, characteristic) in
|
||||
return characteristic.toMyArgs()
|
||||
}
|
||||
}
|
||||
|
||||
func getDescriptors(myCharacteristicKey: Int64) throws -> [MyGattDescriptorArgs] {
|
||||
let characteristicKey = Int(myCharacteristicKey)
|
||||
guard let descriptors = cachedDescriptors[characteristicKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
return descriptors.map { (key, descriptor) in
|
||||
return descriptor.toMyArgs()
|
||||
}
|
||||
}
|
||||
|
||||
func readCharacteristic(myPeripheralKey: Int64, myServiceKey: Int64, myCharacteristicKey: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
|
||||
do {
|
||||
let peripheralKey = Int(myPeripheralKey)
|
||||
guard let peripheral = cachedPeripherals[peripheralKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let serviceKey = Int(myServiceKey)
|
||||
guard let characteristics = cachedCharacteristics[serviceKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let characteristicKey = Int(myCharacteristicKey)
|
||||
guard let characteristic = characteristics[characteristicKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let unfinishedCompletion = readCharacteristicCompletions[characteristicKey]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
peripheral.readValue(for: characteristic)
|
||||
readCharacteristicCompletions[characteristicKey] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func writeCharacteristic(myPeripheralKey: Int64, myServiceKey: Int64, myCharacteristicKey: Int64, value: FlutterStandardTypedData, myTypeNumber: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
do {
|
||||
let peripheralKey = Int(myPeripheralKey)
|
||||
guard let peripheral = cachedPeripherals[peripheralKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let serviceKey = Int(myServiceKey)
|
||||
guard let characteristics = cachedCharacteristics[serviceKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let characteristicKey = Int(myCharacteristicKey)
|
||||
guard let characteristic = characteristics[characteristicKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let data = value.data
|
||||
let myTypeRawValue = Int(myTypeNumber)
|
||||
guard let myTypeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: myTypeRawValue) else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let type = myTypeArgs.toType()
|
||||
let unfinishedCompletion = writeCharacteristicCompletions[characteristicKey]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
peripheral.writeValue(data, for: characteristic, type: type)
|
||||
writeCharacteristicCompletions[characteristicKey] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func notifyCharacteristic(myPeripheralKey: Int64, myServiceKey: Int64, myCharacteristicKey: Int64, state: Bool, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
do {
|
||||
let peripheralKey = Int(myPeripheralKey)
|
||||
guard let peripheral = cachedPeripherals[peripheralKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let serviceKey = Int(myServiceKey)
|
||||
guard let characteristics = cachedCharacteristics[serviceKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let characteristicKey = Int(myCharacteristicKey)
|
||||
guard let characteristic = characteristics[characteristicKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let unfinishedCompletion = notifyCharacteristicCompletions[characteristicKey]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
peripheral.setNotifyValue(state, for: characteristic)
|
||||
notifyCharacteristicCompletions[characteristicKey] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func readDescriptor(myPeripheralKey: Int64, myCharacteristicKey: Int64, myDescriptorKey: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
|
||||
do {
|
||||
let peripheralKey = Int(myPeripheralKey)
|
||||
guard let peripheral = cachedPeripherals[peripheralKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let characteristicKey = Int(myCharacteristicKey)
|
||||
guard let descriptors = cachedDescriptors[characteristicKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let descriptorKey = Int(myDescriptorKey)
|
||||
guard let descriptor = descriptors[descriptorKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let unfinishedCompletion = readDescriptorCompletions[descriptorKey]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
peripheral.readValue(for: descriptor)
|
||||
readDescriptorCompletions[descriptorKey] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func writeDescriptor(myPeripheralKey: Int64, myCharacteristicKey: Int64, myDescriptorKey: Int64, value: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
do {
|
||||
let peripheralKey = Int(myPeripheralKey)
|
||||
guard let peripheral = cachedPeripherals[peripheralKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let characteristicKey = Int(myCharacteristicKey)
|
||||
guard let descriptors = cachedDescriptors[characteristicKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let descriptorKey = Int(myDescriptorKey)
|
||||
guard let descriptor = descriptors[descriptorKey] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let data = value.data
|
||||
let unfinishedCompletion = writeDescriptorCompletions[descriptorKey]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
peripheral.writeValue(data, for: descriptor)
|
||||
writeDescriptorCompletions[descriptorKey] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func didUpdateState(_ state: CBManagerState) {
|
||||
let completion = setUpCompletion
|
||||
if state != .unknown && completion != nil {
|
||||
let myStateArgs = state.toMyArgs()
|
||||
let myStateNumber = Int64(myStateArgs.rawValue)
|
||||
let myArgs = MyCentralControllerArgs(myStateNumber: myStateNumber)
|
||||
completion!(.success(myArgs))
|
||||
setUpCompletion = nil
|
||||
}
|
||||
let myStateArgs = state.toMyArgs()
|
||||
let myStateNumber = Int64(myStateArgs.rawValue)
|
||||
myApi.onStateChanged(myStateNumber: myStateNumber) {}
|
||||
}
|
||||
|
||||
|
||||
func didDiscover(_ peripheral: CBPeripheral, _ advertisementData: [String : Any], _ rssiNumber: NSNumber) {
|
||||
let peripheralKey = peripheral.hash
|
||||
if cachedPeripherals[peripheralKey] == nil {
|
||||
peripheral.delegate = myPeripheralDelegate
|
||||
cachedPeripherals[peripheralKey] = peripheral
|
||||
}
|
||||
let myPeripheralArgs = peripheral.toMyArgs()
|
||||
let rssi = rssiNumber.int64Value
|
||||
let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String
|
||||
let rawManufacturerSpecificData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data
|
||||
var manufacturerSpecificData = [Int64: FlutterStandardTypedData]()
|
||||
if rawManufacturerSpecificData != nil {
|
||||
do {
|
||||
guard let data = rawManufacturerSpecificData else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
guard data.count >= 2 else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let key = Int64(data[0]) | (Int64(data[1]) << 8)
|
||||
let bytes = data.count > 2 ? data[2...data.count-1] : Data()
|
||||
let value = FlutterStandardTypedData(bytes: bytes)
|
||||
manufacturerSpecificData[key] = value
|
||||
} catch {
|
||||
manufacturerSpecificData = [:]
|
||||
}
|
||||
}
|
||||
let rawServiceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID] ?? []
|
||||
let serviceUUIDs = rawServiceUUIDs.map { uuid in uuid.uuidString }
|
||||
let rawServiceData = advertisementData[CBAdvertisementDataServiceDataKey] as? [CBUUID: Data] ?? [:]
|
||||
let elements = rawServiceData.map { (uuid, data) in
|
||||
let key = uuid.uuidString
|
||||
let value = FlutterStandardTypedData(bytes: data)
|
||||
return (key, value)
|
||||
}
|
||||
let serviceData = [String?: FlutterStandardTypedData?](uniqueKeysWithValues: elements)
|
||||
let myAdvertisementArgs = MyAdvertisementArgs(name: name, manufacturerSpecificData: manufacturerSpecificData, serviceUUIDs: serviceUUIDs, serviceData: serviceData)
|
||||
myApi.onDiscovered(myPeripheralArgs: myPeripheralArgs, rssi: rssi, myAdvertisementArgs: myAdvertisementArgs) {}
|
||||
}
|
||||
|
||||
func didConnect(_ peripheral: CBPeripheral) {
|
||||
let peripheralKey = peripheral.hash
|
||||
let myPeripheralKey = Int64(peripheralKey)
|
||||
myApi.onPeripheralStateChanged(myPeripheralKey: myPeripheralKey, state: true) {}
|
||||
guard let completion = connectCompletions.removeValue(forKey: peripheralKey) else {
|
||||
return
|
||||
}
|
||||
completion(.success(()))
|
||||
}
|
||||
|
||||
func didFailToConnect(_ peripheral: CBPeripheral, _ error: Error?) {
|
||||
let peripheralKey = peripheral.hash
|
||||
guard let completion = connectCompletions.removeValue(forKey: peripheralKey) else {
|
||||
return
|
||||
}
|
||||
completion(.failure(error ?? MyError.unknown))
|
||||
}
|
||||
|
||||
func didDisconnectPeripheral(_ peripheral: CBPeripheral, _ error: Error?) {
|
||||
let peripheralKey = peripheral.hash
|
||||
let myPeripheralKey = Int64(peripheralKey)
|
||||
let discoverGattCompletion = discoverGattCompletions.removeValue(forKey: peripheralKey)
|
||||
if discoverGattCompletion != nil {
|
||||
didDiscoverGATT(peripheral, error ?? MyError.unknown)
|
||||
}
|
||||
let services = cachedServices[peripheralKey] ?? [:]
|
||||
for service in services {
|
||||
let characteristics = cachedCharacteristics[service.key] ?? [:]
|
||||
for characteristic in characteristics {
|
||||
let readCharacteristicCompletion = readCharacteristicCompletions.removeValue(forKey: characteristic.key)
|
||||
let writeCharacteristicCompletion = writeCharacteristicCompletions.removeValue(forKey: characteristic.key)
|
||||
if readCharacteristicCompletion != nil {
|
||||
readCharacteristicCompletion!(.failure(MyError.illegalState))
|
||||
}
|
||||
if writeCharacteristicCompletion != nil {
|
||||
writeCharacteristicCompletion!(.failure(MyError.illegalState))
|
||||
}
|
||||
let descriptors = cachedDescriptors[characteristic.key] ?? [:]
|
||||
for descriptor in descriptors {
|
||||
let readDescriptorCompletion = readDescriptorCompletions.removeValue(forKey: descriptor.key)
|
||||
let writeDescriptorCompletion = writeDescriptorCompletions.removeValue(forKey: descriptor.key)
|
||||
if readDescriptorCompletion != nil {
|
||||
readDescriptorCompletion!(.failure(MyError.illegalState))
|
||||
}
|
||||
if writeDescriptorCompletion != nil {
|
||||
writeDescriptorCompletion!(.failure(MyError.illegalState))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
myApi.onPeripheralStateChanged(myPeripheralKey: myPeripheralKey, state: false) {}
|
||||
guard let completion = disconnectCompletions.removeValue(forKey: peripheralKey) else {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func didDiscoverServices(_ peripheral: CBPeripheral, _ error: Error?) {
|
||||
let peripheralKey = peripheral.hash
|
||||
if error == nil {
|
||||
var services = peripheral.services ?? []
|
||||
if services.isEmpty {
|
||||
didDiscoverGATT(peripheral, error)
|
||||
} else {
|
||||
let service = services.removeFirst()
|
||||
unfinishedServices[peripheralKey] = services
|
||||
peripheral.discoverCharacteristics(nil, for: service)
|
||||
}
|
||||
} else {
|
||||
didDiscoverGATT(peripheral, error)
|
||||
}
|
||||
}
|
||||
|
||||
func didDiscoverCharacteristics(_ peripheral: CBPeripheral, _ service: CBService, _ error: Error?) {
|
||||
let peripheralKey = peripheral.hash
|
||||
if error == nil {
|
||||
var characteristics = service.characteristics ?? []
|
||||
if characteristics.isEmpty {
|
||||
var services = unfinishedServices.removeValue(forKey: peripheralKey) ?? []
|
||||
if services.isEmpty {
|
||||
didDiscoverGATT(peripheral, error)
|
||||
} else {
|
||||
let service = services.removeFirst()
|
||||
unfinishedServices[peripheralKey] = services
|
||||
peripheral.discoverCharacteristics(nil, for: service)
|
||||
}
|
||||
} else {
|
||||
let characteristic = characteristics.removeFirst()
|
||||
unfinishedCharacteristics[peripheralKey] = characteristics
|
||||
peripheral.discoverDescriptors(for: characteristic)
|
||||
}
|
||||
} else {
|
||||
didDiscoverGATT(peripheral, error)
|
||||
}
|
||||
}
|
||||
|
||||
func didDiscoverDescriptors(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic, _ error: Error?) {
|
||||
let peripheralKey = peripheral.hash
|
||||
if error == nil {
|
||||
var characteristics = unfinishedCharacteristics.removeValue(forKey: peripheralKey) ?? []
|
||||
if (characteristics.isEmpty) {
|
||||
var services = unfinishedServices.removeValue(forKey: peripheralKey) ?? []
|
||||
if services.isEmpty {
|
||||
didDiscoverGATT(peripheral, error)
|
||||
} else {
|
||||
let service = services.removeFirst()
|
||||
unfinishedServices[peripheralKey] = services
|
||||
peripheral.discoverCharacteristics(nil, for: service)
|
||||
}
|
||||
} else {
|
||||
let characteristic = characteristics.removeFirst()
|
||||
unfinishedCharacteristics[peripheralKey] = characteristics
|
||||
peripheral.discoverDescriptors(for: characteristic)
|
||||
}
|
||||
} else {
|
||||
didDiscoverGATT(peripheral, error)
|
||||
}
|
||||
}
|
||||
|
||||
private func didDiscoverGATT(_ peripheral: CBPeripheral, _ error: Error?) {
|
||||
let peripheralKey = peripheral.hash
|
||||
unfinishedServices.removeValue(forKey: peripheralKey)
|
||||
unfinishedCharacteristics.removeValue(forKey: peripheralKey)
|
||||
guard let completion = discoverGattCompletions.removeValue(forKey: peripheralKey) else {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
let services = peripheral.services ?? []
|
||||
var cachedServices = [Int: CBService]()
|
||||
for service in services {
|
||||
let serviceKey = service.hash
|
||||
cachedServices[serviceKey] = service
|
||||
let characteristics = service.characteristics ?? []
|
||||
var cachedCharacteristics = [Int: CBCharacteristic]()
|
||||
for characteristic in characteristics {
|
||||
let characteristicKey = characteristic.hash
|
||||
cachedCharacteristics[characteristicKey] = characteristic
|
||||
let descriptors = characteristic.descriptors ?? []
|
||||
var cachedDescriptors = [Int: CBDescriptor]()
|
||||
for descriptor in descriptors {
|
||||
let descriptorKey = descriptor.hash
|
||||
cachedDescriptors[descriptorKey] = descriptor
|
||||
}
|
||||
self.cachedDescriptors[characteristicKey] = cachedDescriptors
|
||||
}
|
||||
self.cachedCharacteristics[serviceKey] = cachedCharacteristics
|
||||
}
|
||||
self.cachedServices[peripheralKey] = cachedServices
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
|
||||
func didUpdateCharacteristicValue(_ characteristic: CBCharacteristic, _ error: Error?) {
|
||||
let characteristicKey = characteristic.hash
|
||||
guard let completion = readCharacteristicCompletions.removeValue(forKey: characteristicKey) else {
|
||||
let myCharacteristicKey = Int64(characteristicKey)
|
||||
let rawValue = characteristic.value ?? Data()
|
||||
let value = FlutterStandardTypedData(bytes: rawValue)
|
||||
myApi.onCharacteristicValueChanged(myCharacteristicKey: myCharacteristicKey, value: value) {}
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
let rawValue = characteristic.value ?? Data()
|
||||
let value = FlutterStandardTypedData(bytes: rawValue)
|
||||
completion(.success(value))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
|
||||
func didWriteCharacteristicValue(_ characteristic: CBCharacteristic, _ error: Error?) {
|
||||
let characteristicKey = characteristic.hash
|
||||
guard let completion = writeCharacteristicCompletions.removeValue(forKey: characteristicKey) else {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
|
||||
func didUpdateNotificationState(_ characteristic: CBCharacteristic, _ error: Error?) {
|
||||
let characteristicKey = characteristic.hash
|
||||
guard let completion = notifyCharacteristicCompletions.removeValue(forKey: characteristicKey) else {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
|
||||
func didUpdateDescriptorValue(_ descriptor: CBDescriptor, _ error: Error?) {
|
||||
let descriptorKey = descriptor.hash
|
||||
guard let completion = readDescriptorCompletions.removeValue(forKey: descriptorKey) else {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
// TODO: Need to confirm wheather the corresponding descriptor type and value is correct.
|
||||
let value: FlutterStandardTypedData
|
||||
let rawValue = descriptor.value
|
||||
do {
|
||||
switch descriptor.uuid.uuidString {
|
||||
case CBUUIDCharacteristicExtendedPropertiesString:
|
||||
fallthrough
|
||||
case CBUUIDClientCharacteristicConfigurationString:
|
||||
fallthrough
|
||||
case CBUUIDServerCharacteristicConfigurationString:
|
||||
guard let rawNumber = rawValue as? NSNumber else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
value = FlutterStandardTypedData(bytes: rawNumber.data)
|
||||
case CBUUIDCharacteristicUserDescriptionString:
|
||||
fallthrough
|
||||
case CBUUIDCharacteristicAggregateFormatString:
|
||||
guard let rawString = rawValue as? String else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
value = FlutterStandardTypedData(bytes: rawString.data)
|
||||
case CBUUIDCharacteristicFormatString:
|
||||
guard let rawData = rawValue as? Data else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
value = FlutterStandardTypedData(bytes: rawData)
|
||||
case CBUUIDL2CAPPSMCharacteristicString:
|
||||
guard let rawU16 = rawValue as? UInt16 else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
value = FlutterStandardTypedData(bytes: rawU16.data)
|
||||
default:
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
} catch {
|
||||
value = FlutterStandardTypedData()
|
||||
}
|
||||
completion(.success((value)))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
|
||||
func didWriteDescriptorValue(_ descriptor: CBDescriptor, _ error: Error?) {
|
||||
let descriptorKey = descriptor.hash
|
||||
guard let completion = writeDescriptorCompletions.removeValue(forKey: descriptorKey) else {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CBManagerState {
|
||||
func toMyArgs() -> MyCentralStateArgs {
|
||||
switch self {
|
||||
case .unauthorized:
|
||||
return .unauthorized
|
||||
case .poweredOff:
|
||||
return .poweredOff
|
||||
case .poweredOn:
|
||||
return .poweredOn
|
||||
default:
|
||||
return .unsupported
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CBPeripheral {
|
||||
func toMyArgs() -> MyPeripheralArgs {
|
||||
let key = Int64(hash)
|
||||
let uuid = identifier.uuidString
|
||||
return MyPeripheralArgs(key: key, uuid: uuid)
|
||||
}
|
||||
}
|
||||
|
||||
extension CBService {
|
||||
func toMyArgs() -> MyGattServiceArgs {
|
||||
let key = Int64(hash)
|
||||
let uuid = uuid.uuidString
|
||||
return MyGattServiceArgs(key: key, uuid: uuid)
|
||||
}
|
||||
}
|
||||
|
||||
extension CBCharacteristic {
|
||||
func toMyArgs() -> MyGattCharacteristicArgs {
|
||||
let key = Int64(hash)
|
||||
let uuid = uuid.uuidString
|
||||
let myPropertyArgses = properties.toMyArgses()
|
||||
let myPropertyNumbers = myPropertyArgses.map { myPropertyArgs in Int64(myPropertyArgs.rawValue) }
|
||||
return MyGattCharacteristicArgs(key: key, uuid: uuid, myPropertyNumbers: myPropertyNumbers)
|
||||
}
|
||||
}
|
||||
|
||||
extension CBDescriptor {
|
||||
func toMyArgs() -> MyGattDescriptorArgs {
|
||||
let key = Int64(hash)
|
||||
let uuid = uuid.uuidString
|
||||
return MyGattDescriptorArgs(key: key, uuid: uuid)
|
||||
}
|
||||
}
|
||||
|
||||
extension CBCharacteristicProperties {
|
||||
func toMyArgses() -> [MyGattCharacteristicPropertyArgs] {
|
||||
var myPropertyArgs = [MyGattCharacteristicPropertyArgs]()
|
||||
if contains(.read) {
|
||||
myPropertyArgs.append(.read)
|
||||
}
|
||||
if contains(.write) {
|
||||
myPropertyArgs.append(.write)
|
||||
}
|
||||
if contains(.writeWithoutResponse) {
|
||||
myPropertyArgs.append(.writeWithoutResponse)
|
||||
}
|
||||
if contains(.notify) {
|
||||
myPropertyArgs.append(.notify)
|
||||
}
|
||||
if contains(.indicate) {
|
||||
myPropertyArgs.append(.indicate)
|
||||
}
|
||||
return myPropertyArgs
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattCharacteristicWriteTypeArgs {
|
||||
func toType() -> CBCharacteristicWriteType {
|
||||
switch self {
|
||||
case .withResponse:
|
||||
return .withResponse
|
||||
case .withoutResponse:
|
||||
return .withoutResponse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NSNumber {
|
||||
var data: Data {
|
||||
var source = self
|
||||
return Data(bytes: &source, count: MemoryLayout<NSNumber>.size)
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
var data: Data {
|
||||
return data(using: String.Encoding.utf8)!
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt16 {
|
||||
var data: Data {
|
||||
var source = self
|
||||
return Data(bytes: &source, count: MemoryLayout<UInt16>.size)
|
||||
}
|
||||
}
|
@ -0,0 +1,650 @@
|
||||
//
|
||||
// MyCentralController.swift
|
||||
// bluetooth_low_energy_ios
|
||||
//
|
||||
// Created by 闫守旺 on 2023/8/13.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreBluetooth
|
||||
|
||||
#if os(iOS)
|
||||
import Flutter
|
||||
#elseif os(macOS)
|
||||
import FlutterMacOS
|
||||
#else
|
||||
#error("Unsupported platform.")
|
||||
#endif
|
||||
|
||||
class MyCentralManager: MyCentralManagerHostApi {
|
||||
init(_ binaryMessenger: FlutterBinaryMessenger) {
|
||||
self.binaryMessenger = binaryMessenger
|
||||
}
|
||||
|
||||
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 tearDown() throws {
|
||||
if(centralManager.isScanning) {
|
||||
centralManager.stopScan()
|
||||
}
|
||||
for peripheral in peripherals.values {
|
||||
if peripheral.state != .disconnected {
|
||||
centralManager.cancelPeripheralConnection(peripheral)
|
||||
}
|
||||
}
|
||||
peripherals.removeAll()
|
||||
services.removeAll()
|
||||
characteristics.removeAll()
|
||||
descriptors.removeAll()
|
||||
peripheralsArgs.removeAll()
|
||||
servicesArgsOfPeripheralsArgs.removeAll()
|
||||
servicesArgs.removeAll()
|
||||
characteristicsArgs.removeAll()
|
||||
descriptorsArgs.removeAll()
|
||||
setUpCompletion = nil
|
||||
connectCompletions.removeAll()
|
||||
disconnectCompletions.removeAll()
|
||||
readRssiCompletions.removeAll()
|
||||
discoverGattCompletions.removeAll()
|
||||
unfinishedServices.removeAll()
|
||||
unfinishedCharacteristics.removeAll()
|
||||
readCharacteristicCompletions.removeAll()
|
||||
writeCharacteristicCompletions.removeAll()
|
||||
notifyCharacteristicCompletions.removeAll()
|
||||
readDescriptorCompletions.removeAll()
|
||||
writeDescriptorCompletions.removeAll()
|
||||
}
|
||||
|
||||
func startDiscovery() throws {
|
||||
let options = [CBCentralManagerScanOptionAllowDuplicatesKey: true]
|
||||
centralManager.scanForPeripherals(withServices: nil, options: options)
|
||||
}
|
||||
|
||||
func stopDiscovery() throws {
|
||||
centralManager.stopScan()
|
||||
}
|
||||
|
||||
func connect(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
do {
|
||||
let unfinishedCompletion = connectCompletions[peripheralHashCodeArgs]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
centralManager.connect(peripheral)
|
||||
connectCompletions[peripheralHashCodeArgs] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func disconnect(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
do {
|
||||
let unfinishedCompletion = disconnectCompletions[peripheralHashCodeArgs]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
centralManager.cancelPeripheralConnection(peripheral)
|
||||
disconnectCompletions[peripheralHashCodeArgs] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func getMaximumWriteLength(peripheralHashCodeArgs: Int64, typeNumberArgs: Int64) throws -> Int64 {
|
||||
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let typeRawValue = Int(typeNumberArgs)
|
||||
guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeRawValue) else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let type = typeArgs.toWriteType()
|
||||
let maximumWriteLength = peripheral.maximumWriteValueLength(for: type)
|
||||
let maximumWriteLengthArgs = Int64(maximumWriteLength)
|
||||
return maximumWriteLengthArgs
|
||||
}
|
||||
|
||||
func readRSSI(peripheralHashCodeArgs: Int64, completion: @escaping (Result<Int64, Error>) -> Void) {
|
||||
do {
|
||||
let unfinishedCompletion = readRssiCompletions[peripheralHashCodeArgs]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
peripheral.readRSSI()
|
||||
readRssiCompletions[peripheralHashCodeArgs] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func discoverGATT(peripheralHashCodeArgs: Int64, completion: @escaping (Result<[MyGattServiceArgs], Error>) -> Void) {
|
||||
do {
|
||||
let unfinishedCompletion = discoverGattCompletions[peripheralHashCodeArgs]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
peripheral.discoverServices(nil)
|
||||
discoverGattCompletions[peripheralHashCodeArgs] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func readCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
|
||||
do {
|
||||
let unfinishedCompletion = readCharacteristicCompletions[characteristicHashCodeArgs]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
guard let characteristic = characteristics[characteristicHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
peripheral.readValue(for: characteristic)
|
||||
readCharacteristicCompletions[characteristicHashCodeArgs] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func writeCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: 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 {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
guard let characteristic = characteristics[characteristicHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let data = valueArgs.data
|
||||
let typeRawValue = Int(typeNumberArgs)
|
||||
guard let typeArgs = MyGattCharacteristicWriteTypeArgs(rawValue: typeRawValue) else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let type = typeArgs.toWriteType()
|
||||
peripheral.writeValue(data, for: characteristic, type: type)
|
||||
writeCharacteristicCompletions[characteristicHashCodeArgs] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func notifyCharacteristic(peripheralHashCodeArgs: Int64, characteristicHashCodeArgs: 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 {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
guard let characteristic = characteristics[characteristicHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let enabled = stateArgs
|
||||
peripheral.setNotifyValue(enabled, for: characteristic)
|
||||
notifyCharacteristicCompletions[characteristicHashCodeArgs] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func readDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: Int64, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
|
||||
do {
|
||||
let unfinishedCompletion = readDescriptorCompletions[descriptorHashCodeArgs]
|
||||
if unfinishedCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
guard let peripheral = peripherals[peripheralHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
guard let descriptor = descriptors[descriptorHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
peripheral.readValue(for: descriptor)
|
||||
readDescriptorCompletions[descriptorHashCodeArgs] = completion
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func writeDescriptor(peripheralHashCodeArgs: Int64, descriptorHashCodeArgs: 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 {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
guard let descriptor = descriptors[descriptorHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let data = valueArgs.data
|
||||
peripheral.writeValue(data, for: descriptor)
|
||||
writeDescriptorCompletions[descriptorHashCodeArgs] = 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) {}
|
||||
}
|
||||
|
||||
|
||||
func didDiscover(_ 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 rssiArgs = rssi.int64Value
|
||||
let advertiseDataArgs = advertisementData.toAdvertiseDataArgs()
|
||||
api.onDiscovered(peripheralArgs: peripheralArgs, rssiArgs: rssiArgs, advertiseDataArgs: advertiseDataArgs) {}
|
||||
}
|
||||
|
||||
func didConnect(_ peripheral: CBPeripheral) {
|
||||
let peripheralHashCode = peripheral.hash
|
||||
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
|
||||
return
|
||||
}
|
||||
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
|
||||
let completion = connectCompletions.removeValue(forKey: peripheralHashCodeArgs)
|
||||
completion?(.success(()))
|
||||
let stateArgs = true
|
||||
api.onPeripheralStateChanged(peripheralArgs: peripheralArgs, stateArgs: stateArgs) {}
|
||||
}
|
||||
|
||||
func didFailToConnect(_ peripheral: CBPeripheral, _ error: Error?) {
|
||||
let peripheralHashCode = peripheral.hash
|
||||
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
|
||||
return
|
||||
}
|
||||
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
|
||||
let completion = connectCompletions.removeValue(forKey: peripheralHashCodeArgs)
|
||||
completion?(.failure(error ?? MyError.unknown))
|
||||
}
|
||||
|
||||
func didDisconnectPeripheral(_ peripheral: CBPeripheral, _ error: Error?) {
|
||||
let peripheralHashCode = peripheral.hash
|
||||
guard let peripheralArgs = peripheralsArgs[peripheralHashCode] else {
|
||||
return
|
||||
}
|
||||
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[peripheralHashCodeArgs] ?? []
|
||||
for serviceArgs in servicesArgs {
|
||||
let characteristicsArgs = serviceArgs.characteristicsArgs.map { args in args! }
|
||||
for characteristicArgs in characteristicsArgs {
|
||||
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
|
||||
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 readDescriptorCompletion = readDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs)
|
||||
let writeDescriptorCompletion = writeDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs)
|
||||
readDescriptorCompletion?(.failure(error ?? MyError.unknown))
|
||||
writeDescriptorCompletion?(.failure(error ?? MyError.unknown))
|
||||
}
|
||||
}
|
||||
}
|
||||
let stateArgs = false
|
||||
api.onPeripheralStateChanged(peripheralArgs: peripheralArgs, stateArgs: stateArgs) {}
|
||||
guard let completion = disconnectCompletions.removeValue(forKey: peripheralHashCodeArgs) else {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
let rssiArgs = rssi.int64Value
|
||||
completion(.success((rssiArgs)))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
servicesArgsOfPeripheralsArgs[peripheralhashCodeArgs] = servicesArgs
|
||||
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 {
|
||||
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) {}
|
||||
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 {
|
||||
return
|
||||
}
|
||||
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
|
||||
guard let completion = writeCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) else {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
|
||||
func didUpdateNotificationState(_ characteristic: CBCharacteristic, _ error: Error?) {
|
||||
let characteristicHashCode = characteristic.hash
|
||||
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
|
||||
return
|
||||
}
|
||||
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
|
||||
guard let completion = notifyCharacteristicCompletions.removeValue(forKey: characteristicHashCodeArgs) else {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
|
||||
func didUpdateDescriptorValue(_ descriptor: CBDescriptor, _ error: Error?) {
|
||||
let descriptorHashCode = descriptor.hash
|
||||
guard let descriptorArgs = descriptorsArgs[descriptorHashCode] else {
|
||||
return
|
||||
}
|
||||
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
|
||||
guard let completion = readDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs) else {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
// TODO: Need to confirm wheather the corresponding descriptor type and value is correct.
|
||||
let valueArgs: FlutterStandardTypedData
|
||||
let value = descriptor.value
|
||||
do {
|
||||
switch descriptor.uuid.uuidString {
|
||||
case CBUUIDCharacteristicExtendedPropertiesString:
|
||||
fallthrough
|
||||
case CBUUIDClientCharacteristicConfigurationString:
|
||||
fallthrough
|
||||
case CBUUIDServerCharacteristicConfigurationString:
|
||||
guard let numberValue = value as? NSNumber else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
valueArgs = FlutterStandardTypedData(bytes: numberValue.data)
|
||||
case CBUUIDCharacteristicUserDescriptionString:
|
||||
fallthrough
|
||||
case CBUUIDCharacteristicAggregateFormatString:
|
||||
guard let stringValue = value as? String else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
valueArgs = FlutterStandardTypedData(bytes: stringValue.data)
|
||||
case CBUUIDCharacteristicFormatString:
|
||||
guard let bytes = value as? Data else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
valueArgs = FlutterStandardTypedData(bytes: bytes)
|
||||
case CBUUIDL2CAPPSMCharacteristicString:
|
||||
guard let uint16Value = value as? UInt16 else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
valueArgs = FlutterStandardTypedData(bytes: uint16Value.data)
|
||||
default:
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
} catch {
|
||||
valueArgs = FlutterStandardTypedData()
|
||||
}
|
||||
completion(.success((valueArgs)))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
|
||||
func didWriteDescriptorValue(_ descriptor: CBDescriptor, _ error: Error?) {
|
||||
let descriptorHashCode = descriptor.hash
|
||||
guard let descriptorArgs = descriptorsArgs[descriptorHashCode] else {
|
||||
return
|
||||
}
|
||||
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
|
||||
guard let completion = writeDescriptorCompletions.removeValue(forKey: descriptorHashCodeArgs) else {
|
||||
return
|
||||
}
|
||||
if error == nil {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(error!))
|
||||
}
|
||||
}
|
||||
}
|
@ -9,30 +9,29 @@ import Foundation
|
||||
import CoreBluetooth
|
||||
|
||||
class MyCentralManagerDelegate: NSObject, CBCentralManagerDelegate {
|
||||
private let myCentralController: MyCentralController
|
||||
|
||||
init(_ myCentralController: MyCentralController) {
|
||||
self.myCentralController = myCentralController
|
||||
init(_ centralManager: MyCentralManager) {
|
||||
self.centralManager = centralManager
|
||||
}
|
||||
|
||||
private let centralManager: MyCentralManager
|
||||
|
||||
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
||||
let state = central.state
|
||||
myCentralController.didUpdateState(state)
|
||||
centralManager.didUpdateState()
|
||||
}
|
||||
|
||||
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
|
||||
myCentralController.didDiscover(peripheral, advertisementData, RSSI)
|
||||
centralManager.didDiscover(peripheral, advertisementData, RSSI)
|
||||
}
|
||||
|
||||
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
|
||||
myCentralController.didConnect(peripheral)
|
||||
centralManager.didConnect(peripheral)
|
||||
}
|
||||
|
||||
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
|
||||
myCentralController.didFailToConnect(peripheral, error)
|
||||
centralManager.didFailToConnect(peripheral, error)
|
||||
}
|
||||
|
||||
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
|
||||
myCentralController.didDisconnectPeripheral(peripheral, error)
|
||||
centralManager.didDisconnectPeripheral(peripheral, error)
|
||||
}
|
||||
}
|
||||
|
@ -9,41 +9,45 @@ import Foundation
|
||||
import CoreBluetooth
|
||||
|
||||
class MyPeripheralDelegate: NSObject, CBPeripheralDelegate {
|
||||
private let myCentralController: MyCentralController
|
||||
private let centralManager: MyCentralManager
|
||||
|
||||
init(_ myCentralController: MyCentralController) {
|
||||
self.myCentralController = myCentralController
|
||||
init(_ centralManager: MyCentralManager) {
|
||||
self.centralManager = centralManager
|
||||
}
|
||||
|
||||
func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) {
|
||||
centralManager.didReadRSSI(peripheral, RSSI, error)
|
||||
}
|
||||
|
||||
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
|
||||
myCentralController.didDiscoverServices(peripheral, error)
|
||||
centralManager.didDiscoverServices(peripheral, error)
|
||||
}
|
||||
|
||||
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
|
||||
myCentralController.didDiscoverCharacteristics(peripheral, service, error)
|
||||
centralManager.didDiscoverCharacteristics(peripheral, service, error)
|
||||
}
|
||||
|
||||
func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) {
|
||||
myCentralController.didDiscoverDescriptors(peripheral, characteristic, error)
|
||||
centralManager.didDiscoverDescriptors(peripheral, characteristic, error)
|
||||
}
|
||||
|
||||
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
|
||||
myCentralController.didUpdateCharacteristicValue(characteristic, error)
|
||||
centralManager.didUpdateCharacteristicValue(characteristic, error)
|
||||
}
|
||||
|
||||
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
|
||||
myCentralController.didWriteCharacteristicValue(characteristic, error)
|
||||
centralManager.didWriteCharacteristicValue(characteristic, error)
|
||||
}
|
||||
|
||||
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
|
||||
myCentralController.didUpdateNotificationState(characteristic, error)
|
||||
centralManager.didUpdateNotificationState(characteristic, error)
|
||||
}
|
||||
|
||||
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?) {
|
||||
myCentralController.didUpdateDescriptorValue(descriptor, error)
|
||||
centralManager.didUpdateDescriptorValue(descriptor, error)
|
||||
}
|
||||
|
||||
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
|
||||
myCentralController.didWriteDescriptorValue(descriptor, error)
|
||||
centralManager.didWriteDescriptorValue(descriptor, error)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,375 @@
|
||||
//
|
||||
// MyPeripheralManager.swift
|
||||
// bluetooth_low_energy_darwin
|
||||
//
|
||||
// Created by 闫守旺 on 2023/10/7.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreBluetooth
|
||||
|
||||
#if os(iOS)
|
||||
import Flutter
|
||||
#elseif os(macOS)
|
||||
import FlutterMacOS
|
||||
#else
|
||||
#error("Unsupported platform.")
|
||||
#endif
|
||||
|
||||
class MyPeripheralManager: MyPeripheralManagerHostApi {
|
||||
init(_ binaryMessenger: FlutterBinaryMessenger) {
|
||||
self.binaryMessenger = binaryMessenger
|
||||
}
|
||||
|
||||
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 tearDown() throws {
|
||||
if(peripheralManager.isAdvertising) {
|
||||
peripheralManager.stopAdvertising()
|
||||
}
|
||||
centrals.removeAll()
|
||||
services.removeAll()
|
||||
characteristics.removeAll()
|
||||
descriptors.removeAll()
|
||||
requests.removeAll()
|
||||
centralsArgs.removeAll()
|
||||
servicesArgs.removeAll()
|
||||
characteristicsArgs.removeAll()
|
||||
descriptorsArgs.removeAll()
|
||||
setUpCompletion = nil
|
||||
addServiceCompletion = nil
|
||||
startAdvertisingCompletion = nil
|
||||
notifyCharacteristicValueChangedCallbacks.removeAll()
|
||||
}
|
||||
|
||||
func addService(serviceArgs: MyGattServiceArgs, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
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 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
|
||||
}
|
||||
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 clearServices() throws {
|
||||
peripheralManager.removeAllServices()
|
||||
let servicesArgs = self.servicesArgs.values
|
||||
for serviceArgs in servicesArgs {
|
||||
freeService(serviceArgs)
|
||||
}
|
||||
}
|
||||
|
||||
private func freeService(_ serviceArgs: MyGattServiceArgs) {
|
||||
let characteristicsArgs = serviceArgs.characteristicsArgs
|
||||
for args in characteristicsArgs {
|
||||
guard let characteristicArgs = args else {
|
||||
continue
|
||||
}
|
||||
let descriptorsArgs = characteristicArgs.descriptorsArgs
|
||||
for args in descriptorsArgs {
|
||||
guard let descriptorArgs = args else {
|
||||
continue
|
||||
}
|
||||
let descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
|
||||
guard let descriptor = self.descriptors.removeValue(forKey: descriptorHashCodeArgs) else {
|
||||
continue
|
||||
}
|
||||
let descriptorHashCode = descriptor.hash
|
||||
self.descriptorsArgs.removeValue(forKey: descriptorHashCode)
|
||||
}
|
||||
let characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
|
||||
guard let characteristic = self.characteristics.removeValue(forKey: characteristicHashCodeArgs) else {
|
||||
continue
|
||||
}
|
||||
let characteristicHashCode = characteristic.hash
|
||||
self.characteristicsArgs.removeValue(forKey: characteristicHashCode)
|
||||
}
|
||||
let serviceHashCodeArgs = serviceArgs.hashCodeArgs
|
||||
guard let service = self.services.removeValue(forKey: serviceHashCodeArgs) else {
|
||||
return
|
||||
}
|
||||
let serviceHashCode = service.hash
|
||||
self.servicesArgs.removeValue(forKey: serviceHashCode)
|
||||
}
|
||||
|
||||
func startAdvertising(advertiseDataArgs: MyAdvertiseDataArgs, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
do {
|
||||
if startAdvertisingCompletion != nil {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
let advertisementData = try advertiseDataArgs.toAdvertiseData()
|
||||
peripheralManager.startAdvertising(advertisementData)
|
||||
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 = central.maximumUpdateValueLength
|
||||
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[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[idArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let result = statusArgs ? CBATTError.Code.success : CBATTError.Code.requestNotSupported
|
||||
peripheralManager.respond(to: request, withResult: result)
|
||||
}
|
||||
|
||||
func notifyCharacteristicValueChanged(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, valueArgs: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
do {
|
||||
if notifyCharacteristicValueChangedCallbacks.count > 0 {
|
||||
throw MyError.illegalState
|
||||
}
|
||||
let value = valueArgs.data
|
||||
guard let characteristic = characteristics[characteristicHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
guard let central = centrals[centralHashCodeArgs] else {
|
||||
throw MyError.illegalArgument
|
||||
}
|
||||
let centrals = [central]
|
||||
let updated = peripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals)
|
||||
if updated {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
notifyCharacteristicValueChangedCallbacks.append {
|
||||
let updated = self.peripheralManager.updateValue(value, for: characteristic, onSubscribedCentrals: centrals)
|
||||
if updated {
|
||||
completion(.success(()))
|
||||
} else {
|
||||
completion(.failure(MyError.unknown))
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
func didUpdateState() {
|
||||
let state = peripheralManager.state
|
||||
let stateArgs = state.toArgs()
|
||||
let stateNumberArgs = Int64(stateArgs.rawValue)
|
||||
if state != .unknown && setUpCompletion != nil {
|
||||
let args = MyPeripheralManagerArgs(stateNumberArgs: stateNumberArgs)
|
||||
setUpCompletion!(.success(args))
|
||||
setUpCompletion = nil
|
||||
}
|
||||
api.onStateChanged(stateNumberArgs: stateNumberArgs) {}
|
||||
}
|
||||
|
||||
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) {}
|
||||
}
|
||||
|
||||
func didReceiveWrite(_ requests: [CBATTRequest]) {
|
||||
for request in requests {
|
||||
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) {}
|
||||
}
|
||||
}
|
||||
|
||||
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) {}
|
||||
}
|
||||
|
||||
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) {}
|
||||
}
|
||||
|
||||
func isReadyToUpdateSubscribers() {
|
||||
let callbacks = notifyCharacteristicValueChangedCallbacks
|
||||
notifyCharacteristicValueChangedCallbacks.removeAll()
|
||||
for callback in callbacks {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
//
|
||||
// MyPeripheralManagerDelegate.swift
|
||||
// bluetooth_low_energy_darwin
|
||||
//
|
||||
// Created by 闫守旺 on 2023/10/7.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreBluetooth
|
||||
|
||||
class MyPeripheralManagerDelegate: NSObject, CBPeripheralManagerDelegate {
|
||||
init(_ peripheralManager: MyPeripheralManager) {
|
||||
self.peripheralManager = peripheralManager
|
||||
}
|
||||
|
||||
private let peripheralManager: MyPeripheralManager
|
||||
|
||||
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
|
||||
peripheralManager.didUpdateState()
|
||||
}
|
||||
|
||||
func peripheralManager(_ peripheral: CBPeripheralManager, didAdd service: CBService, error: Error?) {
|
||||
peripheralManager.didAdd(service, error)
|
||||
}
|
||||
|
||||
func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) {
|
||||
peripheralManager.didStartAdvertising(error)
|
||||
}
|
||||
|
||||
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
|
||||
peripheralManager.didReceiveRead(request)
|
||||
}
|
||||
|
||||
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
|
||||
peripheralManager.didReceiveWrite(requests)
|
||||
}
|
||||
|
||||
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
|
||||
peripheralManager.didSubscribeTo(central, characteristic)
|
||||
}
|
||||
|
||||
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) {
|
||||
peripheralManager.didUnsubscribeFrom(central, characteristic)
|
||||
}
|
||||
|
||||
func peripheralManagerIsReady(toUpdateSubscribers peripheral: CBPeripheralManager) {
|
||||
peripheralManager.isReadyToUpdateSubscribers()
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'src/my_central_controller.dart';
|
||||
import 'src/my_bluetooth_low_energy.dart';
|
||||
|
||||
abstract class BluetoothLowEnergyDarwin {
|
||||
static void registerWith() {
|
||||
CentralController.instance = MyCentralController();
|
||||
BluetoothLowEnergy.instance = MyBluetoothLowEnergy();
|
||||
}
|
||||
}
|
||||
|
224
bluetooth_low_energy_darwin/lib/src/my_api.dart
Normal file
224
bluetooth_low_energy_darwin/lib/src/my_api.dart
Normal file
@ -0,0 +1,224 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_gatt_characteristic2.dart';
|
||||
import 'my_gatt_descriptor2.dart';
|
||||
import 'my_gatt_service2.dart';
|
||||
|
||||
export 'my_api.g.dart';
|
||||
|
||||
extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs {
|
||||
BluetoothLowEnergyState toState() {
|
||||
return BluetoothLowEnergyState.values[index];
|
||||
}
|
||||
}
|
||||
|
||||
extension MyAdvertiseDataArgsX on MyAdvertiseDataArgs {
|
||||
AdvertiseData toAdvertiseData() {
|
||||
final name = nameArgs;
|
||||
final serviceUUIDs = serviceUUIDsArgs
|
||||
.cast<String>()
|
||||
.map((args) => UUID.fromString(args))
|
||||
.toList();
|
||||
final serviceData = serviceDataArgs.cast<String, Uint8List>().map(
|
||||
(uuidArgs, dataArgs) {
|
||||
final uuid = UUID.fromString(uuidArgs);
|
||||
final data = dataArgs;
|
||||
return MapEntry(uuid, data);
|
||||
},
|
||||
);
|
||||
final manufacturerSpecificData =
|
||||
manufacturerSpecificDataArgs?.toManufacturerSpecificData();
|
||||
return AdvertiseData(
|
||||
name: name,
|
||||
serviceUUIDs: serviceUUIDs,
|
||||
serviceData: serviceData,
|
||||
manufacturerSpecificData: manufacturerSpecificData,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs {
|
||||
ManufacturerSpecificData toManufacturerSpecificData() {
|
||||
final id = idArgs;
|
||||
final data = dataArgs;
|
||||
return ManufacturerSpecificData(
|
||||
id: id,
|
||||
data: data,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattCharacteristicPropertyArgsX
|
||||
on MyGattCharacteristicPropertyArgs {
|
||||
GattCharacteristicProperty toProperty() {
|
||||
return GattCharacteristicProperty.values[index];
|
||||
}
|
||||
}
|
||||
|
||||
extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType {
|
||||
MyGattCharacteristicWriteTypeArgs toArgs() {
|
||||
return MyGattCharacteristicWriteTypeArgs.values[index];
|
||||
}
|
||||
}
|
||||
|
||||
extension MyPeripheralArgsX on MyPeripheralArgs {
|
||||
Peripheral toPeripheral() {
|
||||
final hashCode = hashCodeArgs;
|
||||
final uuid = UUID.fromString(uuidArgs);
|
||||
return MyPeripheral(
|
||||
hashCode: hashCode,
|
||||
uuid: uuid,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattServiceArgsX on MyGattServiceArgs {
|
||||
MyGattService2 toService2() {
|
||||
final hashCode = hashCodeArgs;
|
||||
final uuid = UUID.fromString(uuidArgs);
|
||||
final characteristics = characteristicsArgs
|
||||
.cast<MyGattCharacteristicArgs>()
|
||||
.map((args) => args.toCharacteristic2())
|
||||
.toList();
|
||||
return MyGattService2(
|
||||
hashCode: hashCode,
|
||||
uuid: uuid,
|
||||
characteristics: characteristics,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs {
|
||||
MyGattCharacteristic2 toCharacteristic2() {
|
||||
final hashCode = hashCodeArgs;
|
||||
final uuid = UUID.fromString(uuidArgs);
|
||||
final properties = propertyNumbersArgs.cast<int>().map(
|
||||
(args) {
|
||||
final propertyArgs = MyGattCharacteristicPropertyArgs.values[args];
|
||||
return propertyArgs.toProperty();
|
||||
},
|
||||
).toList();
|
||||
final descriptors = descriptorsArgs
|
||||
.cast<MyGattDescriptorArgs>()
|
||||
.map((args) => args.toDescriptor2())
|
||||
.toList();
|
||||
return MyGattCharacteristic2(
|
||||
hashCode: hashCode,
|
||||
uuid: uuid,
|
||||
properties: properties,
|
||||
descriptors: descriptors,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattDescriptorArgsX on MyGattDescriptorArgs {
|
||||
MyGattDescriptor2 toDescriptor2() {
|
||||
final hashCode = hashCodeArgs;
|
||||
final uuid = UUID.fromString(uuidArgs);
|
||||
return MyGattDescriptor2(
|
||||
hashCode: hashCode,
|
||||
uuid: uuid,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyCentralArgsX on MyCentralArgs {
|
||||
MyCentral toCentral() {
|
||||
final hashCode = hashCodeArgs;
|
||||
final uuid = UUID.fromString(uuidArgs);
|
||||
return MyCentral(
|
||||
hashCode: hashCode,
|
||||
uuid: uuid,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension AdvertiseDataX on AdvertiseData {
|
||||
MyAdvertiseDataArgs 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 MyAdvertiseDataArgs(
|
||||
nameArgs: nameArgs,
|
||||
serviceUUIDsArgs: serviceUUIDsArgs,
|
||||
serviceDataArgs: serviceDataArgs,
|
||||
manufacturerSpecificDataArgs: manufacturerSpecificDataArgs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension ManufacturerSpecificDataX on ManufacturerSpecificData {
|
||||
MyManufacturerSpecificDataArgs toArgs() {
|
||||
final idArgs = id;
|
||||
final dataArgs = data;
|
||||
return MyManufacturerSpecificDataArgs(
|
||||
idArgs: idArgs,
|
||||
dataArgs: dataArgs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattServiceX on MyGattService {
|
||||
MyGattServiceArgs toArgs() {
|
||||
final hashCodeArgs = hashCode;
|
||||
final uuidArgs = uuid.toString();
|
||||
final characteristicsArgs = characteristics
|
||||
.cast<MyGattCharacteristic>()
|
||||
.map((characteristic) => characteristic.toArgs())
|
||||
.toList();
|
||||
return MyGattServiceArgs(
|
||||
hashCodeArgs: hashCodeArgs,
|
||||
uuidArgs: uuidArgs,
|
||||
characteristicsArgs: characteristicsArgs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattCharacteristicX on MyGattCharacteristic {
|
||||
MyGattCharacteristicArgs toArgs() {
|
||||
final hashCodeArgs = hashCode;
|
||||
final uuidArgs = uuid.toString();
|
||||
final propertyNumbersArgs = properties.map((property) {
|
||||
final propertyArgs = property.toArgs();
|
||||
return propertyArgs.index;
|
||||
}).toList();
|
||||
final descriptorsArgs = descriptors
|
||||
.cast<MyGattDescriptor>()
|
||||
.map((descriptor) => descriptor.toArgs())
|
||||
.toList();
|
||||
return MyGattCharacteristicArgs(
|
||||
hashCodeArgs: hashCodeArgs,
|
||||
uuidArgs: uuidArgs,
|
||||
propertyNumbersArgs: propertyNumbersArgs,
|
||||
descriptorsArgs: descriptorsArgs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattDescriptorX on MyGattDescriptor {
|
||||
MyGattDescriptorArgs toArgs() {
|
||||
final hashCodeArgs = hashCode;
|
||||
final uuidArgs = uuid.toString();
|
||||
final valueArgs = value;
|
||||
return MyGattDescriptorArgs(
|
||||
hashCodeArgs: hashCodeArgs,
|
||||
uuidArgs: uuidArgs,
|
||||
valueArgs: valueArgs,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension GattCharacteristicPropertyX on GattCharacteristicProperty {
|
||||
MyGattCharacteristicPropertyArgs toArgs() {
|
||||
return MyGattCharacteristicPropertyArgs.values[index];
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,15 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_central_manager.dart';
|
||||
import 'my_peripheral_manager.dart';
|
||||
|
||||
class MyBluetoothLowEnergy extends BluetoothLowEnergy {
|
||||
@override
|
||||
final MyCentralManager centralManager;
|
||||
@override
|
||||
final MyPeripheralManager peripheralManager;
|
||||
|
||||
MyBluetoothLowEnergy()
|
||||
: centralManager = MyCentralManager(),
|
||||
peripheralManager = MyPeripheralManager();
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
abstract class MyBluetoothLowEnergyManager extends BluetoothLowEnergyManager {
|
||||
MyBluetoothLowEnergyManager()
|
||||
: _state = BluetoothLowEnergyState.unknown,
|
||||
_stateChangedController = StreamController.broadcast();
|
||||
|
||||
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
|
||||
_stateChangedController;
|
||||
|
||||
BluetoothLowEnergyState _state;
|
||||
@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;
|
||||
|
||||
@protected
|
||||
Future<void> throwWithoutState(BluetoothLowEnergyState state) async {
|
||||
if (this.state != state) {
|
||||
throw BluetoothLowEnergyError(
|
||||
'$state is expected, but current state is ${this.state}.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,370 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_gatt_characteristic.dart';
|
||||
import 'my_gatt_descriptor.dart';
|
||||
import 'my_gatt_service.dart';
|
||||
import 'my_peripheral.dart';
|
||||
|
||||
class MyCentralController extends CentralController
|
||||
implements MyCentralControllerFlutterApi {
|
||||
MyCentralController()
|
||||
: _myApi = MyCentralControllerHostApi(),
|
||||
_stateChangedController = StreamController.broadcast(),
|
||||
_discoveredController = StreamController.broadcast(),
|
||||
_peripheralStateChangedController = StreamController.broadcast(),
|
||||
_characteristicValueChangedController = StreamController.broadcast(),
|
||||
_myPeripherals = {},
|
||||
_myServices = {},
|
||||
_myCharacteristics = {},
|
||||
_myDescriptors = {},
|
||||
_state = CentralState.unknown;
|
||||
|
||||
final MyCentralControllerHostApi _myApi;
|
||||
final StreamController<CentralStateChangedEventArgs> _stateChangedController;
|
||||
final StreamController<CentralDiscoveredEventArgs> _discoveredController;
|
||||
final StreamController<PeripheralStateChangedEventArgs>
|
||||
_peripheralStateChangedController;
|
||||
final StreamController<GattCharacteristicValueChangedEventArgs>
|
||||
_characteristicValueChangedController;
|
||||
final Map<int, MyPeripheral> _myPeripherals;
|
||||
final Map<int, MyGattService> _myServices;
|
||||
final Map<int, MyGattCharacteristic> _myCharacteristics;
|
||||
final Map<int, MyGattDescriptor> _myDescriptors;
|
||||
|
||||
CentralState _state;
|
||||
@override
|
||||
CentralState get state => _state;
|
||||
|
||||
@override
|
||||
Stream<CentralStateChangedEventArgs> get stateChanged =>
|
||||
_stateChangedController.stream;
|
||||
@override
|
||||
Stream<CentralDiscoveredEventArgs> get discovered =>
|
||||
_discoveredController.stream;
|
||||
@override
|
||||
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
|
||||
_peripheralStateChangedController.stream;
|
||||
@override
|
||||
Stream<GattCharacteristicValueChangedEventArgs>
|
||||
get characteristicValueChanged =>
|
||||
_characteristicValueChangedController.stream;
|
||||
|
||||
Future<void> _throwWithState(CentralState state) async {
|
||||
if (this.state == state) {
|
||||
throw BluetoothLowEnergyError('$state is unexpected.');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _throwWithoutState(CentralState state) async {
|
||||
if (this.state != state) {
|
||||
throw BluetoothLowEnergyError(
|
||||
'$state is expected, but current state is ${this.state}.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setUp() async {
|
||||
await _throwWithoutState(CentralState.unknown);
|
||||
final args = await _myApi.setUp();
|
||||
final myStateArgs = MyCentralStateArgs.values[args.myStateNumber];
|
||||
_state = myStateArgs.toState();
|
||||
MyCentralControllerFlutterApi.setup(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> tearDown() async {
|
||||
await _throwWithState(CentralState.unknown);
|
||||
await _myApi.tearDown();
|
||||
MyCentralControllerFlutterApi.setup(null);
|
||||
_myPeripherals.clear();
|
||||
_myServices.clear();
|
||||
_myCharacteristics.clear();
|
||||
_myDescriptors.clear();
|
||||
_state = CentralState.unknown;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startDiscovery() async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
await _myApi.startDiscovery();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopDiscovery() async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
await _myApi.stopDiscovery();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> connect(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
await _myApi.connect(myPeripheral.hashCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> disconnect(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
await _myApi.disconnect(myPeripheral.hashCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getMaximumWriteLength(
|
||||
Peripheral peripheral, {
|
||||
required GattCharacteristicWriteType type,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
final myTypeArgs = type.toMyArgs();
|
||||
final myTypeNumber = myTypeArgs.index;
|
||||
final maximumWriteLength = await _myApi.getMaximumWriteLength(
|
||||
myPeripheral.hashCode,
|
||||
myTypeNumber,
|
||||
);
|
||||
return maximumWriteLength;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> discoverGATT(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
await _myApi.discoverGATT(myPeripheral.hashCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattService>> getServices(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
final myServiceArgses = await _myApi.getServices(myPeripheral.hashCode);
|
||||
return myServiceArgses.cast<MyGattServiceArgs>().map(
|
||||
(myServiceArgs) {
|
||||
final myService = MyGattService.fromMyArgs(
|
||||
myPeripheral,
|
||||
myServiceArgs,
|
||||
);
|
||||
_myServices[myService.hashCode] = myService;
|
||||
return myService;
|
||||
},
|
||||
).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattCharacteristic>> getCharacteristics(
|
||||
GattService service,
|
||||
) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myService = service as MyGattService;
|
||||
final myCharactersiticArgses = await _myApi.getCharacteristics(
|
||||
myService.hashCode,
|
||||
);
|
||||
return myCharactersiticArgses.cast<MyGattCharacteristicArgs>().map(
|
||||
(myCharacteristicArgs) {
|
||||
final myCharacteristic = MyGattCharacteristic.fromMyArgs(
|
||||
myService,
|
||||
myCharacteristicArgs,
|
||||
);
|
||||
_myCharacteristics[myCharacteristic.hashCode] = myCharacteristic;
|
||||
return myCharacteristic;
|
||||
},
|
||||
).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattDescriptor>> getDescriptors(
|
||||
GattCharacteristic characteristic,
|
||||
) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myDescriptorArgses = await _myApi.getDescriptors(
|
||||
myCharacteristic.hashCode,
|
||||
);
|
||||
return myDescriptorArgses.cast<MyGattDescriptorArgs>().map(
|
||||
(myDescriptorArgs) {
|
||||
final myDescriptor = MyGattDescriptor.fromMyArgs(
|
||||
myCharacteristic,
|
||||
myDescriptorArgs,
|
||||
);
|
||||
_myDescriptors[myDescriptor.hashCode] = myDescriptor;
|
||||
return myDescriptor;
|
||||
},
|
||||
).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> readCharacteristic(
|
||||
GattCharacteristic characteristic,
|
||||
) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
final value = await _myApi.readCharacteristic(
|
||||
myPeripheral.hashCode,
|
||||
myService.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> writeCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
required Uint8List value,
|
||||
required GattCharacteristicWriteType type,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
final myTypeArgs = type.toMyArgs();
|
||||
final myTypeNumber = myTypeArgs.index;
|
||||
await _myApi.writeCharacteristic(
|
||||
myPeripheral.hashCode,
|
||||
myService.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
value,
|
||||
myTypeNumber,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> notifyCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
required bool state,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
await _myApi.notifyCharacteristic(
|
||||
myPeripheral.hashCode,
|
||||
myService.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
state,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myDescriptor = descriptor as MyGattDescriptor;
|
||||
final myCharacteristic = myDescriptor.myCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
final value = await _myApi.readDescriptor(
|
||||
myPeripheral.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
myDescriptor.hashCode,
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> writeDescriptor(
|
||||
GattDescriptor descriptor, {
|
||||
required Uint8List value,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myDescriptor = descriptor as MyGattDescriptor;
|
||||
final myCharacteristic = myDescriptor.myCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
await _myApi.writeDescriptor(
|
||||
myPeripheral.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
myDescriptor.hashCode,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onStateChanged(int myStateNumber) {
|
||||
final myStateArgs = MyCentralStateArgs.values[myStateNumber];
|
||||
final state = myStateArgs.toState();
|
||||
if (_state == state) {
|
||||
return;
|
||||
}
|
||||
_state = state;
|
||||
final eventArgs = CentralStateChangedEventArgs(state);
|
||||
_stateChangedController.add(eventArgs);
|
||||
}
|
||||
|
||||
@override
|
||||
void onDiscovered(
|
||||
MyPeripheralArgs myPeripheralArgs,
|
||||
int rssi,
|
||||
MyAdvertisementArgs myAdvertisementArgs,
|
||||
) {
|
||||
final myPeripheral = MyPeripheral.fromMyArgs(myPeripheralArgs);
|
||||
_myPeripherals[myPeripheral.hashCode] = myPeripheral;
|
||||
final advertisement = myAdvertisementArgs.toAdvertisement();
|
||||
final eventArgs = CentralDiscoveredEventArgs(
|
||||
myPeripheral,
|
||||
rssi,
|
||||
advertisement,
|
||||
);
|
||||
_discoveredController.add(eventArgs);
|
||||
}
|
||||
|
||||
@override
|
||||
void onPeripheralStateChanged(int myPeripheralKey, bool state) {
|
||||
final myPeripheral = _myPeripherals[myPeripheralKey];
|
||||
if (myPeripheral == null) {
|
||||
return;
|
||||
}
|
||||
final eventArgs = PeripheralStateChangedEventArgs(myPeripheral, state);
|
||||
_peripheralStateChangedController.add(eventArgs);
|
||||
}
|
||||
|
||||
@override
|
||||
void onCharacteristicValueChanged(int myCharacteristicKey, Uint8List value) {
|
||||
final myCharacteristic =
|
||||
_myCharacteristics[myCharacteristicKey] as MyGattCharacteristic;
|
||||
final eventArgs = GattCharacteristicValueChangedEventArgs(
|
||||
myCharacteristic,
|
||||
value,
|
||||
);
|
||||
_characteristicValueChangedController.add(eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
extension on MyAdvertisementArgs {
|
||||
Advertisement toAdvertisement() {
|
||||
final serviceUUIDs = this
|
||||
.serviceUUIDs
|
||||
.cast<String>()
|
||||
.map((uuid) => UUID.fromString(uuid))
|
||||
.toList();
|
||||
final serviceData = this.serviceData.cast<String, Uint8List>().map(
|
||||
(uuid, data) {
|
||||
final key = UUID.fromString(uuid);
|
||||
final value = data;
|
||||
return MapEntry(key, value);
|
||||
},
|
||||
);
|
||||
return Advertisement(
|
||||
name: name,
|
||||
manufacturerSpecificData: manufacturerSpecificData.cast<int, Uint8List>(),
|
||||
serviceUUIDs: serviceUUIDs,
|
||||
serviceData: serviceData,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on MyCentralStateArgs {
|
||||
CentralState toState() {
|
||||
return CentralState.values[index];
|
||||
}
|
||||
}
|
||||
|
||||
extension on GattCharacteristicWriteType {
|
||||
MyGattCharacteristicWriteTypeArgs toMyArgs() {
|
||||
return MyGattCharacteristicWriteTypeArgs.values[index];
|
||||
}
|
||||
}
|
272
bluetooth_low_energy_darwin/lib/src/my_central_manager.dart
Normal file
272
bluetooth_low_energy_darwin/lib/src/my_central_manager.dart
Normal file
@ -0,0 +1,272 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.dart';
|
||||
import 'my_bluetooth_low_energy_manager.dart';
|
||||
import 'my_gatt_characteristic2.dart';
|
||||
import 'my_gatt_descriptor2.dart';
|
||||
|
||||
class MyCentralManager extends MyBluetoothLowEnergyManager
|
||||
implements CentralManager, MyCentralManagerFlutterApi {
|
||||
MyCentralManager()
|
||||
: _api = MyCentralManagerHostApi(),
|
||||
_discoveredController = StreamController.broadcast(),
|
||||
_peripheralStateChangedController = StreamController.broadcast(),
|
||||
_characteristicValueChangedController = StreamController.broadcast();
|
||||
|
||||
final MyCentralManagerHostApi _api;
|
||||
final StreamController<DiscoveredEventArgs> _discoveredController;
|
||||
final StreamController<PeripheralStateChangedEventArgs>
|
||||
_peripheralStateChangedController;
|
||||
final StreamController<GattCharacteristicValueChangedEventArgs>
|
||||
_characteristicValueChangedController;
|
||||
|
||||
@override
|
||||
Stream<DiscoveredEventArgs> get discovered => _discoveredController.stream;
|
||||
@override
|
||||
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
|
||||
_peripheralStateChangedController.stream;
|
||||
@override
|
||||
Stream<GattCharacteristicValueChangedEventArgs>
|
||||
get characteristicValueChanged =>
|
||||
_characteristicValueChangedController.stream;
|
||||
|
||||
@override
|
||||
Future<void> setUp() async {
|
||||
await throwWithoutState(BluetoothLowEnergyState.unknown);
|
||||
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,
|
||||
MyAdvertiseDataArgs advertiseDataArgs,
|
||||
) {
|
||||
final peripheral = peripheralArgs.toPeripheral();
|
||||
final rssi = rssiArgs;
|
||||
final advertiseData = advertiseDataArgs.toAdvertiseData();
|
||||
final eventArgs = DiscoveredEventArgs(
|
||||
peripheral,
|
||||
rssi,
|
||||
advertiseData,
|
||||
);
|
||||
_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);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_gatt_service.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyGattCharacteristic extends MyObject implements GattCharacteristic {
|
||||
final MyGattService myService;
|
||||
@override
|
||||
final UUID uuid;
|
||||
@override
|
||||
final List<GattCharacteristicProperty> properties;
|
||||
|
||||
MyGattCharacteristic(
|
||||
super.hashCode,
|
||||
this.myService,
|
||||
this.uuid,
|
||||
this.properties,
|
||||
);
|
||||
|
||||
factory MyGattCharacteristic.fromMyArgs(
|
||||
MyGattService myService,
|
||||
MyGattCharacteristicArgs myArgs,
|
||||
) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
final properties = myArgs.myPropertyNumbers.cast<int>().map(
|
||||
(myPropertyNumber) {
|
||||
final myPropertyArgs =
|
||||
MyGattCharacteristicPropertyArgs.values[myPropertyNumber];
|
||||
return myPropertyArgs.toProperty();
|
||||
},
|
||||
).toList();
|
||||
return MyGattCharacteristic(hashCode, myService, uuid, properties);
|
||||
}
|
||||
}
|
||||
|
||||
extension on MyGattCharacteristicPropertyArgs {
|
||||
GattCharacteristicProperty toProperty() {
|
||||
return GattCharacteristicProperty.values[index];
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
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;
|
||||
|
||||
MyGattCharacteristic2({
|
||||
super.hashCode,
|
||||
required super.uuid,
|
||||
required super.properties,
|
||||
required List<MyGattDescriptor2> descriptors,
|
||||
}) : super(descriptors: descriptors);
|
||||
|
||||
@override
|
||||
List<MyGattDescriptor2> get descriptors =>
|
||||
super.descriptors.cast<MyGattDescriptor2>();
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_gatt_characteristic.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyGattDescriptor extends MyObject implements GattDescriptor {
|
||||
final MyGattCharacteristic myCharacteristic;
|
||||
@override
|
||||
final UUID uuid;
|
||||
|
||||
MyGattDescriptor(super.hashCode, this.myCharacteristic, this.uuid);
|
||||
|
||||
factory MyGattDescriptor.fromMyArgs(
|
||||
MyGattCharacteristic myCharacteristic,
|
||||
MyGattDescriptorArgs myArgs,
|
||||
) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
return MyGattDescriptor(hashCode, myCharacteristic, uuid);
|
||||
}
|
||||
}
|
12
bluetooth_low_energy_darwin/lib/src/my_gatt_descriptor2.dart
Normal file
12
bluetooth_low_energy_darwin/lib/src/my_gatt_descriptor2.dart
Normal file
@ -0,0 +1,12 @@
|
||||
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;
|
||||
|
||||
MyGattDescriptor2({
|
||||
super.hashCode,
|
||||
required super.uuid,
|
||||
});
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_object.dart';
|
||||
import 'my_peripheral.dart';
|
||||
|
||||
class MyGattService extends MyObject implements GattService {
|
||||
final MyPeripheral myPeripheral;
|
||||
@override
|
||||
final UUID uuid;
|
||||
|
||||
MyGattService(super.hashCode, this.myPeripheral, this.uuid);
|
||||
|
||||
factory MyGattService.fromMyArgs(
|
||||
MyPeripheral myPeripheral,
|
||||
MyGattServiceArgs myArgs,
|
||||
) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
return MyGattService(hashCode, myPeripheral, uuid);
|
||||
}
|
||||
}
|
17
bluetooth_low_energy_darwin/lib/src/my_gatt_service2.dart
Normal file
17
bluetooth_low_energy_darwin/lib/src/my_gatt_service2.dart
Normal file
@ -0,0 +1,17 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_gatt_characteristic2.dart';
|
||||
|
||||
class MyGattService2 extends MyGattService {
|
||||
late final MyPeripheral peripheral;
|
||||
|
||||
MyGattService2({
|
||||
super.hashCode,
|
||||
required super.uuid,
|
||||
required List<MyGattCharacteristic2> characteristics,
|
||||
}) : super(characteristics: characteristics);
|
||||
|
||||
@override
|
||||
List<MyGattCharacteristic2> get characteristics =>
|
||||
super.characteristics.cast<MyGattCharacteristic2>();
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
abstract class MyObject {
|
||||
@override
|
||||
final int hashCode;
|
||||
|
||||
MyObject(this.hashCode);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is MyObject && other.hashCode == hashCode;
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyPeripheral extends MyObject implements Peripheral {
|
||||
@override
|
||||
final UUID uuid;
|
||||
|
||||
MyPeripheral(super.hashCode, this.uuid);
|
||||
|
||||
factory MyPeripheral.fromMyArgs(MyPeripheralArgs myArgs) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
return MyPeripheral(hashCode, uuid);
|
||||
}
|
||||
}
|
228
bluetooth_low_energy_darwin/lib/src/my_peripheral_manager.dart
Normal file
228
bluetooth_low_energy_darwin/lib/src/my_peripheral_manager.dart
Normal file
@ -0,0 +1,228 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.dart';
|
||||
import 'my_bluetooth_low_energy_manager.dart';
|
||||
|
||||
class MyPeripheralManager extends MyBluetoothLowEnergyManager
|
||||
implements PeripheralManager, MyPeripheralManagerFlutterApi {
|
||||
final MyPeripheralManagerHostApi _api;
|
||||
final StreamController<ReadGattCharacteristicCommandEventArgs>
|
||||
_readCharacteristicCommandReceivedController;
|
||||
final StreamController<WriteGattCharacteristicCommandEventArgs>
|
||||
_writeCharacteristicCommandReceivedController;
|
||||
final StreamController<NotifyGattCharacteristicCommandEventArgs>
|
||||
_notifyCharacteristicCommandReceivedController;
|
||||
|
||||
MyPeripheralManager()
|
||||
: _api = MyPeripheralManagerHostApi(),
|
||||
_readCharacteristicCommandReceivedController =
|
||||
StreamController.broadcast(),
|
||||
_writeCharacteristicCommandReceivedController =
|
||||
StreamController.broadcast(),
|
||||
_notifyCharacteristicCommandReceivedController =
|
||||
StreamController.broadcast();
|
||||
|
||||
@override
|
||||
Stream<ReadGattCharacteristicCommandEventArgs>
|
||||
get readCharacteristicCommandReceived =>
|
||||
_readCharacteristicCommandReceivedController.stream;
|
||||
|
||||
@override
|
||||
Stream<WriteGattCharacteristicCommandEventArgs>
|
||||
get writeCharacteristicCommandReceived =>
|
||||
_writeCharacteristicCommandReceivedController.stream;
|
||||
|
||||
@override
|
||||
Stream<NotifyGattCharacteristicCommandEventArgs>
|
||||
get notifyCharacteristicCommandReceived =>
|
||||
_notifyCharacteristicCommandReceivedController.stream;
|
||||
|
||||
@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(AdvertiseData advertiseData) async {
|
||||
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
|
||||
final advertiseDataArgs = advertiseData.toArgs();
|
||||
await _api.startAdvertising(advertiseDataArgs);
|
||||
}
|
||||
|
||||
@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,
|
||||
GattCharacteristic characteristic,
|
||||
int id,
|
||||
int offset,
|
||||
bool status,
|
||||
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,
|
||||
GattCharacteristic characteristic,
|
||||
int id,
|
||||
int offset,
|
||||
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,
|
||||
GattCharacteristic characteristic,
|
||||
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);
|
||||
}
|
||||
}
|
@ -9,124 +9,213 @@ import 'package:pigeon/pigeon.dart';
|
||||
),
|
||||
)
|
||||
@HostApi()
|
||||
abstract class MyCentralControllerHostApi {
|
||||
abstract class MyCentralManagerHostApi {
|
||||
@async
|
||||
MyCentralControllerArgs setUp();
|
||||
void tearDown();
|
||||
MyCentralManagerArgs setUp();
|
||||
void startDiscovery();
|
||||
void stopDiscovery();
|
||||
@async
|
||||
void connect(int myPeripheralKey);
|
||||
void connect(int peripheralHashCodeArgs);
|
||||
@async
|
||||
void disconnect(int myPeripheralKey);
|
||||
int getMaximumWriteLength(int myPeripheralKey, int myTypeNumber);
|
||||
void disconnect(int peripheralHashCodeArgs);
|
||||
int getMaximumWriteLength(int peripheralHashCodeArgs, int typeNumberArgs);
|
||||
@async
|
||||
void discoverGATT(int myPeripheralKey);
|
||||
List<MyGattServiceArgs> getServices(int myPeripheralKey);
|
||||
List<MyGattCharacteristicArgs> getCharacteristics(int myServiceKey);
|
||||
List<MyGattDescriptorArgs> getDescriptors(int myCharacteristicKey);
|
||||
int readRSSI(int peripheralHashCodeArgs);
|
||||
@async
|
||||
List<MyGattServiceArgs> discoverGATT(int peripheralHashCodeArgs);
|
||||
@async
|
||||
Uint8List readCharacteristic(
|
||||
int myPeripheralKey,
|
||||
int myServiceKey,
|
||||
int myCharacteristicKey,
|
||||
int peripheralHashCodeArgs,
|
||||
int characteristicHashCodeArgs,
|
||||
);
|
||||
@async
|
||||
void writeCharacteristic(
|
||||
int myPeripheralKey,
|
||||
int myServiceKey,
|
||||
int myCharacteristicKey,
|
||||
Uint8List value,
|
||||
int myTypeNumber,
|
||||
int peripheralHashCodeArgs,
|
||||
int characteristicHashCodeArgs,
|
||||
Uint8List valueArgs,
|
||||
int typeNumberArgs,
|
||||
);
|
||||
@async
|
||||
void notifyCharacteristic(
|
||||
int myPeripheralKey,
|
||||
int myServiceKey,
|
||||
int myCharacteristicKey,
|
||||
bool state,
|
||||
int peripheralHashCodeArgs,
|
||||
int characteristicHashCodeArgs,
|
||||
bool stateArgs,
|
||||
);
|
||||
@async
|
||||
Uint8List readDescriptor(
|
||||
int myPeripheralKey,
|
||||
int myCharacteristicKey,
|
||||
int myDescriptorKey,
|
||||
int peripheralHashCodeArgs,
|
||||
int descriptorHashCodeArgs,
|
||||
);
|
||||
@async
|
||||
void writeDescriptor(
|
||||
int myPeripheralKey,
|
||||
int myCharacteristicKey,
|
||||
int myDescriptorKey,
|
||||
Uint8List value,
|
||||
int peripheralHashCodeArgs,
|
||||
int descriptorHashCodeArgs,
|
||||
Uint8List valueArgs,
|
||||
);
|
||||
}
|
||||
|
||||
@FlutterApi()
|
||||
abstract class MyCentralControllerFlutterApi {
|
||||
void onStateChanged(int myStateNumber);
|
||||
abstract class MyCentralManagerFlutterApi {
|
||||
void onStateChanged(int stateNumberArgs);
|
||||
void onDiscovered(
|
||||
MyPeripheralArgs myPeripheralArgs,
|
||||
int rssi,
|
||||
MyAdvertisementArgs myAdvertisementArgs,
|
||||
MyPeripheralArgs peripheralArgs,
|
||||
int rssiArgs,
|
||||
MyAdvertiseDataArgs advertiseDataArgs,
|
||||
);
|
||||
void onPeripheralStateChanged(
|
||||
MyPeripheralArgs peripheralArgs,
|
||||
bool stateArgs,
|
||||
);
|
||||
void onCharacteristicValueChanged(
|
||||
MyGattCharacteristicArgs characteristicArgs,
|
||||
Uint8List valueArgs,
|
||||
);
|
||||
void onPeripheralStateChanged(int myPeripheralKey, bool state);
|
||||
void onCharacteristicValueChanged(int myCharacteristicKey, Uint8List value);
|
||||
}
|
||||
|
||||
class MyCentralControllerArgs {
|
||||
final int myStateNumber;
|
||||
@HostApi()
|
||||
abstract class MyPeripheralManagerHostApi {
|
||||
@async
|
||||
MyPeripheralManagerArgs setUp();
|
||||
@async
|
||||
void addService(MyGattServiceArgs serviceArgs);
|
||||
void removeService(int serviceHashCodeArgs);
|
||||
void clearServices();
|
||||
@async
|
||||
void startAdvertising(MyAdvertiseDataArgs advertiseDataArgs);
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
MyCentralControllerArgs(this.myStateNumber);
|
||||
@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 key;
|
||||
final String uuid;
|
||||
final int hashCodeArgs;
|
||||
final String uuidArgs;
|
||||
|
||||
MyPeripheralArgs(this.key, this.uuid);
|
||||
MyPeripheralArgs(this.hashCodeArgs, this.uuidArgs);
|
||||
}
|
||||
|
||||
class MyAdvertisementArgs {
|
||||
final String? name;
|
||||
final Map<int?, Uint8List?> manufacturerSpecificData;
|
||||
final List<String?> serviceUUIDs;
|
||||
final Map<String?, Uint8List?> serviceData;
|
||||
class MyAdvertiseDataArgs {
|
||||
final String? nameArgs;
|
||||
final List<String?> serviceUUIDsArgs;
|
||||
final Map<String?, Uint8List?> serviceDataArgs;
|
||||
final MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs;
|
||||
|
||||
MyAdvertisementArgs(
|
||||
this.name,
|
||||
this.manufacturerSpecificData,
|
||||
this.serviceUUIDs,
|
||||
this.serviceData,
|
||||
MyAdvertiseDataArgs(
|
||||
this.nameArgs,
|
||||
this.serviceUUIDsArgs,
|
||||
this.serviceDataArgs,
|
||||
this.manufacturerSpecificDataArgs,
|
||||
);
|
||||
}
|
||||
|
||||
class MyGattServiceArgs {
|
||||
final int key;
|
||||
final String uuid;
|
||||
class MyManufacturerSpecificDataArgs {
|
||||
final int idArgs;
|
||||
final Uint8List dataArgs;
|
||||
|
||||
MyGattServiceArgs(this.key, this.uuid);
|
||||
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 key;
|
||||
final String uuid;
|
||||
final List<int?> myPropertyNumbers;
|
||||
final int hashCodeArgs;
|
||||
final String uuidArgs;
|
||||
final List<int?> propertyNumbersArgs;
|
||||
final List<MyGattDescriptorArgs?> descriptorsArgs;
|
||||
|
||||
MyGattCharacteristicArgs(
|
||||
this.key,
|
||||
this.uuid,
|
||||
this.myPropertyNumbers,
|
||||
this.hashCodeArgs,
|
||||
this.uuidArgs,
|
||||
this.propertyNumbersArgs,
|
||||
this.descriptorsArgs,
|
||||
);
|
||||
}
|
||||
|
||||
class MyGattDescriptorArgs {
|
||||
final int key;
|
||||
final String uuid;
|
||||
final int hashCodeArgs;
|
||||
final String uuidArgs;
|
||||
final Uint8List? valueArgs;
|
||||
|
||||
MyGattDescriptorArgs(this.key, this.uuid);
|
||||
MyGattDescriptorArgs(
|
||||
this.hashCodeArgs,
|
||||
this.uuidArgs,
|
||||
this.valueArgs,
|
||||
);
|
||||
}
|
||||
|
||||
enum MyCentralStateArgs {
|
||||
enum MyBluetoothLowEnergyStateArgs {
|
||||
unknown,
|
||||
unsupported,
|
||||
unauthorized,
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: bluetooth_low_energy_darwin
|
||||
description: iOS and macOS implementation of the bluetooth_low_energy plugin.
|
||||
version: 2.2.0
|
||||
version: 3.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: ^2.2.0
|
||||
bluetooth_low_energy_platform_interface: ^3.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
pigeon: ^10.1.6
|
||||
pigeon: ^11.0.1
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
|
@ -1,3 +1,37 @@
|
||||
## 3.0.0
|
||||
|
||||
* Add `PeripheralManager` api.
|
||||
* Add `CentralManager#readRSSI` method.
|
||||
* Add `CentralManager.instance` api.
|
||||
* Add `PeripheralManager.instance` api.
|
||||
* Move `CentralController` to `CentralManager`.
|
||||
* Move `CentralState` to `BluetoothLowEnergyState`.
|
||||
* Move `CentralDiscoveredEventArgs` to `DiscoveredEventArgs`.
|
||||
* Move `Advertisement` class to `AdvertiseData` class.
|
||||
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
|
||||
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
|
||||
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
|
||||
* Fix known issues.
|
||||
|
||||
## 3.0.0-dev.4
|
||||
|
||||
* Move `Advertisement` class to `AdvertiseData` class.
|
||||
|
||||
## 3.0.0-dev.3
|
||||
|
||||
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
|
||||
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
|
||||
|
||||
## 3.0.0-dev.2
|
||||
|
||||
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
|
||||
* Add `CentralManager.instance` api.
|
||||
* Add `PeripheralManager.instance` api.
|
||||
|
||||
## 3.0.0-dev.1
|
||||
|
||||
* Implement new central manager api.
|
||||
|
||||
## 2.2.0
|
||||
|
||||
* Add `CentralController#getMaximumWriteLength` method.
|
||||
|
@ -1,9 +1,9 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'src/my_central_controller.dart';
|
||||
import 'src/my_bluetooth_low_energy.dart';
|
||||
|
||||
abstract class BluetoothLowEnergyLinux {
|
||||
static void registerWith() {
|
||||
CentralController.instance = MyCentralController();
|
||||
BluetoothLowEnergy.instance = MyBluetoothLowEnergy();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_central_manager.dart';
|
||||
|
||||
class MyBluetoothLowEnergy extends BluetoothLowEnergy {
|
||||
@override
|
||||
final MyCentralManager centralManager;
|
||||
@override
|
||||
PeripheralManager get peripheralManager => throw UnimplementedError();
|
||||
|
||||
MyBluetoothLowEnergy() : centralManager = MyCentralManager();
|
||||
}
|
@ -3,54 +3,87 @@ import 'dart:typed_data';
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
import 'package:bluez/bluez.dart';
|
||||
|
||||
extension MyBlueZAdapter on BlueZAdapter {
|
||||
CentralState get state {
|
||||
return powered ? CentralState.poweredOn : CentralState.poweredOff;
|
||||
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 MyBlueZDevice on BlueZDevice {
|
||||
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");
|
||||
}
|
||||
|
||||
Advertisement get advertisement {
|
||||
final name = this.name.isNotEmpty ? this.name : null;
|
||||
final manufacturerSpecificData = manufacturerData.map((key, value) {
|
||||
final id = key.id;
|
||||
final data = Uint8List.fromList(value);
|
||||
return MapEntry(id, data);
|
||||
UUID get myUUID => uuid.toMyUUID();
|
||||
|
||||
List<MyGattService2> get myServices =>
|
||||
gattServices.map((service) => MyGattService2(service)).toList();
|
||||
|
||||
AdvertiseData get myAdvertiseData {
|
||||
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);
|
||||
});
|
||||
final serviceUUIDs = uuids.map((uuid) => uuid.toUUID()).toList();
|
||||
final serviceData = this.serviceData.map((uuid, data) {
|
||||
final key = uuid.toUUID();
|
||||
final value = Uint8List.fromList(data);
|
||||
return MapEntry(key, value);
|
||||
});
|
||||
return Advertisement(
|
||||
name: name,
|
||||
manufacturerSpecificData: manufacturerSpecificData,
|
||||
serviceUUIDs: serviceUUIDs,
|
||||
serviceData: serviceData,
|
||||
return AdvertiseData(
|
||||
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 MyBlueZUUID on BlueZUUID {
|
||||
UUID toUUID() => UUID(value);
|
||||
}
|
||||
extension BlueZGattServiceX on BlueZGattService {
|
||||
UUID get myUUID => uuid.toMyUUID();
|
||||
|
||||
extension MyBlueZGattCharacteristic on BlueZGattCharacteristic {
|
||||
List<GattCharacteristicProperty> get properties => flags
|
||||
.map((e) => e.toProperty())
|
||||
.whereType<GattCharacteristicProperty>()
|
||||
List<MyGattCharacteristic2> get myCharacteristics => characteristics
|
||||
.map((characteristic) => MyGattCharacteristic2(characteristic))
|
||||
.toList();
|
||||
}
|
||||
|
||||
extension MyBlueZGattCharacteristicFlag on BlueZGattCharacteristicFlag {
|
||||
GattCharacteristicProperty? toProperty() {
|
||||
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) {
|
||||
case BlueZGattCharacteristicFlag.read:
|
||||
return GattCharacteristicProperty.read;
|
||||
@ -68,8 +101,8 @@ extension MyBlueZGattCharacteristicFlag on BlueZGattCharacteristicFlag {
|
||||
}
|
||||
}
|
||||
|
||||
extension MyGattCharacteristicWriteType on GattCharacteristicWriteType {
|
||||
BlueZGattCharacteristicWriteType toBlueZ() {
|
||||
extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType {
|
||||
BlueZGattCharacteristicWriteType toBlueZWriteType() {
|
||||
switch (this) {
|
||||
case GattCharacteristicWriteType.withResponse:
|
||||
return BlueZGattCharacteristicWriteType.request;
|
||||
|
@ -1,443 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluetooth_low_energy_linux/src/my_event_args.dart';
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
import 'package:bluez/bluez.dart';
|
||||
|
||||
import 'my_bluez.dart';
|
||||
import 'my_gatt_characteristic.dart';
|
||||
import 'my_gatt_descriptor.dart';
|
||||
import 'my_gatt_service.dart';
|
||||
import 'my_peripheral.dart';
|
||||
|
||||
class MyCentralController extends CentralController {
|
||||
MyCentralController()
|
||||
: _client = BlueZClient(),
|
||||
_stateChangedController = StreamController.broadcast(),
|
||||
_discoveredController = StreamController.broadcast(),
|
||||
_peripheralStateChangedController = StreamController.broadcast(),
|
||||
_characteristicValueChangedController = StreamController.broadcast(),
|
||||
_myPeripheralDiscoveredController = StreamController.broadcast(),
|
||||
_devicePropertiesChangedSubscriptions = {},
|
||||
_characteristicPropertiesChangedSubscriptions = {},
|
||||
_myPeripherals = {},
|
||||
_myServices = {},
|
||||
_myCharacteristics = {},
|
||||
_myDescriptors = {},
|
||||
_state = CentralState.unknown;
|
||||
|
||||
final BlueZClient _client;
|
||||
final StreamController<CentralStateChangedEventArgs> _stateChangedController;
|
||||
final StreamController<CentralDiscoveredEventArgs> _discoveredController;
|
||||
final StreamController<PeripheralStateChangedEventArgs>
|
||||
_peripheralStateChangedController;
|
||||
final StreamController<GattCharacteristicValueChangedEventArgs>
|
||||
_characteristicValueChangedController;
|
||||
final StreamController<MyPeripheralDiscoveredEventArgs>
|
||||
_myPeripheralDiscoveredController;
|
||||
final Map<int, StreamSubscription<List<String>>>
|
||||
_devicePropertiesChangedSubscriptions;
|
||||
final Map<int, StreamSubscription<List<String>>>
|
||||
_characteristicPropertiesChangedSubscriptions;
|
||||
final Map<int, MyPeripheral> _myPeripherals;
|
||||
final Map<int, Map<int, MyGattService>> _myServices;
|
||||
final Map<int, Map<int, MyGattCharacteristic>> _myCharacteristics;
|
||||
final Map<int, Map<int, MyGattDescriptor>> _myDescriptors;
|
||||
|
||||
BlueZAdapter get _adapter => _client.adapters.first;
|
||||
CentralState _state;
|
||||
@override
|
||||
CentralState get state => _state;
|
||||
|
||||
@override
|
||||
Stream<CentralStateChangedEventArgs> get stateChanged =>
|
||||
_stateChangedController.stream;
|
||||
@override
|
||||
Stream<CentralDiscoveredEventArgs> get discovered =>
|
||||
_discoveredController.stream;
|
||||
@override
|
||||
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
|
||||
_peripheralStateChangedController.stream;
|
||||
@override
|
||||
Stream<GattCharacteristicValueChangedEventArgs>
|
||||
get characteristicValueChanged =>
|
||||
_characteristicValueChangedController.stream;
|
||||
Stream<MyPeripheralDiscoveredEventArgs> get _myPeripheralDiscovered =>
|
||||
_myPeripheralDiscoveredController.stream;
|
||||
|
||||
late StreamSubscription<List<String>> _adapterPropertiesChangedSubscription;
|
||||
late StreamSubscription<BlueZDevice> _deviceAddedSubscription;
|
||||
late StreamSubscription<BlueZDevice> _deviceRemovedSubscription;
|
||||
|
||||
Future<void> _throwWithState(CentralState state) async {
|
||||
if (this.state == state) {
|
||||
throw BluetoothLowEnergyError('$state is unexpected.');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _throwWithoutState(CentralState state) async {
|
||||
if (this.state != state) {
|
||||
throw BluetoothLowEnergyError(
|
||||
'$state is expected, but current state is ${this.state}.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setUp() async {
|
||||
await _throwWithoutState(CentralState.unknown);
|
||||
await _client.connect();
|
||||
_state =
|
||||
_client.adapters.isEmpty ? CentralState.unsupported : _adapter.state;
|
||||
if (_state == CentralState.unsupported) {
|
||||
return;
|
||||
}
|
||||
for (var device in _client.devices) {
|
||||
if (device.adapter.address != _adapter.address) {
|
||||
continue;
|
||||
}
|
||||
_beginDevicePropertiesChangedListener(device);
|
||||
}
|
||||
_adapterPropertiesChangedSubscription = _adapter.propertiesChanged.listen(
|
||||
_onAdapterPropertiesChanged,
|
||||
);
|
||||
_deviceAddedSubscription = _client.deviceAdded.listen(_onDeviceAdded);
|
||||
_deviceRemovedSubscription = _client.deviceRemoved.listen(_onDeviceRemoved);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> tearDown() async {
|
||||
await _throwWithState(CentralState.unknown);
|
||||
if (_state != CentralState.unsupported && _adapter.discovering) {
|
||||
await _adapter.stopDiscovery();
|
||||
}
|
||||
for (var myPeripheral in _myPeripherals.values) {
|
||||
final device = myPeripheral.device;
|
||||
if (device.connected) {
|
||||
await device.disconnect();
|
||||
}
|
||||
}
|
||||
_myPeripherals.clear();
|
||||
_myServices.clear();
|
||||
_myCharacteristics.clear();
|
||||
_myDescriptors.clear();
|
||||
for (var device in _client.devices) {
|
||||
if (device.adapter.address != _adapter.address) {
|
||||
continue;
|
||||
}
|
||||
_endDevicePropertiesChangedListener(device);
|
||||
}
|
||||
_adapterPropertiesChangedSubscription.cancel();
|
||||
_deviceAddedSubscription.cancel();
|
||||
_deviceRemovedSubscription.cancel();
|
||||
await _client.close();
|
||||
_state = CentralState.unknown;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startDiscovery() async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
await _adapter.startDiscovery();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopDiscovery() async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
await _adapter.stopDiscovery();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> connect(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
final device = myPeripheral.device;
|
||||
await device.connect();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> disconnect(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
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<void> discoverGATT(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
final device = myPeripheral.device;
|
||||
if (!device.connected) {
|
||||
throw BluetoothLowEnergyError('Peripheral is disconnected.');
|
||||
}
|
||||
if (device.servicesResolved) {
|
||||
return;
|
||||
}
|
||||
await _myPeripheralDiscovered.firstWhere(
|
||||
(eventArgs) => eventArgs.myPeripheral == myPeripheral,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattService>> getServices(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
final myServices = _myServices[myPeripheral.hashCode];
|
||||
if (myServices == null) {
|
||||
throw ArgumentError();
|
||||
}
|
||||
return myServices.values.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattCharacteristic>> getCharacteristics(
|
||||
GattService service,
|
||||
) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myService = service as MyGattService;
|
||||
final myCharacteristics = _myCharacteristics[myService.hashCode];
|
||||
if (myCharacteristics == null) {
|
||||
throw ArgumentError();
|
||||
}
|
||||
return myCharacteristics.values.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattDescriptor>> getDescriptors(
|
||||
GattCharacteristic characteristic,
|
||||
) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myDescriptors = _myDescriptors[myCharacteristic.hashCode];
|
||||
if (myDescriptors == null) {
|
||||
throw ArgumentError();
|
||||
}
|
||||
return myDescriptors.values.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> readCharacteristic(
|
||||
GattCharacteristic characteristic,
|
||||
) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
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(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final blueZCharacteristic = myCharacteristic.characteristic;
|
||||
await blueZCharacteristic.writeValue(
|
||||
value,
|
||||
type: type.toBlueZ(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> notifyCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
required bool state,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final blueZCharacteristic = myCharacteristic.characteristic;
|
||||
if (state) {
|
||||
await blueZCharacteristic.startNotify();
|
||||
} else {
|
||||
await blueZCharacteristic.stopNotify();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myDescriptor = descriptor as MyGattDescriptor;
|
||||
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(CentralState.poweredOn);
|
||||
final myDescriptor = descriptor as MyGattDescriptor;
|
||||
final blueZDescriptor = myDescriptor.descriptor;
|
||||
await blueZDescriptor.writeValue(value);
|
||||
}
|
||||
|
||||
void _onAdapterPropertiesChanged(List<String> properties) {
|
||||
for (var property in properties) {
|
||||
switch (property) {
|
||||
case 'Powered':
|
||||
final state = _adapter.state;
|
||||
if (_state == state) {
|
||||
return;
|
||||
}
|
||||
_state = state;
|
||||
final eventArgs = CentralStateChangedEventArgs(state);
|
||||
_stateChangedController.add(eventArgs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onDeviceAdded(BlueZDevice device) {
|
||||
if (device.adapter.address != _adapter.address) {
|
||||
return;
|
||||
}
|
||||
_onDiscovered(device);
|
||||
_beginDevicePropertiesChangedListener(device);
|
||||
}
|
||||
|
||||
void _onDeviceRemoved(BlueZDevice device) {
|
||||
if (device.adapter.address != _adapter.address) {
|
||||
return;
|
||||
}
|
||||
_endDevicePropertiesChangedListener(device);
|
||||
}
|
||||
|
||||
void _onDiscovered(BlueZDevice device) {
|
||||
final myPeripheral = MyPeripheral(device);
|
||||
_myPeripherals[myPeripheral.hashCode] = myPeripheral;
|
||||
final rssi = device.rssi;
|
||||
final advertisement = device.advertisement;
|
||||
final eventArgs = CentralDiscoveredEventArgs(
|
||||
myPeripheral,
|
||||
rssi,
|
||||
advertisement,
|
||||
);
|
||||
_discoveredController.add(eventArgs);
|
||||
}
|
||||
|
||||
void _beginDevicePropertiesChangedListener(BlueZDevice device) {
|
||||
final subscription = device.propertiesChanged.listen((properties) {
|
||||
for (var property in properties) {
|
||||
switch (property) {
|
||||
case 'RSSI':
|
||||
_onDiscovered(device);
|
||||
break;
|
||||
case 'Connected':
|
||||
final myPeripheral =
|
||||
_myPeripherals[device.hashCode] as MyPeripheral;
|
||||
final state = device.connected;
|
||||
final eventArgs = PeripheralStateChangedEventArgs(
|
||||
myPeripheral,
|
||||
state,
|
||||
);
|
||||
_peripheralStateChangedController.add(eventArgs);
|
||||
break;
|
||||
case 'UUIDs':
|
||||
break;
|
||||
case 'ServicesResolved':
|
||||
if (device.servicesResolved) {
|
||||
final myPeripheral =
|
||||
_myPeripherals[device.hashCode] as MyPeripheral;
|
||||
final myServices = <int, MyGattService>{};
|
||||
for (var service in device.gattServices) {
|
||||
final myService = MyGattService(service);
|
||||
myServices[myService.hashCode] = myService;
|
||||
final myCharacteristics = <int, MyGattCharacteristic>{};
|
||||
for (var characteristic in service.characteristics) {
|
||||
final myCharacteristic = MyGattCharacteristic(characteristic);
|
||||
myCharacteristics[myCharacteristic.hashCode] =
|
||||
myCharacteristic;
|
||||
_beginCharacteristicPropertiesChangedListener(
|
||||
service,
|
||||
characteristic,
|
||||
);
|
||||
final myDescriptors = <int, MyGattDescriptor>{};
|
||||
for (var descriptor in characteristic.descriptors) {
|
||||
final myDescriptor = MyGattDescriptor(descriptor);
|
||||
myDescriptors[myDescriptor.hashCode] = myDescriptor;
|
||||
}
|
||||
_myDescriptors[myCharacteristic.hashCode] = myDescriptors;
|
||||
}
|
||||
_myCharacteristics[myService.hashCode] = myCharacteristics;
|
||||
}
|
||||
_myServices[myPeripheral.hashCode] = myServices;
|
||||
final eventArgs = MyPeripheralDiscoveredEventArgs(myPeripheral);
|
||||
_myPeripheralDiscoveredController.add(eventArgs);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
_devicePropertiesChangedSubscriptions[device.hashCode] = subscription;
|
||||
}
|
||||
|
||||
void _endDevicePropertiesChangedListener(BlueZDevice device) {
|
||||
for (var service in device.gattServices) {
|
||||
for (var characteristic in service.characteristics) {
|
||||
_endCharacteristicPropertiesChangedListener(characteristic);
|
||||
}
|
||||
}
|
||||
final subscription = _devicePropertiesChangedSubscriptions.remove(
|
||||
device.address,
|
||||
);
|
||||
subscription?.cancel();
|
||||
}
|
||||
|
||||
void _beginCharacteristicPropertiesChangedListener(
|
||||
BlueZGattService service,
|
||||
BlueZGattCharacteristic characteristic,
|
||||
) {
|
||||
final subscription = characteristic.propertiesChanged.listen((properties) {
|
||||
for (var property in properties) {
|
||||
switch (property) {
|
||||
case 'Value':
|
||||
final myCharacteristics = _myCharacteristics[service.hashCode];
|
||||
if (myCharacteristics == null) {
|
||||
throw ArgumentError();
|
||||
}
|
||||
final myCharacteristic = myCharacteristics[characteristic.hashCode]
|
||||
as MyGattCharacteristic;
|
||||
final value = Uint8List.fromList(characteristic.value);
|
||||
final eventArgs = GattCharacteristicValueChangedEventArgs(
|
||||
myCharacteristic,
|
||||
value,
|
||||
);
|
||||
_characteristicValueChangedController.add(eventArgs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
_characteristicPropertiesChangedSubscriptions[characteristic.hashCode] =
|
||||
subscription;
|
||||
}
|
||||
|
||||
void _endCharacteristicPropertiesChangedListener(
|
||||
BlueZGattCharacteristic characteristic,
|
||||
) {
|
||||
final subscription = _characteristicPropertiesChangedSubscriptions.remove(
|
||||
characteristic.hashCode,
|
||||
);
|
||||
subscription?.cancel();
|
||||
}
|
||||
}
|
342
bluetooth_low_energy_linux/lib/src/my_central_manager.dart
Normal file
342
bluetooth_low_energy_linux/lib/src/my_central_manager.dart
Normal file
@ -0,0 +1,342 @@
|
||||
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 {
|
||||
MyCentralManager()
|
||||
: _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 BluetoothLowEnergyError(
|
||||
'$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 BluetoothLowEnergyError('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.myAdvertiseData;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
import 'package:bluez/bluez.dart';
|
||||
|
||||
import 'my_peripheral.dart';
|
||||
class BlueZDeviceServicesResolvedEventArgs extends EventArgs {
|
||||
final BlueZDevice device;
|
||||
|
||||
class MyPeripheralDiscoveredEventArgs extends EventArgs {
|
||||
final MyPeripheral myPeripheral;
|
||||
|
||||
MyPeripheralDiscoveredEventArgs(this.myPeripheral);
|
||||
BlueZDeviceServicesResolvedEventArgs(this.device);
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
import 'package:bluez/bluez.dart';
|
||||
|
||||
import 'my_bluez.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyGattCharacteristic extends MyObject implements GattCharacteristic {
|
||||
final BlueZGattCharacteristic characteristic;
|
||||
|
||||
MyGattCharacteristic(this.characteristic) : super(characteristic);
|
||||
|
||||
@override
|
||||
UUID get uuid => characteristic.uuid.toUUID();
|
||||
|
||||
@override
|
||||
List<GattCharacteristicProperty> get properties => characteristic.properties;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
import 'package:bluez/bluez.dart';
|
||||
|
||||
import 'my_bluez.dart';
|
||||
|
||||
class MyGattCharacteristic2 extends MyGattCharacteristic {
|
||||
final BlueZGattCharacteristic characteristic;
|
||||
|
||||
MyGattCharacteristic2(this.characteristic)
|
||||
: super(
|
||||
hashCode: characteristic.hashCode,
|
||||
uuid: characteristic.myUUID,
|
||||
properties: characteristic.myProperties,
|
||||
descriptors: characteristic.myDescriptors,
|
||||
);
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
import 'package:bluez/bluez.dart';
|
||||
|
||||
import 'my_bluez.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyGattDescriptor extends MyObject implements GattDescriptor {
|
||||
final BlueZGattDescriptor descriptor;
|
||||
|
||||
MyGattDescriptor(this.descriptor) : super(descriptor);
|
||||
|
||||
@override
|
||||
UUID get uuid => descriptor.uuid.toUUID();
|
||||
}
|
14
bluetooth_low_energy_linux/lib/src/my_gatt_descriptor2.dart
Normal file
14
bluetooth_low_energy_linux/lib/src/my_gatt_descriptor2.dart
Normal file
@ -0,0 +1,14 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
import 'package:bluez/bluez.dart';
|
||||
|
||||
import 'my_bluez.dart';
|
||||
|
||||
class MyGattDescriptor2 extends MyGattDescriptor {
|
||||
final BlueZGattDescriptor descriptor;
|
||||
|
||||
MyGattDescriptor2(this.descriptor)
|
||||
: super(
|
||||
hashCode: descriptor.hashCode,
|
||||
uuid: descriptor.myUUID,
|
||||
);
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
import 'package:bluez/bluez.dart';
|
||||
|
||||
import 'my_bluez.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyGattService extends MyObject implements GattService {
|
||||
final BlueZGattService service;
|
||||
|
||||
MyGattService(this.service) : super(service);
|
||||
|
||||
@override
|
||||
UUID get uuid => service.uuid.toUUID();
|
||||
}
|
15
bluetooth_low_energy_linux/lib/src/my_gatt_service2.dart
Normal file
15
bluetooth_low_energy_linux/lib/src/my_gatt_service2.dart
Normal file
@ -0,0 +1,15 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
import 'package:bluez/bluez.dart';
|
||||
|
||||
import 'my_bluez.dart';
|
||||
|
||||
class MyGattService2 extends MyGattService {
|
||||
final BlueZGattService service;
|
||||
|
||||
MyGattService2(this.service)
|
||||
: super(
|
||||
hashCode: service.hashCode,
|
||||
uuid: service.myUUID,
|
||||
characteristics: service.myCharacteristics,
|
||||
);
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
abstract class MyObject {
|
||||
@override
|
||||
final int hashCode;
|
||||
|
||||
MyObject(Object instance) : hashCode = instance.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is MyObject && other.hashCode == hashCode;
|
||||
}
|
||||
}
|
@ -2,13 +2,13 @@ import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_pla
|
||||
import 'package:bluez/bluez.dart';
|
||||
|
||||
import 'my_bluez.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyPeripheral extends MyObject implements Peripheral {
|
||||
class MyPeripheral2 extends MyPeripheral {
|
||||
final BlueZDevice device;
|
||||
|
||||
MyPeripheral(this.device) : super(device);
|
||||
|
||||
@override
|
||||
UUID get uuid => device.uuid.toUUID();
|
||||
MyPeripheral2(this.device)
|
||||
: super(
|
||||
hashCode: device.hashCode,
|
||||
uuid: device.myUUID,
|
||||
);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
name: bluetooth_low_energy_linux
|
||||
description: Linux implementation of the bluetooth_low_energy plugin.
|
||||
version: 2.2.0
|
||||
version: 3.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: ^2.2.0
|
||||
bluetooth_low_energy_platform_interface: ^3.0.0
|
||||
bluez: ^0.8.1
|
||||
|
||||
dev_dependencies:
|
||||
|
@ -1,3 +1,45 @@
|
||||
## 3.0.0
|
||||
|
||||
* Add `PeripheralManager` api.
|
||||
* Add `CentralManager#readRSSI` method.
|
||||
* Add `CentralManager.instance` api.
|
||||
* Add `PeripheralManager.instance` api.
|
||||
* Move `CentralController` to `CentralManager`.
|
||||
* Move `CentralState` to `BluetoothLowEnergyState`.
|
||||
* Move `CentralDiscoveredEventArgs` to `DiscoveredEventArgs`.
|
||||
* Move `Advertisement` class to `AdvertiseData` class.
|
||||
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
|
||||
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
|
||||
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
|
||||
* Fix known issues.
|
||||
|
||||
## 3.0.0-dev.5
|
||||
|
||||
* Move `Advertisement` class to `AdvertiseData` class.
|
||||
|
||||
## 3.0.0-dev.4
|
||||
|
||||
* Fix issues.
|
||||
|
||||
## 3.0.0-dev.3
|
||||
|
||||
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
|
||||
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
|
||||
|
||||
## 3.0.0-dev.2
|
||||
|
||||
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
|
||||
* Add `CentralManager.instance` api.
|
||||
* Add `PeripheralManager.instance` api.
|
||||
|
||||
## 3.0.0-dev.1
|
||||
|
||||
* Add `PeripheralManager` api.
|
||||
* Add `CentralManager#readRSSI` method.
|
||||
* Move `CentralController` to `CentralManager`.
|
||||
* Move `CentralState` to `BluetoothLowEnergyState`.
|
||||
* Move `CentralDiscoveredEventArgs` to `DiscoveredEventArgs`.
|
||||
|
||||
## 2.2.0
|
||||
|
||||
* Add `GattCharacteristicWriteType` argument to `CentralController#getMaximumWriteLength` method.
|
||||
|
@ -1,12 +1,24 @@
|
||||
export 'src/errors.dart';
|
||||
export 'src/event_args.dart';
|
||||
export 'src/central_controller.dart';
|
||||
export 'src/central_state.dart';
|
||||
export 'src/bluetooth_low_energy.dart';
|
||||
export 'src/bluetooth_low_energy_manager.dart';
|
||||
export 'src/bluetooth_low_energy_state.dart';
|
||||
export 'src/central_manager.dart';
|
||||
export 'src/peripheral_manager.dart';
|
||||
export 'src/bluetooth_low_energy_peer.dart';
|
||||
export 'src/central.dart';
|
||||
export 'src/peripheral.dart';
|
||||
export 'src/uuid.dart';
|
||||
export 'src/advertisement.dart';
|
||||
export 'src/advertise_data.dart';
|
||||
export 'src/manufacturer_specific_data.dart';
|
||||
export 'src/gatt_service.dart';
|
||||
export 'src/gatt_characteristic.dart';
|
||||
export 'src/gatt_characteristic_property.dart';
|
||||
export 'src/gatt_characteristic_write_type.dart';
|
||||
export 'src/gatt_descriptor.dart';
|
||||
export 'src/my_object.dart';
|
||||
export 'src/my_peripheral.dart';
|
||||
export 'src/my_gatt_service.dart';
|
||||
export 'src/my_gatt_characteristic.dart';
|
||||
export 'src/my_gatt_descriptor.dart';
|
||||
export 'src/my_central.dart';
|
||||
|
@ -1,26 +1,27 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'manufacturer_specific_data.dart';
|
||||
import 'uuid.dart';
|
||||
|
||||
/// The advertisement discovered from a peripheral.
|
||||
class Advertisement {
|
||||
/// The advertise data discovered from a peripheral.
|
||||
class AdvertiseData {
|
||||
/// The name of the peripheral.
|
||||
final String? name;
|
||||
|
||||
/// The manufacturer specific data of the peripheral.
|
||||
final Map<int, Uint8List> manufacturerSpecificData;
|
||||
|
||||
/// The GATT service uuids of the peripheral.
|
||||
final List<UUID> serviceUUIDs;
|
||||
|
||||
/// The GATT service data of the peripheral.
|
||||
final Map<UUID, Uint8List> serviceData;
|
||||
|
||||
/// Constructs an [Advertisement].
|
||||
Advertisement({
|
||||
/// The manufacturer specific data of the peripheral.
|
||||
final ManufacturerSpecificData? manufacturerSpecificData;
|
||||
|
||||
/// Constructs an [AdvertiseData].
|
||||
AdvertiseData({
|
||||
this.name,
|
||||
this.manufacturerSpecificData = const {},
|
||||
this.serviceUUIDs = const [],
|
||||
this.serviceData = const {},
|
||||
this.manufacturerSpecificData,
|
||||
});
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
import 'central_manager.dart';
|
||||
import 'peripheral_manager.dart';
|
||||
|
||||
/// The bluetooth low energy interface.
|
||||
///
|
||||
/// Call `setUp` before use any api.
|
||||
abstract class BluetoothLowEnergy extends PlatformInterface {
|
||||
/// Constructs a [BluetoothLowEnergy].
|
||||
BluetoothLowEnergy() : super(token: _token);
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static BluetoothLowEnergy? _instance;
|
||||
|
||||
/// The default instance of [BluetoothLowEnergy] to use.
|
||||
static BluetoothLowEnergy get instance {
|
||||
final instance = _instance;
|
||||
if (instance == null) {
|
||||
throw UnimplementedError(
|
||||
'`BluetoothLowEnergy` is not implemented on this platform.',
|
||||
);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// Platform-specific implementations should set this with their own
|
||||
/// platform-specific class that extends [BluetoothLowEnergy] when
|
||||
/// they register themselves.
|
||||
static set instance(BluetoothLowEnergy instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
/// Gets the instance of central manager.
|
||||
CentralManager get centralManager;
|
||||
|
||||
/// Gets the instance of peripheral manager.
|
||||
PeripheralManager get peripheralManager;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import 'bluetooth_low_energy_state.dart';
|
||||
import 'event_args.dart';
|
||||
|
||||
/// The abstract base class that manages central and peripheral objects.
|
||||
abstract class BluetoothLowEnergyManager {
|
||||
/// The current state of the manager.
|
||||
BluetoothLowEnergyState get state;
|
||||
|
||||
/// Tells the manager’s state updated.
|
||||
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged;
|
||||
|
||||
/// Sets up this bluetooth low energy manager.
|
||||
Future<void> setUp();
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import 'uuid.dart';
|
||||
|
||||
/// An object that represents a remote device.
|
||||
abstract class BluetoothLowEnergyPeer {
|
||||
/// The UUID associated with the peer.
|
||||
UUID get uuid;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
/// The state of the bluetooth low energy.
|
||||
enum BluetoothLowEnergyState {
|
||||
/// The bluetooth low energy is unknown.
|
||||
unknown,
|
||||
|
||||
/// The bluetooth low energy is unsupported.
|
||||
unsupported,
|
||||
|
||||
/// The bluetooth low energy is unauthorized.
|
||||
unauthorized,
|
||||
|
||||
/// The bluetooth low energy is powered off.
|
||||
poweredOff,
|
||||
|
||||
/// The bluetooth low energy is powered on.
|
||||
poweredOn,
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
import 'bluetooth_low_energy_peer.dart';
|
||||
|
||||
/// A remote device connected to a local app, which is acting as a peripheral.
|
||||
abstract class Central extends BluetoothLowEnergyPeer {}
|
@ -1,120 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
import 'central_state.dart';
|
||||
import 'event_args.dart';
|
||||
import 'gatt_characteristic.dart';
|
||||
import 'gatt_characteristic_write_type.dart';
|
||||
import 'gatt_descriptor.dart';
|
||||
import 'gatt_service.dart';
|
||||
import 'peripheral.dart';
|
||||
|
||||
/// The central controller used to communicate with peripherals.
|
||||
/// Call `setUp` before use any api, and call `tearDown` when it is no longer needed.
|
||||
abstract class CentralController extends PlatformInterface {
|
||||
/// Constructs a [CentralController].
|
||||
CentralController() : super(token: _token);
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static CentralController? _instance;
|
||||
|
||||
/// The default instance of [CentralController] to use.
|
||||
static CentralController get instance {
|
||||
final instance = _instance;
|
||||
if (instance == null) {
|
||||
const message =
|
||||
'`BluetoothLowEnergy` is not implemented on this platform.';
|
||||
throw UnimplementedError(message);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// Platform-specific implementations should set this with their own
|
||||
/// platform-specific class that extends [CentralController] when
|
||||
/// they register themselves.
|
||||
static set instance(CentralController instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
/// Gets the state of the central.
|
||||
CentralState get state;
|
||||
|
||||
/// Used to listen the central state changed event.
|
||||
Stream<CentralStateChangedEventArgs> get stateChanged;
|
||||
|
||||
/// Used to listen the central discovered event.
|
||||
Stream<CentralDiscoveredEventArgs> get discovered;
|
||||
|
||||
/// Used to listen peripherals state changed event.
|
||||
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged;
|
||||
|
||||
/// Used to listen GATT characteristics value changed event.
|
||||
Stream<GattCharacteristicValueChangedEventArgs>
|
||||
get characteristicValueChanged;
|
||||
|
||||
/// Sets up the central controller.
|
||||
Future<void> setUp();
|
||||
|
||||
/// Tears down the central controller.
|
||||
Future<void> tearDown();
|
||||
|
||||
/// Starts to discover peripherals.
|
||||
Future<void> startDiscovery();
|
||||
|
||||
/// Stops to discover peripherals.
|
||||
Future<void> stopDiscovery();
|
||||
|
||||
/// Connects to the peripheral.
|
||||
Future<void> connect(Peripheral peripheral);
|
||||
|
||||
/// Disconnects form the peripheral.
|
||||
Future<void> disconnect(Peripheral peripheral);
|
||||
|
||||
/// Gets the max length in bytes for a single write type of the peripheral.
|
||||
Future<int> getMaximumWriteLength(
|
||||
Peripheral peripheral, {
|
||||
required GattCharacteristicWriteType type,
|
||||
});
|
||||
|
||||
/// Discovers GATT of the peripheral.
|
||||
Future<void> discoverGATT(Peripheral peripheral);
|
||||
|
||||
/// Gets GATT services of the peripheral.
|
||||
Future<List<GattService>> getServices(Peripheral peripheral);
|
||||
|
||||
/// Gets GATT characteristics of the GATT service.
|
||||
Future<List<GattCharacteristic>> getCharacteristics(GattService service);
|
||||
|
||||
/// Gets GATT descriptors of the GATT characteristic.
|
||||
Future<List<GattDescriptor>> getDescriptors(
|
||||
GattCharacteristic characteristic,
|
||||
);
|
||||
|
||||
/// Reads value of the GATT characteristic.
|
||||
Future<Uint8List> readCharacteristic(GattCharacteristic characteristic);
|
||||
|
||||
/// Writes value of the GATT characteristic.
|
||||
Future<void> writeCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
required Uint8List value,
|
||||
required GattCharacteristicWriteType type,
|
||||
});
|
||||
|
||||
/// Notifies value of the GATT characteristic.
|
||||
Future<void> notifyCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
required bool state,
|
||||
});
|
||||
|
||||
/// Reads value of the GATT descriptor.
|
||||
Future<Uint8List> readDescriptor(GattDescriptor descriptor);
|
||||
|
||||
/// Writes value of the GATT descriptor.
|
||||
Future<void> writeDescriptor(
|
||||
GattDescriptor descriptor, {
|
||||
required Uint8List value,
|
||||
});
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'bluetooth_low_energy.dart';
|
||||
import 'bluetooth_low_energy_manager.dart';
|
||||
import 'event_args.dart';
|
||||
import 'gatt_characteristic.dart';
|
||||
import 'gatt_characteristic_write_type.dart';
|
||||
import 'gatt_descriptor.dart';
|
||||
import 'gatt_service.dart';
|
||||
import 'peripheral.dart';
|
||||
|
||||
/// An object that scans for, discovers, connects to, and manages peripherals.
|
||||
abstract class CentralManager extends BluetoothLowEnergyManager {
|
||||
/// Gets the instance of [CentralManager].
|
||||
static CentralManager get instance =>
|
||||
BluetoothLowEnergy.instance.centralManager;
|
||||
|
||||
/// 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 characteristic’s value changed.
|
||||
Stream<GattCharacteristicValueChangedEventArgs>
|
||||
get characteristicValueChanged;
|
||||
|
||||
/// Scans for peripherals that are advertising services.
|
||||
Future<void> startDiscovery();
|
||||
|
||||
/// Asks the central manager to stop scanning for peripherals.
|
||||
Future<void> stopDiscovery();
|
||||
|
||||
/// Establishes a local connection to a peripheral.
|
||||
Future<void> connect(Peripheral peripheral);
|
||||
|
||||
/// 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);
|
||||
|
||||
/// Discovers the GATT services, characteristics and descriptors of the peripheral.
|
||||
Future<List<GattService>> discoverGATT(Peripheral peripheral);
|
||||
|
||||
/// Retrieves the value of a specified characteristic.
|
||||
Future<Uint8List> readCharacteristic(GattCharacteristic characteristic);
|
||||
|
||||
/// Writes the value of a characteristic.
|
||||
Future<void> writeCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
required Uint8List value,
|
||||
required GattCharacteristicWriteType type,
|
||||
});
|
||||
|
||||
/// Sets notifications or indications for the value of a specified characteristic.
|
||||
Future<void> notifyCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
required bool state,
|
||||
});
|
||||
|
||||
/// Retrieves the value of a specified characteristic descriptor.
|
||||
Future<Uint8List> readDescriptor(GattDescriptor descriptor);
|
||||
|
||||
/// Writes the value of a characteristic descriptor.
|
||||
Future<void> writeDescriptor(
|
||||
GattDescriptor descriptor, {
|
||||
required Uint8List value,
|
||||
});
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
/// The state of the central.
|
||||
enum CentralState {
|
||||
/// The central is unknown.
|
||||
unknown,
|
||||
|
||||
/// The central is unsupported.
|
||||
unsupported,
|
||||
|
||||
/// The central is unauthorized.
|
||||
unauthorized,
|
||||
|
||||
/// The central is powered off.
|
||||
poweredOff,
|
||||
|
||||
/// The central is powered on.
|
||||
poweredOn,
|
||||
}
|
@ -1,35 +1,36 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'advertisement.dart';
|
||||
import 'central_state.dart';
|
||||
import 'advertise_data.dart';
|
||||
import 'bluetooth_low_energy_state.dart';
|
||||
import 'central.dart';
|
||||
import 'gatt_characteristic.dart';
|
||||
import 'peripheral.dart';
|
||||
|
||||
/// The base event arguments.
|
||||
abstract class EventArgs {}
|
||||
|
||||
/// The central state changed event arguments.
|
||||
class CentralStateChangedEventArgs extends EventArgs {
|
||||
/// The new state of the central.
|
||||
final CentralState state;
|
||||
/// The bluetooth low energy state changed event arguments.
|
||||
class BluetoothLowEnergyStateChangedEventArgs extends EventArgs {
|
||||
/// The new state of the bluetooth low energy.
|
||||
final BluetoothLowEnergyState state;
|
||||
|
||||
/// Constructs a [CentralStateChangedEventArgs].
|
||||
CentralStateChangedEventArgs(this.state);
|
||||
/// Constructs a [BluetoothLowEnergyStateChangedEventArgs].
|
||||
BluetoothLowEnergyStateChangedEventArgs(this.state);
|
||||
}
|
||||
|
||||
/// The central discovered event arguments.
|
||||
class CentralDiscoveredEventArgs extends EventArgs {
|
||||
/// 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;
|
||||
/// The advertise data of the peripheral.
|
||||
final AdvertiseData advertiseData;
|
||||
|
||||
/// Constructs a [CentralDiscoveredEventArgs].
|
||||
CentralDiscoveredEventArgs(this.peripheral, this.rssi, this.advertisement);
|
||||
/// Constructs a [DiscoveredEventArgs].
|
||||
DiscoveredEventArgs(this.peripheral, this.rssi, this.advertiseData);
|
||||
}
|
||||
|
||||
/// The peripheral state changed event arguments.
|
||||
@ -55,3 +56,45 @@ class GattCharacteristicValueChangedEventArgs extends EventArgs {
|
||||
/// Constructs a [GattCharacteristicValueChangedEventArgs].
|
||||
GattCharacteristicValueChangedEventArgs(this.characteristic, this.value);
|
||||
}
|
||||
|
||||
class ReadGattCharacteristicCommandEventArgs {
|
||||
final Central central;
|
||||
final GattCharacteristic characteristic;
|
||||
final int id;
|
||||
final int offset;
|
||||
|
||||
ReadGattCharacteristicCommandEventArgs(
|
||||
this.central,
|
||||
this.characteristic,
|
||||
this.id,
|
||||
this.offset,
|
||||
);
|
||||
}
|
||||
|
||||
class WriteGattCharacteristicCommandEventArgs {
|
||||
final Central central;
|
||||
final GattCharacteristic characteristic;
|
||||
final int id;
|
||||
final int offset;
|
||||
final Uint8List value;
|
||||
|
||||
WriteGattCharacteristicCommandEventArgs(
|
||||
this.central,
|
||||
this.characteristic,
|
||||
this.id,
|
||||
this.offset,
|
||||
this.value,
|
||||
);
|
||||
}
|
||||
|
||||
class NotifyGattCharacteristicCommandEventArgs {
|
||||
final Central central;
|
||||
final GattCharacteristic characteristic;
|
||||
final bool state;
|
||||
|
||||
NotifyGattCharacteristicCommandEventArgs(
|
||||
this.central,
|
||||
this.characteristic,
|
||||
this.state,
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
import 'uuid.dart';
|
||||
|
||||
/// A representation of common aspects of services offered by a peripheral.
|
||||
abstract class GattAttribute {
|
||||
/// The Bluetooth-specific UUID of the attribute.
|
||||
UUID get uuid;
|
||||
}
|
@ -1,17 +1,27 @@
|
||||
import 'gatt_attribute.dart';
|
||||
import 'gatt_characteristic_property.dart';
|
||||
import 'gatt_descriptor.dart';
|
||||
import 'my_gatt_characteristic.dart';
|
||||
import 'my_gatt_descriptor.dart';
|
||||
import 'uuid.dart';
|
||||
|
||||
/// The GATT characteristic.
|
||||
class GattCharacteristic {
|
||||
/// The [UUID] of this GATT characteristic.
|
||||
final UUID uuid;
|
||||
/// A characteristic of a remote peripheral’s service.
|
||||
abstract class GattCharacteristic extends GattAttribute {
|
||||
/// The properties of the characteristic.
|
||||
List<GattCharacteristicProperty> get properties;
|
||||
|
||||
/// The properties of this GATT characteristic.
|
||||
final List<GattCharacteristicProperty> properties;
|
||||
/// A list of the descriptors discovered in this characteristic.
|
||||
List<GattDescriptor> get descriptors;
|
||||
|
||||
/// Constructs a [GattCharacteristic].
|
||||
GattCharacteristic({
|
||||
required this.uuid,
|
||||
required this.properties,
|
||||
});
|
||||
factory GattCharacteristic({
|
||||
required UUID uuid,
|
||||
required List<GattCharacteristicProperty> properties,
|
||||
required List<GattDescriptor> descriptors,
|
||||
}) =>
|
||||
MyGattCharacteristic(
|
||||
uuid: uuid,
|
||||
properties: properties,
|
||||
descriptors: descriptors.cast<MyGattDescriptor>(),
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,18 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'gatt_attribute.dart';
|
||||
import 'my_gatt_descriptor.dart';
|
||||
import 'uuid.dart';
|
||||
|
||||
/// The GATT characteristic.
|
||||
class GattDescriptor {
|
||||
/// The [UUID] of this GATT descriptor.
|
||||
final UUID uuid;
|
||||
|
||||
/// An object that provides further information about a remote peripheral’s characteristic.
|
||||
abstract class GattDescriptor extends GattAttribute {
|
||||
/// Constructs a [GattDescriptor].
|
||||
GattDescriptor({
|
||||
required this.uuid,
|
||||
});
|
||||
factory GattDescriptor({
|
||||
required UUID uuid,
|
||||
required Uint8List value,
|
||||
}) =>
|
||||
MyGattDescriptor(
|
||||
uuid: uuid,
|
||||
value: value,
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,21 @@
|
||||
import 'gatt_attribute.dart';
|
||||
import 'gatt_characteristic.dart';
|
||||
import 'my_gatt_characteristic.dart';
|
||||
import 'my_gatt_service.dart';
|
||||
import 'uuid.dart';
|
||||
|
||||
/// The GATT service.
|
||||
class GattService {
|
||||
/// The [UUID] of this GATT service.
|
||||
final UUID uuid;
|
||||
/// A collection of data and associated behaviors that accomplish a function or feature of a device.
|
||||
abstract class GattService extends GattAttribute {
|
||||
/// A list of characteristics discovered in this service.
|
||||
List<GattCharacteristic> get characteristics;
|
||||
|
||||
/// Constructs a [GattService].
|
||||
GattService({
|
||||
required this.uuid,
|
||||
});
|
||||
factory GattService({
|
||||
required UUID uuid,
|
||||
required List<GattCharacteristic> characteristics,
|
||||
}) =>
|
||||
MyGattService(
|
||||
uuid: uuid,
|
||||
characteristics: characteristics.cast<MyGattCharacteristic>(),
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
/// The manufacturer specific data of the peripheral
|
||||
class ManufacturerSpecificData {
|
||||
/// The manufacturer id.
|
||||
final int id;
|
||||
|
||||
/// The manufacturer data.
|
||||
final Uint8List data;
|
||||
|
||||
/// Constructs an [ManufacturerSpecificData].
|
||||
ManufacturerSpecificData({
|
||||
required this.id,
|
||||
required this.data,
|
||||
});
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import 'central.dart';
|
||||
import 'my_object.dart';
|
||||
import 'uuid.dart';
|
||||
|
||||
class MyCentral extends MyObject implements Central {
|
||||
@override
|
||||
final UUID uuid;
|
||||
|
||||
MyCentral({
|
||||
required super.hashCode,
|
||||
required this.uuid,
|
||||
});
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user