* fix: 优化界面

* fix: 修复已知问题

* fix: 代码优化

* fix: 修复已知问题

* fix: 优化 getMaximumWriteLength 方法

* fix: 修改版本号

* fix: 修改版本号

* fix: 修改 CHANGELOG
This commit is contained in:
iAMD
2023-10-18 17:26:24 +08:00
committed by GitHub
parent 4bec1c9f3a
commit 728402ac16
26 changed files with 872 additions and 413 deletions

View File

@ -1,3 +1,8 @@
## 3.0.2
* Fix the issue that `getMaximumWriteLength` is wrong and coerce the value from 20 to 512.
* Fix the issue that the peripheral manager response is wrong.
## 3.0.1
* Fix the issue that write characteristic will never complete when write without response.

View File

@ -274,6 +274,21 @@ extension MyGattDescriptorArgs {
}
}
extension Int {
func coerceIn(_ minimum: Int, _ maximum: Int) throws -> Int {
if minimum > maximum {
throw MyError.illegalArgument
}
if self < minimum {
return minimum
}
if self > maximum {
return maximum
}
return self
}
}
extension Dictionary {
mutating func getOrPut(_ key: Key, _ defaultValue: () -> Value) -> Value {
guard let value = self[key] else {

View File

@ -154,7 +154,7 @@ class MyCentralManager: MyCentralManagerHostApi {
throw MyError.illegalArgument
}
let type = typeArgs.toWriteType()
let maximumWriteLength = peripheral.maximumWriteValueLength(for: type)
let maximumWriteLength = try peripheral.maximumWriteValueLength(for: type).coerceIn(20, 512)
let maximumWriteLengthArgs = Int64(maximumWriteLength)
return maximumWriteLengthArgs
}
@ -310,7 +310,6 @@ class MyCentralManager: MyCentralManagerHostApi {
api.onStateChanged(stateNumberArgs: stateNumberArgs) {}
}
func didDiscover(_ peripheral: CBPeripheral, _ advertisementData: [String : Any], _ rssi: NSNumber) {
let peripheralArgs = peripheral.toArgs()
let peripheralHashCode = peripheral.hash
@ -329,10 +328,12 @@ class MyCentralManager: MyCentralManagerHostApi {
return
}
let peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
let completion = connectCompletions.removeValue(forKey: peripheralHashCodeArgs)
completion?(.success(()))
let stateArgs = true
api.onPeripheralStateChanged(peripheralArgs: peripheralArgs, stateArgs: stateArgs) {}
guard let completion = connectCompletions.removeValue(forKey: peripheralHashCodeArgs) else {
return
}
completion(.success(()))
}
func didFailToConnect(_ peripheral: CBPeripheral, _ error: Error?) {

View File

@ -7,6 +7,7 @@
import Foundation
// TODO:
enum MyError: Error {
case illegalArgument
case illegalState

View File

@ -202,13 +202,13 @@ class MyPeripheralManager: MyPeripheralManagerHostApi {
guard let central = centrals[centralHashCodeArgs] else {
throw MyError.illegalArgument
}
let maximumWriteLength = central.maximumUpdateValueLength
let maximumWriteLength = try central.maximumUpdateValueLength.coerceIn(20, 512)
let maximumWriteLengthArgs = Int64(maximumWriteLength)
return maximumWriteLengthArgs
}
func sendReadCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool, valueArgs: FlutterStandardTypedData) throws {
guard let request = requests[idArgs] else {
guard let request = requests.removeValue(forKey: idArgs) else {
throw MyError.illegalArgument
}
request.value = valueArgs.data
@ -217,7 +217,7 @@ class MyPeripheralManager: MyPeripheralManagerHostApi {
}
func sendWriteCharacteristicReply(centralHashCodeArgs: Int64, characteristicHashCodeArgs: Int64, idArgs: Int64, offsetArgs: Int64, statusArgs: Bool) throws {
guard let request = requests[idArgs] else {
guard let request = requests.removeValue(forKey: idArgs) else {
throw MyError.illegalArgument
}
let result = statusArgs ? CBATTError.Code.success : CBATTError.Code.requestNotSupported
@ -315,28 +315,35 @@ class MyPeripheralManager: MyPeripheralManagerHostApi {
}
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) {}
//
guard let request = requests.first else {
return
}
if requests.count > 1 {
// TODO:
let result = CBATTError.requestNotSupported
peripheralManager.respond(to: request, withResult: result)
}
let central = request.central
let centralHashCode = central.hash
let centralArgs = centralsArgs.getOrPut(centralHashCode) { central.toArgs() }
let centralHashCodeArgs = centralArgs.hashCodeArgs
centrals[centralHashCodeArgs] = central
let characteristic = request.characteristic
let characteristicHashCode = characteristic.hash
guard let characteristicArgs = characteristicsArgs[characteristicHashCode] else {
peripheralManager.respond(to: request, withResult: .attributeNotFound)
return
}
let idArgs = Int64(request.hash)
self.requests[idArgs] = request
let offsetArgs = Int64(request.offset)
guard let value = request.value else {
peripheralManager.respond(to: request, withResult: .requestNotSupported)
return
}
let valueArgs = FlutterStandardTypedData(bytes: value)
api.onWriteCharacteristicCommandReceived(centralArgs: centralArgs, characteristicArgs: characteristicArgs, idArgs: idArgs, offsetArgs: offsetArgs, valueArgs: valueArgs) {}
}
func didSubscribeTo(_ central: CBCentral, _ characteristic: CBCharacteristic) {

View File

@ -329,7 +329,7 @@ class _ScannerViewState extends State<ScannerView> {
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: Text('$rssi'),
trailing: RssiWidget(rssi),
);
},
separatorBuilder: (context, i) {
@ -375,10 +375,13 @@ class _PeripheralViewState extends State<PeripheralView> {
late final ValueNotifier<GattCharacteristic?> characteristic;
late final ValueNotifier<GattCharacteristicWriteType> writeType;
late final ValueNotifier<int> maximumWriteLength;
late final ValueNotifier<int> rssi;
late final ValueNotifier<List<Log>> logs;
late final TextEditingController writeController;
late final StreamSubscription stateChangedSubscription;
late final StreamSubscription valueChangedSubscription;
late final StreamSubscription rssiChangedSubscription;
late final Timer rssiTimer;
@override
void initState() {
@ -390,7 +393,8 @@ class _PeripheralViewState extends State<PeripheralView> {
service = ValueNotifier(null);
characteristic = ValueNotifier(null);
writeType = ValueNotifier(GattCharacteristicWriteType.withResponse);
maximumWriteLength = ValueNotifier(20);
maximumWriteLength = ValueNotifier(0);
rssi = ValueNotifier(-100);
logs = ValueNotifier([]);
writeController = TextEditingController();
stateChangedSubscription = centralManager.peripheralStateChanged.listen(
@ -423,6 +427,17 @@ class _PeripheralViewState extends State<PeripheralView> {
];
},
);
rssiTimer = Timer.periodic(
const Duration(seconds: 1),
(timer) async {
final state = this.state.value;
if (state) {
rssi.value = await centralManager.readRSSI(eventArgs.peripheral);
} else {
rssi.value = -100;
}
},
);
}
@override
@ -455,10 +470,18 @@ class _PeripheralViewState extends State<PeripheralView> {
final peripheral = eventArgs.peripheral;
if (state) {
await centralManager.disconnect(peripheral);
maximumWriteLength.value = 0;
rssi.value = 0;
} else {
await centralManager.connect(peripheral);
services.value =
await centralManager.discoverGATT(peripheral);
maximumWriteLength.value =
await centralManager.getMaximumWriteLength(
peripheral,
type: writeType.value,
);
rssi.value = await centralManager.readRSSI(peripheral);
}
},
child: Text(state ? 'DISCONNECT' : 'CONNECT'),
@ -592,72 +615,82 @@ class _PeripheralViewState extends State<PeripheralView> {
),
Row(
children: [
Expanded(
child: Center(
child: ValueListenableBuilder(
valueListenable: writeType,
builder: (context, writeType, child) {
final items =
GattCharacteristicWriteType.values.map((type) {
return DropdownMenuItem(
value: type,
child: Text(
type.name,
style: theme.textTheme.bodyMedium,
),
);
}).toList();
return DropdownButton(
items: items,
onChanged: (type) {
if (type == null) {
return;
}
this.writeType.value = type;
},
value: writeType,
underline: const Offstage(),
ValueListenableBuilder(
valueListenable: writeType,
builder: (context, writeType, child) {
return ToggleButtons(
onPressed: (i) async {
final type = GattCharacteristicWriteType.values[i];
this.writeType.value = type;
maximumWriteLength.value =
await centralManager.getMaximumWriteLength(
eventArgs.peripheral,
type: type,
);
},
),
),
),
Expanded(
child: ValueListenableBuilder(
valueListenable: state,
builder: (context, state, child) {
return TextButton(
onPressed: state
? () async {
maximumWriteLength.value =
await centralManager.getMaximumWriteLength(
eventArgs.peripheral,
type: writeType.value,
);
}
: null,
child: ValueListenableBuilder(
valueListenable: maximumWriteLength,
builder: (context, maximumWriteLength, child) {
return Text('MTU: $maximumWriteLength');
},
),
);
},
),
),
IconButton(
onPressed: () async {
final rssi =
await centralManager.readRSSI(eventArgs.peripheral);
log('RSSI: $rssi');
constraints: const BoxConstraints(
minWidth: 0.0,
minHeight: 0.0,
),
borderRadius: BorderRadius.circular(4.0),
isSelected: GattCharacteristicWriteType.values
.map((type) => type == writeType)
.toList(),
children: GattCharacteristicWriteType.values.map((type) {
return Container(
margin: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 4.0,
),
child: Text(type.name),
);
}).toList(),
);
// final segments =
// GattCharacteristicWriteType.values.map((type) {
// return ButtonSegment(
// value: type,
// label: Text(type.name),
// );
// }).toList();
// return SegmentedButton(
// segments: segments,
// selected: {writeType},
// showSelectedIcon: false,
// style: OutlinedButton.styleFrom(
// tapTargetSize: MaterialTapTargetSize.shrinkWrap,
// padding: EdgeInsets.zero,
// visualDensity: VisualDensity.compact,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(8.0),
// ),
// ),
// );
},
),
const SizedBox(width: 8.0),
ValueListenableBuilder(
valueListenable: state,
builder: (context, state, child) {
return ValueListenableBuilder(
valueListenable: maximumWriteLength,
builder: (context, maximumWriteLength, child) {
return Text('$maximumWriteLength');
},
);
},
),
const Spacer(),
ValueListenableBuilder(
valueListenable: rssi,
builder: (context, rssi, child) {
return RssiWidget(rssi);
},
icon: const Icon(Icons.signal_wifi_4_bar),
),
],
),
Container(
margin: const EdgeInsets.symmetric(vertical: 16.0),
margin: const EdgeInsets.only(bottom: 16.0),
height: 160.0,
child: ValueListenableBuilder(
valueListenable: characteristic,
@ -755,6 +788,7 @@ class _PeripheralViewState extends State<PeripheralView> {
@override
void dispose() {
super.dispose();
rssiTimer.cancel();
stateChangedSubscription.cancel();
valueChangedSubscription.cancel();
state.dispose();
@ -764,6 +798,7 @@ class _PeripheralViewState extends State<PeripheralView> {
characteristic.dispose();
writeType.dispose();
maximumWriteLength.dispose();
rssi.dispose();
logs.dispose();
writeController.dispose();
}
@ -1070,3 +1105,25 @@ enum LogType {
notify,
error,
}
class RssiWidget extends StatelessWidget {
final int rssi;
const RssiWidget(
this.rssi, {
super.key,
});
@override
Widget build(BuildContext context) {
final IconData icon;
if (rssi > -70) {
icon = Icons.wifi_rounded;
} else if (rssi > -100) {
icon = Icons.wifi_2_bar_rounded;
} else {
icon = Icons.wifi_1_bar_rounded;
}
return Icon(icon);
}
}

View File

@ -15,7 +15,7 @@ packages:
path: ".."
relative: true
source: path
version: "3.0.0"
version: "3.0.2"
bluetooth_low_energy_platform_interface:
dependency: "direct main"
description:

View File

@ -1,6 +1,6 @@
name: bluetooth_low_energy_darwin
description: iOS and macOS implementation of the bluetooth_low_energy plugin.
version: 3.0.1
version: 3.0.2
homepage: https://github.com/yanshouwang/bluetooth_low_energy
environment: