mirror of
https://github.com/0015/ThatProject.git
synced 2026-01-12 09:17:42 +03:00
302 lines
9.0 KiB
Dart
302 lines
9.0 KiB
Dart
// Copyright 2017, Paul DeMarco.
|
|
// All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_blue/flutter_blue.dart';
|
|
|
|
class ScanResultTile extends StatelessWidget {
|
|
const ScanResultTile({Key key, this.result, this.onTap}) : super(key: key);
|
|
|
|
final ScanResult result;
|
|
final VoidCallback onTap;
|
|
|
|
Widget _buildTitle(BuildContext context) {
|
|
if (result.device.name.length > 0) {
|
|
return Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
Text(
|
|
result.device.name,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
Text(
|
|
result.device.id.toString(),
|
|
style: Theme.of(context).textTheme.caption,
|
|
)
|
|
],
|
|
);
|
|
} else {
|
|
return Text(result.device.id.toString());
|
|
}
|
|
}
|
|
|
|
Widget _buildAdvRow(BuildContext context, String title, String value) {
|
|
return Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
Text(title, style: Theme.of(context).textTheme.caption),
|
|
SizedBox(
|
|
width: 12.0,
|
|
),
|
|
Expanded(
|
|
child: Text(
|
|
value,
|
|
style: Theme.of(context)
|
|
.textTheme
|
|
.caption
|
|
.apply(color: Colors.black),
|
|
softWrap: true,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
String getNiceHexArray(List<int> bytes) {
|
|
return '[${bytes.map((i) => i.toRadixString(16).padLeft(2, '0')).join(', ')}]'
|
|
.toUpperCase();
|
|
}
|
|
|
|
String getNiceManufacturerData(Map<int, List<int>> data) {
|
|
if (data.isEmpty) {
|
|
return null;
|
|
}
|
|
List<String> res = [];
|
|
data.forEach((id, bytes) {
|
|
res.add(
|
|
'${id.toRadixString(16).toUpperCase()}: ${getNiceHexArray(bytes)}');
|
|
});
|
|
return res.join(', ');
|
|
}
|
|
|
|
String getNiceServiceData(Map<String, List<int>> data) {
|
|
if (data.isEmpty) {
|
|
return null;
|
|
}
|
|
List<String> res = [];
|
|
data.forEach((id, bytes) {
|
|
res.add('${id.toUpperCase()}: ${getNiceHexArray(bytes)}');
|
|
});
|
|
return res.join(', ');
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ExpansionTile(
|
|
title: _buildTitle(context),
|
|
leading: Text(result.rssi.toString()),
|
|
trailing: RaisedButton(
|
|
child: Text('CONNECT'),
|
|
color: Colors.black,
|
|
textColor: Colors.white,
|
|
onPressed: (result.advertisementData.connectable) ? onTap : null,
|
|
),
|
|
children: <Widget>[
|
|
_buildAdvRow(
|
|
context, 'Complete Local Name', result.advertisementData.localName),
|
|
_buildAdvRow(context, 'Tx Power Level',
|
|
'${result.advertisementData.txPowerLevel ?? 'N/A'}'),
|
|
_buildAdvRow(
|
|
context,
|
|
'Manufacturer Data',
|
|
getNiceManufacturerData(
|
|
result.advertisementData.manufacturerData) ??
|
|
'N/A'),
|
|
_buildAdvRow(
|
|
context,
|
|
'Service UUIDs',
|
|
(result.advertisementData.serviceUuids.isNotEmpty)
|
|
? result.advertisementData.serviceUuids.join(', ').toUpperCase()
|
|
: 'N/A'),
|
|
_buildAdvRow(context, 'Service Data',
|
|
getNiceServiceData(result.advertisementData.serviceData) ?? 'N/A'),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class ServiceTile extends StatelessWidget {
|
|
final BluetoothService service;
|
|
final List<CharacteristicTile> characteristicTiles;
|
|
|
|
const ServiceTile({Key key, this.service, this.characteristicTiles})
|
|
: super(key: key);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (characteristicTiles.length > 0) {
|
|
return ExpansionTile(
|
|
title: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
Text('Service'),
|
|
Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}',
|
|
style: Theme.of(context)
|
|
.textTheme
|
|
.body1
|
|
.copyWith(color: Theme.of(context).textTheme.caption.color))
|
|
],
|
|
),
|
|
children: characteristicTiles,
|
|
);
|
|
} else {
|
|
return ListTile(
|
|
title: Text('Service'),
|
|
subtitle:
|
|
Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}'),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
class CharacteristicTile extends StatelessWidget {
|
|
final BluetoothCharacteristic characteristic;
|
|
final List<DescriptorTile> descriptorTiles;
|
|
final VoidCallback onReadPressed;
|
|
final VoidCallback onWritePressed;
|
|
final VoidCallback onNotificationPressed;
|
|
|
|
const CharacteristicTile(
|
|
{Key key,
|
|
this.characteristic,
|
|
this.descriptorTiles,
|
|
this.onReadPressed,
|
|
this.onWritePressed,
|
|
this.onNotificationPressed})
|
|
: super(key: key);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return StreamBuilder<List<int>>(
|
|
stream: characteristic.value,
|
|
initialData: characteristic.lastValue,
|
|
builder: (c, snapshot) {
|
|
final value = snapshot.data;
|
|
return ExpansionTile(
|
|
title: ListTile(
|
|
title: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
Text('Characteristic'),
|
|
Text(
|
|
'0x${characteristic.uuid.toString().toUpperCase().substring(4, 8)}',
|
|
style: Theme.of(context).textTheme.body1.copyWith(
|
|
color: Theme.of(context).textTheme.caption.color))
|
|
],
|
|
),
|
|
subtitle: Text(value.toString()),
|
|
contentPadding: EdgeInsets.all(0.0),
|
|
),
|
|
trailing: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: <Widget>[
|
|
IconButton(
|
|
icon: Icon(
|
|
Icons.file_download,
|
|
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
|
|
),
|
|
onPressed: onReadPressed,
|
|
),
|
|
IconButton(
|
|
icon: Icon(Icons.file_upload,
|
|
color: Theme.of(context).iconTheme.color.withOpacity(0.5)),
|
|
onPressed: onWritePressed,
|
|
),
|
|
IconButton(
|
|
icon: Icon(
|
|
characteristic.isNotifying
|
|
? Icons.sync_disabled
|
|
: Icons.sync,
|
|
color: Theme.of(context).iconTheme.color.withOpacity(0.5)),
|
|
onPressed: onNotificationPressed,
|
|
)
|
|
],
|
|
),
|
|
children: descriptorTiles,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class DescriptorTile extends StatelessWidget {
|
|
final BluetoothDescriptor descriptor;
|
|
final VoidCallback onReadPressed;
|
|
final VoidCallback onWritePressed;
|
|
|
|
const DescriptorTile(
|
|
{Key key, this.descriptor, this.onReadPressed, this.onWritePressed})
|
|
: super(key: key);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ListTile(
|
|
title: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: <Widget>[
|
|
Text('Descriptor'),
|
|
Text('0x${descriptor.uuid.toString().toUpperCase().substring(4, 8)}',
|
|
style: Theme.of(context)
|
|
.textTheme
|
|
.body1
|
|
.copyWith(color: Theme.of(context).textTheme.caption.color))
|
|
],
|
|
),
|
|
subtitle: StreamBuilder<List<int>>(
|
|
stream: descriptor.value,
|
|
initialData: descriptor.lastValue,
|
|
builder: (c, snapshot) => Text(snapshot.data.toString()),
|
|
),
|
|
trailing: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: <Widget>[
|
|
IconButton(
|
|
icon: Icon(
|
|
Icons.file_download,
|
|
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
|
|
),
|
|
onPressed: onReadPressed,
|
|
),
|
|
IconButton(
|
|
icon: Icon(
|
|
Icons.file_upload,
|
|
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
|
|
),
|
|
onPressed: onWritePressed,
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class AdapterStateTile extends StatelessWidget {
|
|
const AdapterStateTile({Key key, @required this.state}) : super(key: key);
|
|
|
|
final BluetoothState state;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
color: Colors.redAccent,
|
|
child: ListTile(
|
|
title: Text(
|
|
'Bluetooth adapter is ${state.toString().substring(15)}',
|
|
style: Theme.of(context).primaryTextTheme.subhead,
|
|
),
|
|
trailing: Icon(
|
|
Icons.error,
|
|
color: Theme.of(context).primaryTextTheme.subhead.color,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |