* 修复 UUID 创建失败的问题 * 移除 scanning 属性 * 临时提交 * CentralManager 开发 & 示例项目开发 * CentralManager 开发 & 示例项目开发 * android 插件生命周期监听 * 修改 API * 示例程序开发 * 修改字体,添加 API,解决后台问题 * Central#connect API * 蓝牙连接部分开发 * 蓝牙连接部分开发 * 解决一些问题 * 解决一些问题 * Connect API 优化 * 添加 API * example 开发 * API 基本完成 * 消息重命名 * API 修改,Android 实现 * 删除多余代码 * 删除多余文件 * 解决 descriptor 自动生成报错的问题 * 还原 Kotlin 版本,广播处理代码迁移至 dart 端 * Kotlin 版本升至 1.5.20 * 解决特征值通知没有在主线程触发的问题,优化代码 * 引入哈希值,避免对象销毁后继续使用 * 使用下拉刷新代替搜索按钮 * 解决由于热重载和蓝牙关闭产生的问题 * 更新插件信息 * 更新 README 和 CHANGELOG * 更新许可证 * 添加注释 * 添加注释,central 拆分
472 lines
15 KiB
Dart
472 lines
15 KiB
Dart
import 'package:bluetooth_low_energy/bluetooth_low_energy.dart';
|
|
import 'package:bluetooth_low_energy/src/util.dart' as util;
|
|
import 'package:bluetooth_low_energy/src/message.pb.dart' as proto;
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
final method = MethodChannel('${util.method.name}');
|
|
final event = MethodChannel('${util.event.name}');
|
|
final calls = <MethodCall>[];
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
setUp(() {
|
|
calls.clear();
|
|
method.setMockMethodCallHandler((call) async {
|
|
calls.add(call);
|
|
if (call.method == proto.MessageCategory.BLUETOOTH_AVAILABLE.name) {
|
|
return true;
|
|
} else if (call.method == proto.MessageCategory.BLUETOOTH_STATE.name) {
|
|
return true;
|
|
} else if (call.method ==
|
|
proto.MessageCategory.CENTRAL_START_DISCOVERY.name) {
|
|
return null;
|
|
} else if (call.method ==
|
|
proto.MessageCategory.CENTRAL_STOP_DISCOVERY.name) {
|
|
return null;
|
|
} else if (call.method == proto.MessageCategory.CENTRAL_CONNECT.name) {
|
|
final descriptors = [
|
|
proto.GattDescriptor(
|
|
id: 4000,
|
|
uuid: '2900',
|
|
),
|
|
];
|
|
final characteristics = [
|
|
proto.GattCharacteristic(
|
|
id: 3000,
|
|
uuid: '2A00',
|
|
descriptors: descriptors,
|
|
canRead: true,
|
|
canWrite: true,
|
|
canWriteWithoutResponse: true,
|
|
canNotify: true,
|
|
),
|
|
];
|
|
final services = [
|
|
proto.GattService(
|
|
id: 2000,
|
|
uuid: '1800',
|
|
characteristics: characteristics,
|
|
),
|
|
];
|
|
final gatt = proto.GATT(
|
|
id: 1000,
|
|
mtu: 23,
|
|
services: services,
|
|
);
|
|
return gatt.writeToBuffer();
|
|
} else if (call.method == proto.MessageCategory.GATT_DISCONNECT.name) {
|
|
return null;
|
|
} else if (call.method ==
|
|
proto.MessageCategory.GATT_CHARACTERISTIC_READ.name) {
|
|
return [0x01, 0x02, 0x03, 0x04, 0x05];
|
|
} else if (call.method ==
|
|
proto.MessageCategory.GATT_CHARACTERISTIC_WRITE.name) {
|
|
return null;
|
|
} else if (call.method ==
|
|
proto.MessageCategory.GATT_CHARACTERISTIC_NOTIFY.name) {
|
|
return null;
|
|
} else if (call.method ==
|
|
proto.MessageCategory.GATT_DESCRIPTOR_READ.name) {
|
|
return [0x05, 0x04, 0x03, 0x02, 0x01];
|
|
} else if (call.method ==
|
|
proto.MessageCategory.GATT_DESCRIPTOR_WRITE.name) {
|
|
return null;
|
|
} else {
|
|
throw UnimplementedError();
|
|
}
|
|
});
|
|
event.setMockMethodCallHandler((call) async {
|
|
switch (call.method) {
|
|
case 'listen':
|
|
final state = proto.Message(
|
|
category: proto.MessageCategory.BLUETOOTH_STATE,
|
|
state: false,
|
|
).writeToBuffer();
|
|
await ServicesBinding.instance!.defaultBinaryMessenger
|
|
.handlePlatformMessage(
|
|
event.name,
|
|
event.codec.encodeSuccessEnvelope(state),
|
|
(data) {},
|
|
);
|
|
final discovery = proto.Message(
|
|
category: proto.MessageCategory.CENTRAL_DISCOVERED,
|
|
discovery: proto.Discovery(
|
|
address: 'aa:bb:cc:dd:ee:ff',
|
|
rssi: -50,
|
|
advertisements: [0x07, 0xff, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa],
|
|
),
|
|
).writeToBuffer();
|
|
await ServicesBinding.instance!.defaultBinaryMessenger
|
|
.handlePlatformMessage(
|
|
event.name,
|
|
event.codec.encodeSuccessEnvelope(discovery),
|
|
(data) {},
|
|
);
|
|
final connectionLost = proto.Message(
|
|
category: proto.MessageCategory.GATT_CONNECTION_LOST,
|
|
connectionLost: proto.ConnectionLost(
|
|
id: 1000,
|
|
errorCode: 19,
|
|
),
|
|
).writeToBuffer();
|
|
await ServicesBinding.instance!.defaultBinaryMessenger
|
|
.handlePlatformMessage(
|
|
event.name,
|
|
event.codec.encodeSuccessEnvelope(connectionLost),
|
|
(data) {},
|
|
);
|
|
final characteristicValue = proto.Message(
|
|
category: proto.MessageCategory.GATT_CHARACTERISTIC_NOTIFY,
|
|
characteristicValue: proto.GattCharacteristicValue(
|
|
id: 3000,
|
|
value: [0x0A, 0x0B, 0x0C, 0x0D, 0x0E],
|
|
),
|
|
).writeToBuffer();
|
|
await ServicesBinding.instance!.defaultBinaryMessenger
|
|
.handlePlatformMessage(
|
|
event.name,
|
|
event.codec.encodeSuccessEnvelope(characteristicValue),
|
|
(data) {},
|
|
);
|
|
break;
|
|
case 'cancel':
|
|
return null;
|
|
default:
|
|
throw UnimplementedError();
|
|
}
|
|
});
|
|
});
|
|
tearDown(() {
|
|
method.setMockMethodCallHandler(null);
|
|
event.setMockMethodCallHandler(null);
|
|
});
|
|
|
|
test(
|
|
'${proto.MessageCategory.BLUETOOTH_AVAILABLE}',
|
|
() async {
|
|
final actual = await central.available;
|
|
expect(actual, true);
|
|
expect(
|
|
calls,
|
|
[
|
|
isMethodCall(
|
|
proto.MessageCategory.BLUETOOTH_AVAILABLE.name,
|
|
arguments: null,
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.BLUETOOTH_STATE}',
|
|
() async {
|
|
final actual = await central.state;
|
|
expect(actual, true);
|
|
expect(
|
|
calls,
|
|
[
|
|
isMethodCall(
|
|
proto.MessageCategory.BLUETOOTH_STATE.name,
|
|
arguments: null,
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.BLUETOOTH_STATE} EVENT',
|
|
() async {
|
|
final actual = await central.stateChanged.first;
|
|
expect(actual, false);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.CENTRAL_START_DISCOVERY}',
|
|
() async {
|
|
final services = [UUID('1800'), UUID('1801')];
|
|
await central.startDiscovery(services: services);
|
|
expect(
|
|
calls,
|
|
[
|
|
isMethodCall(
|
|
proto.MessageCategory.CENTRAL_START_DISCOVERY.name,
|
|
arguments: proto.StartDiscoveryArguments(
|
|
services: services.map((uuid) => uuid.name),
|
|
).writeToBuffer(),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.CENTRAL_STOP_DISCOVERY}',
|
|
() async {
|
|
await central.stopDiscovery();
|
|
expect(
|
|
calls,
|
|
[
|
|
isMethodCall(
|
|
proto.MessageCategory.CENTRAL_STOP_DISCOVERY.name,
|
|
arguments: null,
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.CENTRAL_DISCOVERED} EVENT',
|
|
() async {
|
|
final actual = await central.discovered.first;
|
|
final address = MAC('aa:bb:cc:dd:ee:ff');
|
|
expect(actual.address, address);
|
|
final rssi = -50;
|
|
expect(actual.rssi, rssi);
|
|
final advertisements = {
|
|
0xff: [0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa],
|
|
};
|
|
expect(actual.advertisements, advertisements);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.CENTRAL_CONNECT}',
|
|
() async {
|
|
final address = MAC('aa:bb:cc:dd:ee:ff');
|
|
final actual = await central.connect(address);
|
|
expect(actual.mtu, 23);
|
|
expect(actual.services.length, 1);
|
|
final service = actual.services.values.first;
|
|
expect(service.uuid, UUID('1800'));
|
|
expect(service.characteristics.length, 1);
|
|
final characteristic = service.characteristics.values.first;
|
|
expect(characteristic.uuid, UUID('2A00'));
|
|
expect(characteristic.descriptors.length, 1);
|
|
expect(characteristic.canRead, true);
|
|
expect(characteristic.canWrite, true);
|
|
expect(characteristic.canWriteWithoutResponse, true);
|
|
expect(characteristic.canNotify, true);
|
|
final descriptor = characteristic.descriptors.values.first;
|
|
expect(descriptor.uuid, UUID('2900'));
|
|
expect(
|
|
calls,
|
|
[
|
|
isMethodCall(
|
|
proto.MessageCategory.CENTRAL_CONNECT.name,
|
|
arguments: proto.ConnectArguments(
|
|
address: address.name,
|
|
).writeToBuffer(),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.GATT_DISCONNECT}',
|
|
() async {
|
|
final address = MAC('aa:bb:cc:dd:ee:ff');
|
|
final gatt = await central.connect(address);
|
|
await gatt.disconnect();
|
|
expect(
|
|
calls,
|
|
[
|
|
isMethodCall(
|
|
proto.MessageCategory.CENTRAL_CONNECT.name,
|
|
arguments: proto.ConnectArguments(
|
|
address: address.name,
|
|
).writeToBuffer(),
|
|
),
|
|
isMethodCall(
|
|
proto.MessageCategory.GATT_DISCONNECT.name,
|
|
arguments: proto.GattDisconnectArguments(
|
|
address: address.name,
|
|
id: 1000,
|
|
).writeToBuffer(),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.GATT_CONNECTION_LOST}',
|
|
() async {
|
|
final address = MAC('aa:bb:cc:dd:ee:ff');
|
|
final gatt = await central.connect(address);
|
|
final actual = await gatt.connectionLost.first;
|
|
expect(actual, 19);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.GATT_CHARACTERISTIC_READ}',
|
|
() async {
|
|
final address = MAC('aa:bb:cc:dd:ee:ff');
|
|
final gatt = await central.connect(address);
|
|
final service = gatt.services.values.first;
|
|
final characteristic = service.characteristics.values.first;
|
|
final actual = await characteristic.read();
|
|
expect(actual, [0x01, 0x02, 0x03, 0x04, 0x05]);
|
|
expect(
|
|
calls,
|
|
[
|
|
isMethodCall(
|
|
proto.MessageCategory.CENTRAL_CONNECT.name,
|
|
arguments: proto.ConnectArguments(
|
|
address: address.name,
|
|
).writeToBuffer(),
|
|
),
|
|
isMethodCall(
|
|
proto.MessageCategory.GATT_CHARACTERISTIC_READ.name,
|
|
arguments: proto.GattCharacteristicReadArguments(
|
|
address: address.name,
|
|
serviceUuid: service.uuid.name,
|
|
uuid: characteristic.uuid.name,
|
|
id: 3000,
|
|
).writeToBuffer(),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.GATT_CHARACTERISTIC_WRITE}',
|
|
() async {
|
|
final address = MAC('aa:bb:cc:dd:ee:ff');
|
|
final gatt = await central.connect(address);
|
|
final service = gatt.services.values.first;
|
|
final characteristic = service.characteristics.values.first;
|
|
final value = [0x01, 0x02, 0x03, 0x04, 0x05];
|
|
await characteristic.write(value, withoutResponse: true);
|
|
expect(
|
|
calls,
|
|
[
|
|
isMethodCall(
|
|
proto.MessageCategory.CENTRAL_CONNECT.name,
|
|
arguments: proto.ConnectArguments(
|
|
address: address.name,
|
|
).writeToBuffer(),
|
|
),
|
|
isMethodCall(
|
|
proto.MessageCategory.GATT_CHARACTERISTIC_WRITE.name,
|
|
arguments: proto.GattCharacteristicWriteArguments(
|
|
address: address.name,
|
|
serviceUuid: service.uuid.name,
|
|
uuid: characteristic.uuid.name,
|
|
id: 3000,
|
|
value: value,
|
|
withoutResponse: true,
|
|
).writeToBuffer(),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.GATT_CHARACTERISTIC_NOTIFY}',
|
|
() async {
|
|
final address = MAC('aa:bb:cc:dd:ee:ff');
|
|
final gatt = await central.connect(address);
|
|
final service = gatt.services.values.first;
|
|
final characteristic = service.characteristics.values.first;
|
|
await characteristic.notify(true);
|
|
expect(
|
|
calls,
|
|
[
|
|
isMethodCall(
|
|
proto.MessageCategory.CENTRAL_CONNECT.name,
|
|
arguments: proto.ConnectArguments(
|
|
address: address.name,
|
|
).writeToBuffer(),
|
|
),
|
|
isMethodCall(
|
|
proto.MessageCategory.GATT_CHARACTERISTIC_NOTIFY.name,
|
|
arguments: proto.GattCharacteristicNotifyArguments(
|
|
address: address.name,
|
|
serviceUuid: service.uuid.name,
|
|
uuid: characteristic.uuid.name,
|
|
id: 3000,
|
|
state: true,
|
|
).writeToBuffer(),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.GATT_CHARACTERISTIC_NOTIFY} EVENT',
|
|
() async {
|
|
final address = MAC('aa:bb:cc:dd:ee:ff');
|
|
final gatt = await central.connect(address);
|
|
final service = gatt.services.values.first;
|
|
final characteristic = service.characteristics.values.first;
|
|
final value = await characteristic.valueChanged.first;
|
|
expect(value, [0x0A, 0x0B, 0x0C, 0x0D, 0x0E]);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.GATT_DESCRIPTOR_READ}',
|
|
() async {
|
|
final address = MAC('aa:bb:cc:dd:ee:ff');
|
|
final gatt = await central.connect(address);
|
|
final service = gatt.services.values.first;
|
|
final characteristic = service.characteristics.values.first;
|
|
final descriptor = characteristic.descriptors.values.first;
|
|
final actual = await descriptor.read();
|
|
expect(actual, [0x05, 0x04, 0x03, 0x02, 0x01]);
|
|
expect(
|
|
calls,
|
|
[
|
|
isMethodCall(
|
|
proto.MessageCategory.CENTRAL_CONNECT.name,
|
|
arguments: proto.ConnectArguments(
|
|
address: address.name,
|
|
).writeToBuffer(),
|
|
),
|
|
isMethodCall(
|
|
proto.MessageCategory.GATT_DESCRIPTOR_READ.name,
|
|
arguments: proto.GattDescriptorReadArguments(
|
|
address: address.name,
|
|
serviceUuid: service.uuid.name,
|
|
characteristicUuid: characteristic.uuid.name,
|
|
uuid: descriptor.uuid.name,
|
|
id: 4000,
|
|
).writeToBuffer(),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
test(
|
|
'${proto.MessageCategory.GATT_DESCRIPTOR_WRITE}',
|
|
() async {
|
|
final address = MAC('aa:bb:cc:dd:ee:ff');
|
|
final gatt = await central.connect(address);
|
|
final service = gatt.services.values.first;
|
|
final characteristic = service.characteristics.values.first;
|
|
final descriptor = characteristic.descriptors.values.first;
|
|
final value = [0x01, 0x02, 0x03, 0x04, 0x05];
|
|
await descriptor.write(value);
|
|
expect(
|
|
calls,
|
|
[
|
|
isMethodCall(
|
|
proto.MessageCategory.CENTRAL_CONNECT.name,
|
|
arguments: proto.ConnectArguments(
|
|
address: address.name,
|
|
).writeToBuffer(),
|
|
),
|
|
isMethodCall(
|
|
proto.MessageCategory.GATT_DESCRIPTOR_WRITE.name,
|
|
arguments: proto.GattDescriptorWriteArguments(
|
|
address: address.name,
|
|
serviceUuid: service.uuid.name,
|
|
characteristicUuid: characteristic.uuid.name,
|
|
uuid: descriptor.uuid.name,
|
|
id: 4000,
|
|
value: value,
|
|
).writeToBuffer(),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|