Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Services and characteristics list #92

Merged
merged 13 commits into from
Feb 5, 2020
46 changes: 39 additions & 7 deletions example/lib/adapter/ble_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:async';
import 'package:blemulator_example/example_peripherals/generic_peripheral.dart';
import 'package:blemulator_example/model/ble_peripheral.dart';
import 'package:blemulator_example/example_peripherals/sensor_tag.dart';
import 'package:blemulator_example/model/ble_service.dart';
import 'package:flutter_ble_lib/flutter_ble_lib.dart';
import 'package:blemulator/blemulator.dart';

Expand Down Expand Up @@ -30,6 +31,8 @@ class BleAdapter {

Stream<BlePeripheral> get blePeripherals => _blePeripheralsController.stream;

Map<String, Peripheral> _scannedPeripherals = Map();

factory BleAdapter(BleManager bleManager, Blemulator blemulator) {
if (_instance == null) {
_instance = BleAdapter._internal(bleManager, blemulator);
Expand Down Expand Up @@ -58,14 +61,16 @@ class BleAdapter {

Stream<BlePeripheral> _startPeripheralScan() {
return _bleManager.startPeripheralScan().map((scanResult) {
_scannedPeripherals.putIfAbsent(
scanResult.peripheral.identifier, () => scanResult.peripheral);
return BlePeripheral(
scanResult.peripheral.name ??
scanResult.advertisementData.localName ??
'Unknown',
scanResult.peripheral.identifier,
scanResult.rssi,
false,
BlePeripheralCategoryResolver.categoryForScanResult(scanResult),
scanResult.peripheral.name ??
scanResult.advertisementData.localName ??
'Unknown',
scanResult.peripheral.identifier,
scanResult.rssi,
false,
BlePeripheralCategoryResolver.categoryForScanResult(scanResult),
);
});
}
Expand All @@ -82,4 +87,31 @@ class BleAdapter {
_blemulator.addSimulatedPeripheral(GenericPeripheral());
_blemulator.simulate();
}

Future<List<BleService>> discoverAndGetServicesCharacteristics(
String peripheralId) async {
// TODO remove connect() call when connectivity handling is implemented
await _scannedPeripherals[peripheralId].connect();
await _scannedPeripherals[peripheralId]
.discoverAllServicesAndCharacteristics();

List<BleService> bleServices = [];
for (Service service
in await _scannedPeripherals[peripheralId].services()) {
List<Characteristic> serviceCharacteristics =
martawoldanska marked this conversation as resolved.
Show resolved Hide resolved
await service.characteristics();
List<BleCharacteristic> bleCharacteristics = serviceCharacteristics
.map(
(characteristic) =>
martawoldanska marked this conversation as resolved.
Show resolved Hide resolved
BleCharacteristic.fromCharacteristic(characteristic),
)
.toList();
bleServices.add(BleService(service.uuid, bleCharacteristics));
}

// TODO remove when connectivity handling is implemented
_scannedPeripherals[peripheralId].disconnectOrCancelConnection();
martawoldanska marked this conversation as resolved.
Show resolved Hide resolved

return bleServices;
}
}
6 changes: 4 additions & 2 deletions example/lib/common/components/property_row.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class PropertyRow extends StatelessWidget {
final Widget titleIcon;
final Color titleColor;
final String valueCompanion;
final TextStyle valueTextStyle;
final Widget rowAccessory;
final Widget titleAccessory;
final Widget valueAccessory;
Expand All @@ -17,6 +18,7 @@ class PropertyRow extends StatelessWidget {
this.titleIcon,
this.titleColor,
@required this.value,
this.valueTextStyle = CustomTextStyle.cardValue,
this.valueCompanion,
this.rowAccessory,
this.titleAccessory,
Expand Down Expand Up @@ -113,7 +115,7 @@ class PropertyRow extends StatelessWidget {
child: Text(
value ?? '',
textWidthBasis: TextWidthBasis.longestLine,
style: CustomTextStyle.cardValue,
style: valueTextStyle,
),
),
),
Expand All @@ -129,7 +131,7 @@ class PropertyRow extends StatelessWidget {
return Expanded(
child: Text(
value ?? '',
style: CustomTextStyle.cardValue,
style: valueTextStyle,
),
);
}
Expand Down
2 changes: 1 addition & 1 deletion example/lib/device_details/device_detail_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class DeviceDetailsViewState extends State<DeviceDetailsView> {
child: Scaffold(
backgroundColor: Colors.grey[300],
appBar: AppBar(
title: Text('Devicie Details'),
title: Text('Device Details'),
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.autorenew), text: "Automatic",),
Expand Down
52 changes: 43 additions & 9 deletions example/lib/example_peripherals/generic_peripheral.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,49 @@ class GenericPeripheral extends SimulatedPeripheral {
advertisementInterval: Duration(milliseconds: milliseconds),
services: [
SimulatedService(
uuid: 'F000AA00-0001-4000-B000-000000000000',
isAdvertised: true,
characteristics: [
SimulatedCharacteristic(
uuid: 'F000AA10-0001-4000-B000-000000000000',
value: Uint8List.fromList([0]),
convenienceName: 'Generic characteristic'),
],
convenienceName: 'Generic service'),
uuid: 'F000AA00-0001-4000-B000-000000000000',
isAdvertised: true,
characteristics: [
SimulatedCharacteristic(
uuid: 'F000AA10-0001-4000-B000-000000000000',
value: Uint8List.fromList([0]),
convenienceName: 'Generic characteristic 1'),
],
convenienceName: 'Generic service 1',
),
SimulatedService(
uuid: 'F000AA01-0001-4000-B000-000000000000',
isAdvertised: true,
characteristics: [
SimulatedCharacteristic(
uuid: 'F000AA11-0001-4000-B000-000000000000',
value: Uint8List.fromList([0]),
convenienceName: 'Generic characteristic 2'),
],
convenienceName: 'Generic service 2',
),
SimulatedService(
uuid: 'F000AA02-0001-4000-B000-000000000000',
isAdvertised: true,
characteristics: [
SimulatedCharacteristic(
uuid: 'F000AA12-0001-4000-B000-000000000000',
value: Uint8List.fromList([0]),
convenienceName: 'Generic characteristic 3'),
],
convenienceName: 'Generic service 3',
),
SimulatedService(
uuid: 'F000AA03-0001-4000-B000-000000000000',
isAdvertised: true,
characteristics: [
SimulatedCharacteristic(
uuid: 'F000AA13-0001-4000-B000-000000000000',
value: Uint8List.fromList([0]),
convenienceName: 'Generic characteristic 4'),
],
convenienceName: 'Generic service 4',
),
],
martawoldanska marked this conversation as resolved.
Show resolved Hide resolved
);

Expand Down
54 changes: 54 additions & 0 deletions example/lib/model/ble_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'dart:typed_data';

import 'package:equatable/equatable.dart';
import 'package:flutter_ble_lib/flutter_ble_lib.dart';

class BleService extends Equatable {
final String uuid;
final List<BleCharacteristic> characteristics;

BleService(this.uuid, this.characteristics);

@override
martawoldanska marked this conversation as resolved.
Show resolved Hide resolved
List<Object> get props => [uuid, characteristics];
}

class BleCharacteristic extends Equatable {
final String uuid;
final Uint8List value;
final bool isReadable;
final bool isWritableWithResponse;
final bool isWritableWithoutResponse;
final bool isNotifiable;
final bool isIndicatable;

BleCharacteristic(
this.uuid,
this.value,
this.isReadable,
this.isWritableWithResponse,
this.isWritableWithoutResponse,
this.isNotifiable,
this.isIndicatable,
);

@override
List<Object> get props => [
uuid,
value,
isReadable,
isWritableWithResponse,
isWritableWithoutResponse,
isNotifiable,
isIndicatable
];

BleCharacteristic.fromCharacteristic(Characteristic characteristic)
: uuid = characteristic.uuid,
value = null,
isReadable = characteristic.isReadable,
isWritableWithResponse = characteristic.isWritableWithResponse,
isWritableWithoutResponse = characteristic.isWritableWithoutResponse,
isNotifiable = characteristic.isNotifiable,
isIndicatable = characteristic.isIndicatable;
}
133 changes: 118 additions & 15 deletions example/lib/peripheral_details/components/peripheral_details_view.dart
Original file line number Diff line number Diff line change
@@ -1,32 +1,135 @@
import 'package:blemulator_example/model/ble_service.dart';
import 'package:blemulator_example/peripheral_details/bloc.dart';
import 'package:blemulator_example/common/components/property_row.dart';
import 'package:blemulator_example/styles/custom_text_style.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class PeripheralDetailsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
SliverSafeArea(
top: false,
sliver: SliverPadding(
padding: const EdgeInsets.all(8.0),
sliver: SliverToBoxAdapter(
child: BlocBuilder<PeripheralDetailsBloc, PeripheralDetailsState>(
builder: (context, state) {
return PropertyRow(
title: 'Identifier',
titleIcon: Icon(Icons.perm_device_information),
titleColor: Theme.of(context).primaryColor,
value: state.peripheral.id,
);
},
),
SliverPadding(
padding: const EdgeInsets.all(8.0),
sliver: SliverToBoxAdapter(
child: BlocBuilder<PeripheralDetailsBloc, PeripheralDetailsState>(
builder: (context, state) {
return Column(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it redrawn when any of serviceState.expanded has been changed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understand it, only the changed view will be redrawn. Whole column view would be rebuilt each time expanded state changes, but only changed part would be actually redrawn.

mainAxisSize: MainAxisSize.min,
children: <Widget>[
PropertyRow(
title: 'Identifier',
titleIcon: Icon(Icons.perm_device_information),
titleColor: Theme.of(context).primaryColor,
value: state.peripheral.id,
),
],
);
},
),
),
),
BlocBuilder<PeripheralDetailsBloc, PeripheralDetailsState>(
builder: (context, state) {
return _createServiceView(context, state);
},
)
],
);
}

Widget _createServiceView(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering, maybe it(Services view) should be extracted to separate view/widget/file?

BuildContext context,
PeripheralDetailsState state,
) {
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => _createServiceTileView(
context, state.bleServiceStates[index], index),
childCount: state.bleServiceStates.length,
),
);
}

Widget _createServiceTileView(
BuildContext context,
BleServiceState serviceState,
int index,
) {
// ignore: close_sinks
final PeripheralDetailsBloc bloc =
BlocProvider.of<PeripheralDetailsBloc>(context);

return Column(
children: <Widget>[
PropertyRow(
title: "Service UUID",
titleColor: Theme.of(context).primaryColor,
value: serviceState.service.uuid,
valueTextStyle: CustomTextStyle.serviceUuidStyle,
rowAccessory: IconButton(
icon: Icon(
serviceState.expanded ? Icons.unfold_less : Icons.unfold_more),
onPressed: () => bloc.add(ServiceViewExpandedEvent(
index,
)),
),
),
if (serviceState.expanded)
Padding(
padding: EdgeInsets.only(left: 16.0),
child: ListView.builder(
itemCount: serviceState.service.characteristics.length,
itemBuilder: (context, index) => _buildCharacteristicCard(
context, serviceState.service.characteristics[index]),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
),
),
],
);
}

Widget _buildCharacteristicCard(
BuildContext context,
BleCharacteristic characteristic,
) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"UUID: ${characteristic.uuid}",
style: CustomTextStyle.characteristicsStyle,
),
Text(
"Properties: ${_getCharacteristicProperties(characteristic).toString()}",
style: CustomTextStyle.characteristicsStyle,
),
],
),
),
);
}
}

List<String> _getCharacteristicProperties(BleCharacteristic characteristic) {
List<String> properties = new List<String>();

if (characteristic.isWritableWithResponse ||
characteristic.isWritableWithoutResponse) {
martawoldanska marked this conversation as resolved.
Show resolved Hide resolved
properties.add("write");
}
if (characteristic.isReadable) {
properties.add("read");
}
if (characteristic.isIndicatable || characteristic.isNotifiable) {
properties.add("notify");
}
martawoldanska marked this conversation as resolved.
Show resolved Hide resolved

return properties;
}
Loading