Skip to content

Commit

Permalink
[BmMessages] use zero length string for null 'value' property
Browse files Browse the repository at this point in the history
  • Loading branch information
chipweinberger committed Jul 23, 2023
1 parent 2c012e3 commit 01338ed
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1629,6 +1629,9 @@ private static byte[] hexToBytes(String s) {
}

private static String bytesToHex(byte[] bytes) {
if (bytes == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public class MessageMaker {

private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String toHexString(byte[] bytes) {
if (bytes == null) {
return "";
}
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
Expand Down Expand Up @@ -180,9 +183,7 @@ static HashMap<String, Object> bmBluetoothCharacteristic(BluetoothDevice device,
map.put("characteristic_uuid", characteristic.getUuid().toString());
map.put("descriptors", descriptors);
map.put("properties", bmCharacteristicProperties(characteristic.getProperties()));
if(characteristic.getValue() != null) {
map.put("value", toHexString(characteristic.getValue()));
}
map.put("value", toHexString(characteristic.getValue()));
return map;
}

Expand All @@ -192,9 +193,7 @@ static HashMap<String, Object> bmBluetoothDescriptor(BluetoothDevice device, Blu
map.put("descriptor_uuid", descriptor.getUuid().toString());
map.put("characteristic_uuid", descriptor.getCharacteristic().getUuid().toString());
map.put("service_uuid", descriptor.getCharacteristic().getService().getUuid().toString());
if(descriptor.getValue() != null) {
map.put("value", toHexString(descriptor.getValue()));
}
map.put("value", toHexString(descriptor.getValue()));
return map;
}

Expand Down
124 changes: 81 additions & 43 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,16 @@ class FlutterBlueApp extends StatelessWidget {
}
}

class BluetoothOffScreen extends StatelessWidget {
class BluetoothOffScreen extends StatefulWidget {
const BluetoothOffScreen({Key? key, this.adapterState}) : super(key: key);

final BluetoothAdapterState? adapterState;

@override
State<BluetoothOffScreen> createState() => _BluetoothOffScreenState();
}

class _BluetoothOffScreenState extends State<BluetoothOffScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
Expand All @@ -69,7 +74,7 @@ class BluetoothOffScreen extends StatelessWidget {
color: Colors.white54,
),
Text(
'Bluetooth Adapter is ${adapterState != null ? adapterState.toString().split(".").last : 'not available'}.',
'Bluetooth Adapter is ${widget.adapterState != null ? widget.adapterState.toString().split(".").last : 'not available'}.',
style: Theme.of(context).primaryTextTheme.titleSmall?.copyWith(color: Colors.white),
),
ElevatedButton(
Expand All @@ -80,8 +85,10 @@ class BluetoothOffScreen extends StatelessWidget {
FlutterBluePlus.instance.turnOn();
}
} catch (e) {
final snackBar = SnackBar(content: Text('Error: [turnOn] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
if (mounted) {
final snackBar = SnackBar(content: Text('Error: [turnOn] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
},
),
Expand All @@ -92,9 +99,14 @@ class BluetoothOffScreen extends StatelessWidget {
}
}

class FindDevicesScreen extends StatelessWidget {
class FindDevicesScreen extends StatefulWidget {
const FindDevicesScreen({Key? key}) : super(key: key);

@override
State<FindDevicesScreen> createState() => _FindDevicesScreenState();
}

class _FindDevicesScreenState extends State<FindDevicesScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
Expand Down Expand Up @@ -155,8 +167,10 @@ class FindDevicesScreen extends StatelessWidget {
result: r,
onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) {
r.device.connect().catchError((e) {
final snackBar = SnackBar(content: Text('Error: [connect] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
if (mounted) {
final snackBar = SnackBar(content: Text('Error: [connect] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
});
return DeviceScreen(device: r.device);
})),
Expand All @@ -177,9 +191,11 @@ class FindDevicesScreen extends StatelessWidget {
return FloatingActionButton(
child: const Icon(Icons.stop),
onPressed: () {
FlutterBluePlus.instance.stopScan().catchError((e){
final snackBar = SnackBar(content: Text('Error: [stopScan] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
FlutterBluePlus.instance.stopScan().catchError((e) {
if (mounted) {
final snackBar = SnackBar(content: Text('Error: [stopScan] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
});
},
backgroundColor: Colors.red,
Expand All @@ -188,10 +204,13 @@ class FindDevicesScreen extends StatelessWidget {
return FloatingActionButton(
child: const Icon(Icons.search),
onPressed: () {
FlutterBluePlus.instance
.startScan(timeout: const Duration(seconds: 15), androidUsesFineLocation: false).catchError((e){
final snackBar = SnackBar(content: Text('Error: [startScan] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
FlutterBluePlus.instance
.startScan(timeout: const Duration(seconds: 15), androidUsesFineLocation: false)
.catchError((e) {
if (mounted) {
final snackBar = SnackBar(content: Text('Error: [startScan] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
});
});
}
Expand All @@ -201,11 +220,16 @@ class FindDevicesScreen extends StatelessWidget {
}
}

class DeviceScreen extends StatelessWidget {
class DeviceScreen extends StatefulWidget {
const DeviceScreen({Key? key, required this.device}) : super(key: key);

final BluetoothDevice device;

@override
State<DeviceScreen> createState() => _DeviceScreenState();
}

class _DeviceScreenState extends State<DeviceScreen> {
List<int> _getRandomBytes() {
final math = Random();
return [math.nextInt(255), math.nextInt(255), math.nextInt(255), math.nextInt(255)];
Expand All @@ -224,8 +248,10 @@ class DeviceScreen extends StatelessWidget {
try {
await c.read();
} catch (e) {
final snackBar = SnackBar(content: Text('Error: [read] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
if (mounted) {
final snackBar = SnackBar(content: Text('Error: [read] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
},
onWritePressed: () async {
Expand All @@ -235,8 +261,10 @@ class DeviceScreen extends StatelessWidget {
await c.read();
}
} catch (e) {
final snackBar = SnackBar(content: Text('Error: [write] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
if (mounted) {
final snackBar = SnackBar(content: Text('Error: [write] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
},
onNotificationPressed: () async {
Expand All @@ -246,8 +274,10 @@ class DeviceScreen extends StatelessWidget {
await c.read();
}
} catch (e) {
final snackBar = SnackBar(content: Text('Error: [setNotifyValue] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
if (mounted) {
final snackBar = SnackBar(content: Text('Error: [setNotifyValue] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
},
descriptorTiles: c.descriptors
Expand All @@ -271,10 +301,10 @@ class DeviceScreen extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(device.localName),
title: Text(widget.device.localName),
actions: <Widget>[
StreamBuilder<BluetoothConnectionState>(
stream: device.connectionState,
stream: widget.device.connectionState,
initialData: BluetoothConnectionState.connecting,
builder: (c, snapshot) {
VoidCallback? onPressed;
Expand All @@ -283,21 +313,25 @@ class DeviceScreen extends StatelessWidget {
case BluetoothConnectionState.connected:
onPressed = () async {
try {
await device.disconnect();
await widget.device.disconnect();
} catch (e) {
final snackBar = SnackBar(content: Text('Error: [disconnect] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
if (mounted) {
final snackBar = SnackBar(content: Text('Error: [disconnect] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
};
text = 'DISCONNECT';
break;
case BluetoothConnectionState.disconnected:
onPressed = () async {
onPressed = () async {
try {
await device.connect();
await widget.device.connect();
} catch (e) {
final snackBar = SnackBar(content: Text('Error: [connect] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
if (mounted) {
final snackBar = SnackBar(content: Text('Error: [connect] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
};
text = 'CONNECT';
Expand All @@ -321,7 +355,7 @@ class DeviceScreen extends StatelessWidget {
child: Column(
children: <Widget>[
StreamBuilder<BluetoothConnectionState>(
stream: device.connectionState,
stream: widget.device.connectionState,
initialData: BluetoothConnectionState.connecting,
builder: (c, snapshot) => ListTile(
leading: Column(
Expand All @@ -341,9 +375,9 @@ class DeviceScreen extends StatelessWidget {
],
),
title: Text('Device is ${snapshot.data.toString().split('.')[1]}.'),
subtitle: Text('${device.remoteId}'),
subtitle: Text('${widget.device.remoteId}'),
trailing: StreamBuilder<bool>(
stream: device.isDiscoveringServices,
stream: widget.device.isDiscoveringServices,
initialData: false,
builder: (c, snapshot) => IndexedStack(
index: snapshot.data! ? 1 : 0,
Expand All @@ -352,10 +386,12 @@ class DeviceScreen extends StatelessWidget {
child: const Text("Discover Services"),
onPressed: () async {
try {
await device.discoverServices();
await widget.device.discoverServices();
} catch (e) {
final snackBar = SnackBar(content: Text('Error: [discoverServices] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
if (mounted) {
final snackBar = SnackBar(content: Text('Error: [discoverServices] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
},
),
Expand All @@ -375,7 +411,7 @@ class DeviceScreen extends StatelessWidget {
),
),
StreamBuilder<int>(
stream: device.mtu,
stream: widget.device.mtu,
initialData: 0,
builder: (c, snapshot) => ListTile(
title: const Text('MTU Size'),
Expand All @@ -384,16 +420,18 @@ class DeviceScreen extends StatelessWidget {
icon: const Icon(Icons.edit),
onPressed: () async {
try {
await device.requestMtu(223);
await widget.device.requestMtu(223);
} catch (e) {
final snackBar = SnackBar(content: Text('Error: [requestMtu] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
if (mounted) {
final snackBar = SnackBar(content: Text('Error: [requestMtu] ${e.toString()}'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
}),
),
),
StreamBuilder<List<BluetoothService>>(
stream: device.services,
stream: widget.device.services,
initialData: const [],
builder: (c, snapshot) {
return Column(
Expand All @@ -409,12 +447,12 @@ class DeviceScreen extends StatelessWidget {

Stream<int> rssiStream({Duration frequency = const Duration(seconds: 1)}) async* {
var isConnected = true;
final subscription = device.connectionState.listen((v) {
final subscription = widget.device.connectionState.listen((v) {
isConnected = v == BluetoothConnectionState.connected;
});
while (isConnected) {
try {
yield await device.readRssi();
yield await widget.device.readRssi();
} catch (e) {
print("Error reading RSSI: $e");
break;
Expand Down
4 changes: 2 additions & 2 deletions example/lib/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ class _CharacteristicTileState extends State<CharacteristicTile> {
return StreamBuilder<List<int>>(
stream: widget.characteristic.lastValueStream,
initialData: widget.characteristic.lastValue,
builder: (c, snapshot) {
final value = snapshot.data;
builder: (context, snapshot) {
final List<int>? value = snapshot.data;
return ExpansionTile(
title: ListTile(
title: Row(
Expand Down
Loading

0 comments on commit 01338ed

Please sign in to comment.