Improve format for discovered services (#209)
* Update the peripheral detail page to format and display discovered services more clearly, including characteristics. * Address AI comments * Refactor characteristic display format in peripheral detail page for improved clarity
This commit is contained in:
@@ -11,6 +11,8 @@
|
||||
* Display RSSI values in device details
|
||||
* Persist filters
|
||||
* Fix clear log button
|
||||
* Move "Copy Services" button to Services panel header
|
||||
* Enhance services format to be more detailed and human-readable
|
||||
|
||||
## 1.0.0
|
||||
* Initial release
|
||||
@@ -126,19 +126,28 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
|
||||
() async {
|
||||
var services = await bleDevice.discoverServices(withDescriptors: false);
|
||||
debugPrint('${services.length} services discovered');
|
||||
debugPrint(services.toString());
|
||||
debugPrint(_formattedServices(services));
|
||||
setState(() {
|
||||
discoveredServices = services;
|
||||
});
|
||||
|
||||
// Build log message step-by-step for clarity
|
||||
final StringBuffer logMessage = StringBuffer();
|
||||
logMessage.write('${services.length} services discovered');
|
||||
if (kIsWeb) {
|
||||
_addLog(
|
||||
"DiscoverServices",
|
||||
'${services.length} services discovered,\n$webWarning',
|
||||
);
|
||||
logMessage.write(',\n$webWarning');
|
||||
}
|
||||
logMessage.write('\n\n${_formattedServices(services)}');
|
||||
|
||||
_addLog("DiscoverServices", logMessage.toString());
|
||||
},
|
||||
onError: (error) {
|
||||
_addLog("DiscoverServicesError", '$error\n${kIsWeb ? webWarning : ""}');
|
||||
final StringBuffer errorMessage = StringBuffer();
|
||||
errorMessage.write(error);
|
||||
if (kIsWeb) {
|
||||
errorMessage.write('\n$webWarning');
|
||||
}
|
||||
_addLog("DiscoverServicesError", errorMessage.toString());
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -313,12 +322,30 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
|
||||
serviceListBuilder: () => _buildServicesList(onSelect: (_, __) {
|
||||
Navigator.pop(context);
|
||||
}),
|
||||
onCopyServices: discoveredServices.isNotEmpty
|
||||
? () async {
|
||||
await _copyServicesToClipboard();
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _copyServicesToClipboard() async {
|
||||
final servicesText = _formattedServices(discoveredServices);
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: servicesText),
|
||||
);
|
||||
if (context.mounted) {
|
||||
_showSnackBar('All services copied to clipboard');
|
||||
}
|
||||
}
|
||||
|
||||
void _showSnackBar(String message) {
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -329,6 +356,20 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
|
||||
}
|
||||
}
|
||||
|
||||
String _formattedServices(List<BleService> services) {
|
||||
final buffer = StringBuffer();
|
||||
for (var service in services) {
|
||||
buffer.writeln('Service: ${service.uuid}');
|
||||
for (var characteristic in service.characteristics) {
|
||||
final properties =
|
||||
characteristic.properties.map((p) => p.name).join(', ');
|
||||
buffer.writeln(' ${characteristic.uuid} ($properties)');
|
||||
}
|
||||
buffer.writeln();
|
||||
}
|
||||
return buffer.toString().trim();
|
||||
}
|
||||
|
||||
@override
|
||||
void setState(VoidCallback fn) {
|
||||
if (mounted) super.setState(fn);
|
||||
@@ -431,6 +472,9 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
|
||||
child: ServicesSideWidget(
|
||||
discoveredServices: discoveredServices,
|
||||
serviceListBuilder: _buildServicesList,
|
||||
onCopyServices: discoveredServices.isNotEmpty
|
||||
? _copyServicesToClipboard
|
||||
: null,
|
||||
),
|
||||
),
|
||||
// Main content
|
||||
@@ -1151,29 +1195,6 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (discoveredServices.isNotEmpty)
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final servicesText =
|
||||
discoveredServices.map((s) => s.uuid).join('\n');
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: servicesText),
|
||||
);
|
||||
_showSnackBar('All services copied to clipboard');
|
||||
},
|
||||
icon: const Icon(Icons.copy),
|
||||
label: const Text('Copy Services'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: colorScheme.onSurface,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
_addLog(
|
||||
|
||||
@@ -4,10 +4,12 @@ import 'package:universal_ble/universal_ble.dart';
|
||||
class ServicesSideWidget extends StatelessWidget {
|
||||
final List<BleService> discoveredServices;
|
||||
final Function() serviceListBuilder;
|
||||
final VoidCallback? onCopyServices;
|
||||
const ServicesSideWidget({
|
||||
super.key,
|
||||
required this.discoveredServices,
|
||||
required this.serviceListBuilder,
|
||||
this.onCopyServices,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -54,6 +56,22 @@ class ServicesSideWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
if (onCopyServices != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: IconButton(
|
||||
onPressed: onCopyServices,
|
||||
icon: const Icon(Icons.copy),
|
||||
iconSize: 18,
|
||||
tooltip: 'Copy Services',
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(
|
||||
minWidth: 32,
|
||||
minHeight: 32,
|
||||
),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
|
||||
Reference in New Issue
Block a user