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

Adds support for Android companion pairing, device bonding, iOS device name (via GAP) #760

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ To update the kotlin version open Android studio and go to `Tools > Kotlin > Con
The reactive BLE lib supports the following:

- BLE device discovery
- Permission-less connection on Android using the `CompanionDeviceManager` framework.
- Observe host device BLE status
- Establishing a BLE connection
- Maintaining connection status of multiple BLE devices
Expand Down
14 changes: 14 additions & 0 deletions packages/flutter_reactive_ble/lib/src/device_connector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ import 'package:reactive_ble_platform_interface/reactive_ble_platform_interface.
abstract class DeviceConnector {
Stream<ConnectionStateUpdate> get deviceConnectionStateUpdateStream;

Future<BondingStatus> establishBonding({
required String deviceId,
});

Stream<ConnectionStateUpdate> connect({
required String id,
Map<Uuid, List<Uuid>>? servicesWithCharacteristicsToDiscover,
Duration? connectionTimeout,
});

Future<String?> retrieveDeviceName(String id);

Stream<ConnectionStateUpdate> connectToAdvertisingDevice({
required String id,
required List<Uuid> withServices,
Expand Down Expand Up @@ -48,6 +54,10 @@ class DeviceConnectorImpl implements DeviceConnector {
Stream<ConnectionStateUpdate> get deviceConnectionStateUpdateStream =>
_blePlatform.connectionUpdateStream;

@override
Future<BondingStatus> establishBonding({required String deviceId}) async =>
_blePlatform.establishBonding(deviceId);

@override
Stream<ConnectionStateUpdate> connect({
required String id,
Expand Down Expand Up @@ -77,6 +87,10 @@ class DeviceConnectorImpl implements DeviceConnector {
return autoconnectingRepeater.stream;
}

@override
Future<String?> retrieveDeviceName(String id) =>
_blePlatform.retrieveDeviceName(id);

@override
Stream<ConnectionStateUpdate> connectToAdvertisingDevice({
required String id,
Expand Down
18 changes: 18 additions & 0 deletions packages/flutter_reactive_ble/lib/src/device_scanner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import 'package:flutter_reactive_ble/flutter_reactive_ble.dart';
abstract class DeviceScanner {
ScanSession? get currentScan;

Future<DeviceAssociationInfo?> launchCompanionWorkflow({
required String pattern,
required bool singleDeviceScan,
required bool forceConfirmation,
});

Stream<DiscoveredDevice> scanForDevices({
required List<Uuid> withServices,
ScanMode scanMode = ScanMode.balanced,
Expand Down Expand Up @@ -38,6 +44,18 @@ class DeviceScannerImpl implements DeviceScanner {
@override
ScanSession? get currentScan => _currentScanSession;

@override
Future<DeviceAssociationInfo?> launchCompanionWorkflow({
required String pattern,
required bool singleDeviceScan,
required bool forceConfirmation,
}) async =>
_blePlatform.launchCompanionWorkflow(
pattern: pattern,
singleDeviceScan: singleDeviceScan,
forceConfirmation: forceConfirmation,
);

@override
Stream<DiscoveredDevice> scanForDevices({
required List<Uuid> withServices,
Expand Down
33 changes: 33 additions & 0 deletions packages/flutter_reactive_ble/lib/src/reactive_ble.dart
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,20 @@ class FlutterReactiveBle {
return _connectedDeviceOperator.requestConnectionPriority(deviceId, priority);
}

Future<DeviceAssociationInfo?> launchCompanionWorkflow({
required String pattern,
bool singleDeviceScan = true,
bool forceConfirmation = false,
}) async {
await initialize();

return _deviceScanner.launchCompanionWorkflow(
pattern: pattern,
singleDeviceScan: singleDeviceScan,
forceConfirmation: forceConfirmation,
);
}

/// Scan for BLE peripherals advertising the services specified in [withServices]
/// or for all BLE peripherals, if no services is specified. It is recommended to always specify some services.
///
Expand All @@ -247,6 +261,12 @@ class FlutterReactiveBle {
);
}

Future<BondingStatus> establishBonding({required String deviceId}) async {
await initialize();

return _deviceConnector.establishBonding(deviceId: deviceId);
}

/// Establishes a connection to a BLE device.
///
/// Disconnecting the device is achieved by cancelling the stream subscription.
Expand Down Expand Up @@ -274,6 +294,19 @@ class FlutterReactiveBle {
),
);

/// iOS only. Retrieves the name of the device with the given id.
///
/// This operation can only succeed when the host is `connected` with the peripheral.
///
/// It is using the the CBPeripheral `name` property to retrieve the name.
/// See the [documentation](https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519029-name) for details.
/// Under the hood, this uses the GAP profile to retrieve the name.
///
/// This operation is only supported on Apple platforms.
/// It will throw an [UnimplementedError] on other platforms.
Future<String?> retrieveDeviceName(String id) =>
_deviceConnector.retrieveDeviceName(id);

/// Scans for a specific device and connects to it in case a device containing the specified [id]
/// is found and that is advertising the services specified in [withServices].
///
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter_reactive_ble/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ dev_dependencies:
sdk: flutter
functional_data_generator: ^1.1.2
mockito: ^5.0.14

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mocks generated by Mockito 5.4.0 from annotations
// Mocks generated by Mockito 5.4.4 from annotations
// in flutter_reactive_ble/test/connected_device_operation_test.dart.
// Do not manually edit this file.

Expand All @@ -14,6 +14,8 @@ import 'package:reactive_ble_platform_interface/src/reactive_ble_platform_interf
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: deprecated_member_use
// ignore_for_file: deprecated_member_use_from_same_package
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
Expand Down Expand Up @@ -68,23 +70,27 @@ class MockReactiveBlePlatform extends _i1.Mock
Invocation.getter(#scanStream),
returnValue: _i4.Stream<_i2.ScanResult>.empty(),
) as _i4.Stream<_i2.ScanResult>);

@override
_i4.Stream<_i2.BleStatus> get bleStatusStream => (super.noSuchMethod(
Invocation.getter(#bleStatusStream),
returnValue: _i4.Stream<_i2.BleStatus>.empty(),
) as _i4.Stream<_i2.BleStatus>);

@override
_i4.Stream<_i2.ConnectionStateUpdate> get connectionUpdateStream =>
(super.noSuchMethod(
Invocation.getter(#connectionUpdateStream),
returnValue: _i4.Stream<_i2.ConnectionStateUpdate>.empty(),
) as _i4.Stream<_i2.ConnectionStateUpdate>);

@override
_i4.Stream<_i2.CharacteristicValue> get charValueUpdateStream =>
(super.noSuchMethod(
Invocation.getter(#charValueUpdateStream),
returnValue: _i4.Stream<_i2.CharacteristicValue>.empty(),
) as _i4.Stream<_i2.CharacteristicValue>);

@override
_i4.Future<void> initialize() => (super.noSuchMethod(
Invocation.method(
Expand All @@ -94,6 +100,7 @@ class MockReactiveBlePlatform extends _i1.Mock
returnValue: _i4.Future<void>.value(),
returnValueForMissingStub: _i4.Future<void>.value(),
) as _i4.Future<void>);

@override
_i4.Future<void> deinitialize() => (super.noSuchMethod(
Invocation.method(
Expand All @@ -103,6 +110,26 @@ class MockReactiveBlePlatform extends _i1.Mock
returnValue: _i4.Future<void>.value(),
returnValueForMissingStub: _i4.Future<void>.value(),
) as _i4.Future<void>);

@override
_i4.Future<_i2.DeviceAssociationInfo?> launchCompanionWorkflow({
required String? pattern,
required bool? singleDeviceScan,
required bool? forceConfirmation,
}) =>
(super.noSuchMethod(
Invocation.method(
#launchCompanionWorkflow,
[],
{
#pattern: pattern,
#singleDeviceScan: singleDeviceScan,
#forceConfirmation: forceConfirmation,
},
),
returnValue: _i4.Future<_i2.DeviceAssociationInfo?>.value(),
) as _i4.Future<_i2.DeviceAssociationInfo?>);

@override
_i4.Stream<void> scanForDevices({
required List<_i2.Uuid>? withServices,
Expand All @@ -121,6 +148,7 @@ class MockReactiveBlePlatform extends _i1.Mock
),
returnValue: _i4.Stream<void>.empty(),
) as _i4.Stream<void>);

@override
_i4.Future<_i2.Result<_i2.Unit, _i2.GenericFailure<_i2.ClearGattCacheError>?>>
clearGattCache(String? deviceId) => (super.noSuchMethod(
Expand All @@ -140,8 +168,20 @@ class MockReactiveBlePlatform extends _i1.Mock
),
)),
) as _i4.Future<
_i2.Result<_i2.Unit,
_i2.GenericFailure<_i2.ClearGattCacheError>?>>);
_i2
.Result<_i2.Unit, _i2.GenericFailure<_i2.ClearGattCacheError>?>>);

@override
_i4.Future<_i2.BondingStatus> establishBonding(String? deviceId) =>
(super.noSuchMethod(
Invocation.method(
#establishBonding,
[deviceId],
),
returnValue:
_i4.Future<_i2.BondingStatus>.value(_i2.BondingStatus.none),
) as _i4.Future<_i2.BondingStatus>);

@override
_i4.Future<int> readRssi(String? deviceId) => (super.noSuchMethod(
Invocation.method(
Expand All @@ -150,6 +190,7 @@ class MockReactiveBlePlatform extends _i1.Mock
),
returnValue: _i4.Future<int>.value(0),
) as _i4.Future<int>);

@override
_i4.Stream<void> connectToDevice(
String? id,
Expand All @@ -167,6 +208,16 @@ class MockReactiveBlePlatform extends _i1.Mock
),
returnValue: _i4.Stream<void>.empty(),
) as _i4.Stream<void>);

@override
_i4.Future<String?> retrieveDeviceName(String? id) => (super.noSuchMethod(
Invocation.method(
#retrieveDeviceName,
[id],
),
returnValue: _i4.Future<String?>.value(),
) as _i4.Future<String?>);

@override
_i4.Future<void> disconnectDevice(String? deviceId) => (super.noSuchMethod(
Invocation.method(
Expand All @@ -176,6 +227,7 @@ class MockReactiveBlePlatform extends _i1.Mock
returnValue: _i4.Future<void>.value(),
returnValueForMissingStub: _i4.Future<void>.value(),
) as _i4.Future<void>);

@override
_i4.Future<List<_i2.DiscoveredService>> discoverServices(String? deviceId) =>
(super.noSuchMethod(
Expand All @@ -186,6 +238,7 @@ class MockReactiveBlePlatform extends _i1.Mock
returnValue: _i4.Future<List<_i2.DiscoveredService>>.value(
<_i2.DiscoveredService>[]),
) as _i4.Future<List<_i2.DiscoveredService>>);

@override
_i4.Future<List<_i2.DiscoveredService>> getDiscoverServices(
String? deviceId) =>
Expand All @@ -197,6 +250,7 @@ class MockReactiveBlePlatform extends _i1.Mock
returnValue: _i4.Future<List<_i2.DiscoveredService>>.value(
<_i2.DiscoveredService>[]),
) as _i4.Future<List<_i2.DiscoveredService>>);

@override
_i4.Stream<void> readCharacteristic(
_i2.CharacteristicInstance? characteristic) =>
Expand All @@ -207,6 +261,7 @@ class MockReactiveBlePlatform extends _i1.Mock
),
returnValue: _i4.Stream<void>.empty(),
) as _i4.Stream<void>);

@override
_i4.Future<_i2.WriteCharacteristicInfo> writeCharacteristicWithResponse(
_i2.CharacteristicInstance? characteristic,
Expand All @@ -232,6 +287,7 @@ class MockReactiveBlePlatform extends _i1.Mock
),
)),
) as _i4.Future<_i2.WriteCharacteristicInfo>);

@override
_i4.Future<_i2.WriteCharacteristicInfo> writeCharacteristicWithoutResponse(
_i2.CharacteristicInstance? characteristic,
Expand All @@ -257,6 +313,7 @@ class MockReactiveBlePlatform extends _i1.Mock
),
)),
) as _i4.Future<_i2.WriteCharacteristicInfo>);

@override
_i4.Stream<void> subscribeToNotifications(
_i2.CharacteristicInstance? characteristic) =>
Expand All @@ -267,6 +324,7 @@ class MockReactiveBlePlatform extends _i1.Mock
),
returnValue: _i4.Stream<void>.empty(),
) as _i4.Stream<void>);

@override
_i4.Future<void> stopSubscribingToNotifications(
_i2.CharacteristicInstance? characteristic) =>
Expand All @@ -278,6 +336,7 @@ class MockReactiveBlePlatform extends _i1.Mock
returnValue: _i4.Future<void>.value(),
returnValueForMissingStub: _i4.Future<void>.value(),
) as _i4.Future<void>);

@override
_i4.Future<int> requestMtuSize(
String? deviceId,
Expand All @@ -293,6 +352,7 @@ class MockReactiveBlePlatform extends _i1.Mock
),
returnValue: _i4.Future<int>.value(0),
) as _i4.Future<int>);

@override
_i4.Future<_i2.ConnectionPriorityInfo> requestConnectionPriority(
String? deviceId,
Expand Down
Loading
Loading