From d23f2cdde85ae4ad459121db59986520d8f34bec Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Mon, 6 Jul 2020 13:40:51 +0200 Subject: [PATCH 01/31] Bump version to 2.2.6 --- android/build.gradle | 2 +- ios/flutter_ble_lib.podspec | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 2423646c..a397b0eb 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ group 'com.polidea.flutter_ble_lib' -version '2.2.5' +version '2.2.6' buildscript { repositories { diff --git a/ios/flutter_ble_lib.podspec b/ios/flutter_ble_lib.podspec index aa03a4bf..51f47c01 100644 --- a/ios/flutter_ble_lib.podspec +++ b/ios/flutter_ble_lib.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_ble_lib' - s.version = '2.2.5' + s.version = '2.2.6' s.summary = 'A new flutter plugin project.' s.description = <<-DESC A new flutter plugin project. diff --git a/pubspec.yaml b/pubspec.yaml index 9c1d1ebc..beb0c30c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_ble_lib description: FlutterBle Library is a flutter library that supports BLE operations. It uses MultiPlatformBleAdapter as a native backend.. -version: 2.2.5 +version: 2.2.6 homepage: https://github.com/Polidea/FlutterBleLib environment: From f4051db677c2a1c0bba00532e157e548b22a4b10 Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Fri, 17 Jul 2020 15:29:27 +0200 Subject: [PATCH 02/31] Added pedantic lib for better static analysis (#494) * Added pedantic lib for better static analysis Fixed (what I hope) are the biggest static analysis issues on pub.dev. * Fixed device_connection_mixin. * Code review updates. * Format fix. --- analysis_options.yaml | 1 + lib/ble_manager.dart | 4 +- lib/peripheral.dart | 2 +- lib/scan_result.dart | 2 - lib/src/bridge/device_connection_mixin.dart | 67 ++++++++++++++------- pubspec.yaml | 1 + 6 files changed, 48 insertions(+), 29 deletions(-) create mode 100644 analysis_options.yaml diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..d4fcc1ad --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1 @@ +include: package:pedantic/analysis_options.yaml \ No newline at end of file diff --git a/lib/ble_manager.dart b/lib/ble_manager.dart index 34fb4734..5b0def9b 100644 --- a/lib/ble_manager.dart +++ b/lib/ble_manager.dart @@ -34,9 +34,7 @@ abstract class BleManager { static BleManager _instance; factory BleManager() { - if (_instance == null) { - _instance = InternalBleManager(); - } + _instance ??= InternalBleManager(); return _instance; } diff --git a/lib/peripheral.dart b/lib/peripheral.dart index c8298878..4c22fd4c 100644 --- a/lib/peripheral.dart +++ b/lib/peripheral.dart @@ -15,7 +15,7 @@ abstract class _PeripheralMetadata { /// [disconnectOrCancelConnection()] can be used if peripheral is not connected. class Peripheral { static const int NO_MTU_NEGOTIATION = 0; - ManagerForPeripheral _manager; + final ManagerForPeripheral _manager; String name; String identifier; diff --git a/lib/scan_result.dart b/lib/scan_result.dart index 112b3c65..3ae7ce6d 100644 --- a/lib/scan_result.dart +++ b/lib/scan_result.dart @@ -1,8 +1,6 @@ part of flutter_ble_lib; abstract class _ScanResultMetadata { - static const String id = "id"; - static const String name = "name"; static const String rssi = "rssi"; static const String manufacturerData = "manufacturerData"; static const String serviceData = "serviceData"; diff --git a/lib/src/bridge/device_connection_mixin.dart b/lib/src/bridge/device_connection_mixin.dart index 55aa17b9..5b28044c 100644 --- a/lib/src/bridge/device_connection_mixin.dart +++ b/lib/src/bridge/device_connection_mixin.dart @@ -7,20 +7,41 @@ mixin DeviceConnectionMixin on FlutterBLE { Future connectToPeripheral(String deviceIdentifier, bool isAutoConnect, int requestMtu, bool refreshGatt, Duration timeout) async { - return await _methodChannel - .invokeMethod(MethodName.connectToDevice, { - ArgumentName.deviceIdentifier: deviceIdentifier, - ArgumentName.isAutoConnect: isAutoConnect, - ArgumentName.requestMtu: requestMtu, - ArgumentName.refreshGatt: refreshGatt, - ArgumentName.timeoutMillis: timeout?.inMilliseconds - }).catchError((errorJson) => - Future.error(BleError.fromJson(jsonDecode(errorJson.details)))); + return await _methodChannel.invokeMethod( + MethodName.connectToDevice, + { + ArgumentName.deviceIdentifier: deviceIdentifier, + ArgumentName.isAutoConnect: isAutoConnect, + ArgumentName.requestMtu: requestMtu, + ArgumentName.refreshGatt: refreshGatt, + ArgumentName.timeoutMillis: timeout?.inMilliseconds + }, + ).catchError( + (errorJson) => Future.error( + BleError.fromJson(jsonDecode(errorJson.details)), + ), + ); } Stream observePeripheralConnectionState( - String identifier, bool emitCurrentValue) async* { - yield* _peripheralConnectionStateChanges + String identifier, bool emitCurrentValue) { + var controller = StreamController( + onListen: () => _methodChannel.invokeMethod( + MethodName.observeConnectionState, + { + ArgumentName.deviceIdentifier: identifier, + ArgumentName.emitCurrentValue: emitCurrentValue, + }, + ).catchError( + (errorJson) => throw BleError.fromJson(jsonDecode(errorJson.details)), + ), + ); + + controller + .addStream(_peripheralConnectionStateChanges) + .then((value) => controller?.close()); + + return controller.stream .map((jsonString) => ConnectionStateContainer.fromJson(jsonDecode(jsonString))) .where((connectionStateContainer) => @@ -39,24 +60,21 @@ mixin DeviceConnectionMixin on FlutterBLE { return PeripheralConnectionState.disconnecting; default: throw FormatException( - "Unrecognized value of device connection state. Value: $connectionStateString"); + 'Unrecognized value of device connection state. Value: $connectionStateString', + ); } }); - - _methodChannel.invokeMethod( - MethodName.observeConnectionState, { - ArgumentName.deviceIdentifier: identifier, - ArgumentName.emitCurrentValue: emitCurrentValue, - }).catchError( - (errorJson) => throw BleError.fromJson(jsonDecode(errorJson.details))); } Future isPeripheralConnected(String peripheralIdentifier) async { return await _methodChannel .invokeMethod(MethodName.isDeviceConnected, { ArgumentName.deviceIdentifier: peripheralIdentifier, - }).catchError((errorJson) => - Future.error(BleError.fromJson(jsonDecode(errorJson.details)))); + }).catchError( + (errorJson) => Future.error( + BleError.fromJson(jsonDecode(errorJson.details)), + ), + ); } Future disconnectOrCancelPeripheralConnection( @@ -64,7 +82,10 @@ mixin DeviceConnectionMixin on FlutterBLE { return await _methodChannel .invokeMethod(MethodName.cancelConnection, { ArgumentName.deviceIdentifier: peripheralIdentifier, - }).catchError((errorJson) => - Future.error(BleError.fromJson(jsonDecode(errorJson.details)))); + }).catchError( + (errorJson) => Future.error( + BleError.fromJson(jsonDecode(errorJson.details)), + ), + ); } } diff --git a/pubspec.yaml b/pubspec.yaml index beb0c30c..cae815a4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: dev_dependencies: test: ^1.5.3 mockito: ^4.0.0 + pedantic: ^1.9.0 flutter_test: sdk: flutter From 8d97bf57c085d7fd60623a8b2f185858bb05e230 Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Fri, 17 Jul 2020 15:42:05 +0200 Subject: [PATCH 03/31] Bump version to 2.2.7 Updated changelog. --- CHANGELOG.md | 4 ++++ android/build.gradle | 2 +- ios/flutter_ble_lib.podspec | 2 +- pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36a34e29..1f390558 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.7 + +* Minor code style fixes. Adjusted device connection state monitoring. + ## 2.2.6 * Fixed scan quick failures not being reported to the listener (race condition in scanning_mixin.dart) diff --git a/android/build.gradle b/android/build.gradle index a397b0eb..631b5d2c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ group 'com.polidea.flutter_ble_lib' -version '2.2.6' +version '2.2.7' buildscript { repositories { diff --git a/ios/flutter_ble_lib.podspec b/ios/flutter_ble_lib.podspec index 51f47c01..85afd3e1 100644 --- a/ios/flutter_ble_lib.podspec +++ b/ios/flutter_ble_lib.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_ble_lib' - s.version = '2.2.6' + s.version = '2.2.7' s.summary = 'A new flutter plugin project.' s.description = <<-DESC A new flutter plugin project. diff --git a/pubspec.yaml b/pubspec.yaml index cae815a4..2530c4ef 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_ble_lib description: FlutterBle Library is a flutter library that supports BLE operations. It uses MultiPlatformBleAdapter as a native backend.. -version: 2.2.6 +version: 2.2.7 homepage: https://github.com/Polidea/FlutterBleLib environment: From a6a824dcf5bd6427aae7e0c72cb73d62d35b2f26 Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Fri, 17 Jul 2020 16:41:05 +0200 Subject: [PATCH 04/31] Formatted all files according to `dartfmt` --- .../device_details/device_detail_view.dart | 23 +++++++++------- .../device_details/view/auto_test_view.dart | 6 +---- .../lib/device_details/view/button_view.dart | 6 ++--- .../view/logs_container_view.dart | 2 -- .../device_details/view/manual_test_view.dart | 7 ++--- example/lib/main.dart | 26 +++++++++---------- example/lib/repository/device_repository.dart | 10 +++---- example/lib/test_scenarios/base.dart | 2 +- .../bluetooth_state_toggle_scenario.dart | 3 ++- .../sensor_tag_test_scenario.dart | 26 ++++++++++--------- .../lib/test_scenarios/test_scenarios.dart | 2 +- example/lib/util/pair.dart | 2 +- example/test/widget_test.dart | 4 +-- lib/descriptor.dart | 20 +++++++------- test/ble_manager_test.dart | 3 +-- test/mock/mocks.dart | 3 ++- test/scan_result.dart | 1 - test/service_test.dart | 12 +++++---- .../util/transcation_id_generator_test.dart | 2 +- 19 files changed, 79 insertions(+), 81 deletions(-) diff --git a/example/lib/device_details/device_detail_view.dart b/example/lib/device_details/device_detail_view.dart index 6d90a9fa..dc8948a7 100644 --- a/example/lib/device_details/device_detail_view.dart +++ b/example/lib/device_details/device_detail_view.dart @@ -10,7 +10,6 @@ import 'package:flutter_ble_lib_example/device_details/view/manual_test_view.dar class DeviceDetailsView extends StatefulWidget { @override State createState() => DeviceDetailsViewState(); - } class DeviceDetailsViewState extends State { @@ -37,12 +36,12 @@ class DeviceDetailsViewState extends State { _deviceDetailsBloc.init(); _appStateSubscription = _deviceDetailsBloc.disconnectedDevice.listen((bleDevice) async { - Fimber.d("navigate to details"); - _onPause(); - Navigator.pop(context); - _shouldRunOnResume = true; - Fimber.d("back from details"); - }); + Fimber.d("navigate to details"); + _onPause(); + Navigator.pop(context); + _shouldRunOnResume = true; + Fimber.d("back from details"); + }); } void _onPause() { @@ -74,8 +73,14 @@ class DeviceDetailsViewState extends State { title: Text('Device Details'), bottom: TabBar( tabs: [ - Tab(icon: Icon(Icons.autorenew), text: "Automatic",), - Tab(icon: Icon(Icons.settings), text: "Manual",), + Tab( + icon: Icon(Icons.autorenew), + text: "Automatic", + ), + Tab( + icon: Icon(Icons.settings), + text: "Manual", + ), ], ), ), diff --git a/example/lib/device_details/view/auto_test_view.dart b/example/lib/device_details/view/auto_test_view.dart index a8f40914..5717a11d 100644 --- a/example/lib/device_details/view/auto_test_view.dart +++ b/example/lib/device_details/view/auto_test_view.dart @@ -1,12 +1,9 @@ - import 'package:flutter/material.dart'; import 'package:flutter_ble_lib_example/device_details/device_details_bloc.dart'; import 'package:flutter_ble_lib_example/device_details/view/button_view.dart'; import 'package:flutter_ble_lib_example/device_details/view/logs_container_view.dart'; - class AutoTestView extends StatelessWidget { - final DeviceDetailsBloc _deviceDetailsBloc; AutoTestView(this._deviceDetailsBloc); @@ -41,5 +38,4 @@ class AutoTestView extends StatelessWidget { void _startAutoTest() { _deviceDetailsBloc.startAutoTest(); } - -} \ No newline at end of file +} diff --git a/example/lib/device_details/view/button_view.dart b/example/lib/device_details/view/button_view.dart index d795fa3f..f9789f61 100644 --- a/example/lib/device_details/view/button_view.dart +++ b/example/lib/device_details/view/button_view.dart @@ -1,12 +1,10 @@ - import 'package:flutter/material.dart'; class ButtonView extends StatelessWidget { - final String _text; final Function action; - ButtonView(this._text, { this.action }); + ButtonView(this._text, {this.action}); @override Widget build(BuildContext context) { @@ -22,4 +20,4 @@ class ButtonView extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/example/lib/device_details/view/logs_container_view.dart b/example/lib/device_details/view/logs_container_view.dart index dcd89dc4..37398d9f 100644 --- a/example/lib/device_details/view/logs_container_view.dart +++ b/example/lib/device_details/view/logs_container_view.dart @@ -1,10 +1,8 @@ - import 'package:flutter/material.dart'; import 'package:flutter_ble_lib_example/device_details/device_details_bloc.dart'; import 'package:rxdart/rxdart.dart'; class LogsContainerView extends StatelessWidget { - final Observable> _logs; LogsContainerView(this._logs); diff --git a/example/lib/device_details/view/manual_test_view.dart b/example/lib/device_details/view/manual_test_view.dart index f3966180..a01120ce 100644 --- a/example/lib/device_details/view/manual_test_view.dart +++ b/example/lib/device_details/view/manual_test_view.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_ble_lib_example/device_details/device_details_bloc.dart'; import 'package:flutter_ble_lib_example/device_details/view/button_view.dart'; @@ -173,8 +172,10 @@ class ManualTestView extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 2.0), child: Row( children: [ - ButtonView("Monitor temp", action: _monitorCharacteristicForPeripheral), - ButtonView("Turn on temp", action: _writeCharacteristicForPeripheral), + ButtonView("Monitor temp", + action: _monitorCharacteristicForPeripheral), + ButtonView("Turn on temp", + action: _writeCharacteristicForPeripheral), ButtonView("Read temp", action: _readCharacteristicForPeripheral), ], ), diff --git a/example/lib/main.dart b/example/lib/main.dart index f5015a3b..25005b03 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -14,21 +14,21 @@ void main() { final RouteObserver routeObserver = RouteObserver(); class MyApp extends StatelessWidget { - @override Widget build(BuildContext context) { return MaterialApp( - title: 'FlutterBleLib example', - theme: new ThemeData( - primaryColor: new Color(0xFF0A3D91), - accentColor: new Color(0xFFCC0000), - ), - initialRoute: "/", - routes: { - "/": (context) => DevicesBlocProvider(child: DevicesListScreen()), - "/details": (context) => DeviceDetailsBlocProvider(child: DeviceDetailsView()), - }, - navigatorObservers: [routeObserver], - ); + title: 'FlutterBleLib example', + theme: new ThemeData( + primaryColor: new Color(0xFF0A3D91), + accentColor: new Color(0xFFCC0000), + ), + initialRoute: "/", + routes: { + "/": (context) => DevicesBlocProvider(child: DevicesListScreen()), + "/details": (context) => + DeviceDetailsBlocProvider(child: DeviceDetailsView()), + }, + navigatorObservers: [routeObserver], + ); } } diff --git a/example/lib/repository/device_repository.dart b/example/lib/repository/device_repository.dart index 8627fa3f..b9fa1978 100644 --- a/example/lib/repository/device_repository.dart +++ b/example/lib/repository/device_repository.dart @@ -1,16 +1,14 @@ - import 'package:flutter_ble_lib_example/model/ble_device.dart'; import 'package:rxdart/rxdart.dart'; class MissingPickedDeviceException implements Exception {} class DeviceRepository { - static BleDevice _bleDevice; BehaviorSubject _deviceController; - static final DeviceRepository _deviceRepository = DeviceRepository - ._internal(); + static final DeviceRepository _deviceRepository = + DeviceRepository._internal(); factory DeviceRepository() { return _deviceRepository; @@ -25,8 +23,8 @@ class DeviceRepository { _deviceController.add(_bleDevice); } - ValueObservable get pickedDevice => _deviceController.stream.shareValueSeeded(_bleDevice); + ValueObservable get pickedDevice => + _deviceController.stream.shareValueSeeded(_bleDevice); bool get hasPickedDevice => _bleDevice != null; - } diff --git a/example/lib/test_scenarios/base.dart b/example/lib/test_scenarios/base.dart index 2aa90e04..fd240fd7 100644 --- a/example/lib/test_scenarios/base.dart +++ b/example/lib/test_scenarios/base.dart @@ -4,4 +4,4 @@ typedef Logger = Function(String); abstract class TestScenario { Future runTestScenario(Logger log, Logger errorLogger); -} \ No newline at end of file +} diff --git a/example/lib/test_scenarios/bluetooth_state_toggle_scenario.dart b/example/lib/test_scenarios/bluetooth_state_toggle_scenario.dart index 2eb45763..043582c8 100644 --- a/example/lib/test_scenarios/bluetooth_state_toggle_scenario.dart +++ b/example/lib/test_scenarios/bluetooth_state_toggle_scenario.dart @@ -46,7 +46,8 @@ class BluetoothStateTestScenario implements TestScenario { void _observeRadioState(BleManager bleManager, Logger log) async { await _radioStateSubscription?.cancel(); - _radioStateSubscription = bleManager.observeBluetoothState().listen((newState) { + _radioStateSubscription = + bleManager.observeBluetoothState().listen((newState) { log("New radio state: $newState"); }, onError: (error) { log("Error while observing radio state. Error: $error"); diff --git a/example/lib/test_scenarios/sensor_tag_test_scenario.dart b/example/lib/test_scenarios/sensor_tag_test_scenario.dart index c1151c28..9ce95c4b 100644 --- a/example/lib/test_scenarios/sensor_tag_test_scenario.dart +++ b/example/lib/test_scenarios/sensor_tag_test_scenario.dart @@ -3,32 +3,34 @@ part of test_scenarios; class SensorTagTestScenario { PeripheralTestOperations _peripheralTestOperations; - SensorTagTestScenario(BleManager bleManager, - Peripheral peripheral, - Logger log, - Logger logError) { - _peripheralTestOperations = PeripheralTestOperations(bleManager, peripheral, log, logError); + SensorTagTestScenario(BleManager bleManager, Peripheral peripheral, + Logger log, Logger logError) { + _peripheralTestOperations = + PeripheralTestOperations(bleManager, peripheral, log, logError); } Future runTestScenario() async { - _peripheralTestOperations.connect() + _peripheralTestOperations + .connect() .then((_) => _peripheralTestOperations.cancelTransaction()) .then((_) => _peripheralTestOperations.discovery()) .then((_) => _peripheralTestOperations.testRequestingMtu()) .then((_) => _peripheralTestOperations.testReadingRssi()) - .then((_) => _peripheralTestOperations.readWriteMonitorCharacteristicForPeripheral()) - .then((_) => _peripheralTestOperations.readWriteMonitorCharacteristicForService()) + .then((_) => _peripheralTestOperations + .readWriteMonitorCharacteristicForPeripheral()) + .then((_) => _peripheralTestOperations + .readWriteMonitorCharacteristicForService()) .then((_) => _peripheralTestOperations.readWriteMonitorCharacteristic()) .then((_) => Future.delayed(Duration(milliseconds: 100))) - .then((_) => _peripheralTestOperations.readWriteDescriptorForPeripheral()) + .then( + (_) => _peripheralTestOperations.readWriteDescriptorForPeripheral()) .then((_) => _peripheralTestOperations.readWriteDescriptorForService()) - .then((_) => _peripheralTestOperations.readWriteDescriptorForCharacteristic()) + .then((_) => + _peripheralTestOperations.readWriteDescriptorForCharacteristic()) .then((_) => _peripheralTestOperations.readWriteDescriptor()) .then((_) => _peripheralTestOperations.fetchConnectedDevice()) .then((_) => _peripheralTestOperations.fetchKnownDevice()) .then((_) => _peripheralTestOperations.disconnect()) .catchError((error) => _peripheralTestOperations.logError(error)); } - - } diff --git a/example/lib/test_scenarios/test_scenarios.dart b/example/lib/test_scenarios/test_scenarios.dart index 076b4bfe..e79ba77b 100644 --- a/example/lib/test_scenarios/test_scenarios.dart +++ b/example/lib/test_scenarios/test_scenarios.dart @@ -12,4 +12,4 @@ part 'base.dart'; part 'sensor_tag_test_with_scan_and_connection_scenario.dart'; part 'bluetooth_state_toggle_scenario.dart'; part 'sensor_tag_test_scenario.dart'; -part 'peripheral_test_operations.dart'; \ No newline at end of file +part 'peripheral_test_operations.dart'; diff --git a/example/lib/util/pair.dart b/example/lib/util/pair.dart index 5ee17510..4782b7c2 100644 --- a/example/lib/util/pair.dart +++ b/example/lib/util/pair.dart @@ -6,4 +6,4 @@ class Pair { @override String toString() => 'Pair[$first, $second]'; -} \ No newline at end of file +} diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index 4f97d538..def6a68d 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -18,8 +18,8 @@ void main() { // Verify that platform version is retrieved. expect( find.byWidgetPredicate( - (Widget widget) => widget is Text && - widget.data.startsWith('Running on:'), + (Widget widget) => + widget is Text && widget.data.startsWith('Running on:'), ), findsOneWidget, ); diff --git a/lib/descriptor.dart b/lib/descriptor.dart index 5e2e265a..6e43cee5 100644 --- a/lib/descriptor.dart +++ b/lib/descriptor.dart @@ -1,9 +1,9 @@ part of flutter_ble_lib; abstract class _DescriptorMetadata { - static const String uuid = "descriptorUuid"; - static const String id = "descriptorId"; - static const String value = "value"; + static const String uuid = 'descriptorUuid'; + static const String id = 'descriptorId'; + static const String value = 'value'; } class Descriptor extends InternalDescriptor { @@ -37,17 +37,15 @@ class Descriptor extends InternalDescriptor { @override bool operator ==(Object other) => identical(this, other) || - other is Descriptor && - runtimeType == other.runtimeType && - _manager == other._manager && - characteristic == other.characteristic && - uuid == other.uuid; + other is Descriptor && + runtimeType == other.runtimeType && + _manager == other._manager && + characteristic == other.characteristic && + uuid == other.uuid; @override int get hashCode => - _manager.hashCode ^ - characteristic.hashCode ^ - uuid.hashCode; + _manager.hashCode ^ characteristic.hashCode ^ uuid.hashCode; } class DescriptorWithValue extends Descriptor with WithValue { diff --git a/test/ble_manager_test.dart b/test/ble_manager_test.dart index e4470b31..f4c2c86a 100644 --- a/test/ble_manager_test.dart +++ b/test/ble_manager_test.dart @@ -1,4 +1,3 @@ - import 'package:flutter_ble_lib/flutter_ble_lib.dart'; import 'package:test/test.dart'; @@ -13,4 +12,4 @@ void main() { //then expect(secondInstanceOfBleManager, same(firstInstanceOfBlemanager)); }); -} \ No newline at end of file +} diff --git a/test/mock/mocks.dart b/test/mock/mocks.dart index 9d342fad..0bfb4388 100644 --- a/test/mock/mocks.dart +++ b/test/mock/mocks.dart @@ -4,7 +4,8 @@ import 'package:mockito/mockito.dart'; class ManagerForServiceMock extends Mock implements ManagerForService {} -class ManagerForCharacteristicMock extends Mock implements ManagerForCharacteristic {} +class ManagerForCharacteristicMock extends Mock + implements ManagerForCharacteristic {} class ManagerForDescriptorMock extends Mock implements ManagerForDescriptor {} diff --git a/test/scan_result.dart b/test/scan_result.dart index 75a41cc2..12365e0c 100644 --- a/test/scan_result.dart +++ b/test/scan_result.dart @@ -66,7 +66,6 @@ String _createJsonScanResult({ int txPowerLevel, List solicitedServiceUuids, }) { - String serializedManufacturerData; if (manufacturerData != null) { serializedManufacturerData = "\"${base64Encode(manufacturerData)}\""; diff --git a/test/service_test.dart b/test/service_test.dart index 10ead0e7..1c70e155 100644 --- a/test/service_test.dart +++ b/test/service_test.dart @@ -63,14 +63,16 @@ void main() { ])); }); - test("descriptorsForCharacteristic returns characteristics provided by manager", () async { + test( + "descriptorsForCharacteristic returns characteristics provided by manager", + () async { //given when(managerForService.descriptorsForService(service, "123")) .thenAnswer((_) => Future.value([ - createDescriptor(0), - createDescriptor(1), - createDescriptor(2), - ])); + createDescriptor(0), + createDescriptor(1), + createDescriptor(2), + ])); //when var descriptors = await service.descriptorsForCharacteristic("123"); diff --git a/test/src/util/transcation_id_generator_test.dart b/test/src/util/transcation_id_generator_test.dart index efa66cd9..6d93a5af 100644 --- a/test/src/util/transcation_id_generator_test.dart +++ b/test/src/util/transcation_id_generator_test.dart @@ -14,4 +14,4 @@ void main() { generatedIds.add(generatedId); } }); -} \ No newline at end of file +} From 6f05b2fb3cfbeaee260f57d6106fc9ca0f878dca Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Fri, 17 Jul 2020 16:52:14 +0200 Subject: [PATCH 05/31] Bump version to 2.2.8 Updated changelog. --- CHANGELOG.md | 4 ++++ android/build.gradle | 2 +- ios/flutter_ble_lib.podspec | 2 +- pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f390558..2728ee14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.8 + +* Formatted all sources according to dartfmt for consistency + ## 2.2.7 * Minor code style fixes. Adjusted device connection state monitoring. diff --git a/android/build.gradle b/android/build.gradle index 631b5d2c..3b20cada 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ group 'com.polidea.flutter_ble_lib' -version '2.2.7' +version '2.2.8' buildscript { repositories { diff --git a/ios/flutter_ble_lib.podspec b/ios/flutter_ble_lib.podspec index 85afd3e1..a5eeae54 100644 --- a/ios/flutter_ble_lib.podspec +++ b/ios/flutter_ble_lib.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_ble_lib' - s.version = '2.2.7' + s.version = '2.2.8' s.summary = 'A new flutter plugin project.' s.description = <<-DESC A new flutter plugin project. diff --git a/pubspec.yaml b/pubspec.yaml index 2530c4ef..e6463a63 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_ble_lib description: FlutterBle Library is a flutter library that supports BLE operations. It uses MultiPlatformBleAdapter as a native backend.. -version: 2.2.7 +version: 2.2.8 homepage: https://github.com/Polidea/FlutterBleLib environment: From b0e241c6dce5e5ca17160f7a94c36dd7ac37dd59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kojdecki?= Date: Wed, 22 Jul 2020 16:06:58 +0200 Subject: [PATCH 06/31] Fix observing connection state (#500) --- lib/src/bridge/device_connection_mixin.dart | 32 ++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/lib/src/bridge/device_connection_mixin.dart b/lib/src/bridge/device_connection_mixin.dart index 5b28044c..f8a9061a 100644 --- a/lib/src/bridge/device_connection_mixin.dart +++ b/lib/src/bridge/device_connection_mixin.dart @@ -25,7 +25,7 @@ mixin DeviceConnectionMixin on FlutterBLE { Stream observePeripheralConnectionState( String identifier, bool emitCurrentValue) { - var controller = StreamController( + var controller = StreamController( onListen: () => _methodChannel.invokeMethod( MethodName.observeConnectionState, { @@ -37,18 +37,15 @@ mixin DeviceConnectionMixin on FlutterBLE { ), ); - controller - .addStream(_peripheralConnectionStateChanges) - .then((value) => controller?.close()); - - return controller.stream - .map((jsonString) => - ConnectionStateContainer.fromJson(jsonDecode(jsonString))) - .where((connectionStateContainer) => - connectionStateContainer.peripheralIdentifier == identifier) - .map((connectionStateContainer) => - connectionStateContainer.connectionState) - .map((connectionStateString) { + var sourceStream = + _peripheralConnectionStateChanges + .map((jsonString) => + ConnectionStateContainer.fromJson(jsonDecode(jsonString))) + .where((connectionStateContainer) => + connectionStateContainer.peripheralIdentifier == identifier) + .map((connectionStateContainer) => + connectionStateContainer.connectionState) + .map((connectionStateString) { switch (connectionStateString.toLowerCase()) { case NativeConnectionState.connected: return PeripheralConnectionState.connected; @@ -64,6 +61,15 @@ mixin DeviceConnectionMixin on FlutterBLE { ); } }); + + controller + .addStream( + sourceStream, + cancelOnError: true, + ) + .then((value) => controller?.close()); + + return controller.stream; } Future isPeripheralConnected(String peripheralIdentifier) async { From a49ee78daa71c736a48d5e37db3fe915ddfa5e13 Mon Sep 17 00:00:00 2001 From: Bartosz Wilk Date: Fri, 24 Jul 2020 18:30:21 +0200 Subject: [PATCH 07/31] Characteristic unit tests (#444) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [characteristic] add: tests * Apply suggestions from code review Co-authored-by: Mikołaj Kojdecki * Change transactionIds to adhere to library's rule about them * Fix imports Co-authored-by: Mikołaj Kojdecki --- test/characteristic_test.dart | 355 ++++++++++++++++++++++++++++++++++ test/service_test.dart | 46 ++--- 2 files changed, 378 insertions(+), 23 deletions(-) create mode 100644 test/characteristic_test.dart diff --git a/test/characteristic_test.dart b/test/characteristic_test.dart new file mode 100644 index 00000000..543e0a13 --- /dev/null +++ b/test/characteristic_test.dart @@ -0,0 +1,355 @@ +import 'dart:async'; +import 'dart:developer'; +import 'dart:typed_data'; + +import 'package:flutter_ble_lib/flutter_ble_lib.dart'; +import 'package:flutter_ble_lib/src/_managers_for_classes.dart'; +import 'package:mockito/mockito.dart'; +import 'package:test/test.dart'; + +import 'mock/mocks.dart'; +import 'test_util/characteristic_generator.dart'; +import 'test_util/descriptor_generator.dart'; + +class ServiceMock extends Mock implements Service {} + +void main() { + Peripheral peripheral = PeripheralMock(); + ManagerForCharacteristic managerForCharacteristic = + ManagerForCharacteristicMock(); + CharacteristicGenerator characteristicGenerator = + CharacteristicGenerator(managerForCharacteristic); + DescriptorGenerator descriptorGenerator = + DescriptorGenerator(ManagerForDescriptorMock()); + + Characteristic characteristic = + characteristicGenerator.create(123, ServiceMock()); + + DescriptorWithValue createDescriptor(int seed) => + descriptorGenerator.create(seed, characteristic); + + tearDown(() { + [ + peripheral, + managerForCharacteristic, + ].forEach(clearInteractions); + }); + + test("descriptors returns a list of descriptors provided by manager", () async { + //given + when(managerForCharacteristic.descriptorsForCharacteristic(characteristic)) + .thenAnswer((_) => Future.value([ + createDescriptor(0), + createDescriptor(1), + createDescriptor(2), + ])); + + //when + var descriptors = await characteristic.descriptors(); + + //then + expect( + descriptors, + equals([ + createDescriptor(0), + createDescriptor(1), + createDescriptor(2), + ])); + }); + + test("read returns expected value", () async { + //given + when(managerForCharacteristic.readCharacteristicForIdentifier( + any, characteristic, "a123")) + .thenAnswer((_) => Future.value(Uint8List.fromList([1, 2, 3, 4]))); + + //when + var value = await characteristic.read(transactionId: "a123"); + + //then + expect(value, equals(Uint8List.fromList([1, 2, 3, 4]))); + }); + + test( + "read invokes manager with expected params when transactionId is specified", + () { + //when + characteristic.read(transactionId: "a123"); + + //then + verify( + managerForCharacteristic.readCharacteristicForIdentifier( + any, characteristic, "a123"), + ); + }); + + test( + "read generates transactionId when it is not specified", + () { + //when + characteristic.read(); + + //then + verify( + managerForCharacteristic.readCharacteristicForIdentifier( + any, characteristic, argThat(isNotNull)), + ); + }); + + test( + "read generates unique transactionId for each operation", + () { + //when + characteristic.read(); + characteristic.read(); + + //then + var transactionIds = verify( + managerForCharacteristic.readCharacteristicForIdentifier( + any, characteristic, captureThat(isNotNull)), + ).captured; + expect(transactionIds[0], isNot(equals(transactionIds[1]))); + }); + + test( + "write invokes manager with expected params when transactionId is specified", + () { + //when + characteristic.write( + Uint8List.fromList([1, 2, 3, 4]), + false, + transactionId: "a456", + ); + + //then + verify( + managerForCharacteristic.writeCharacteristicForIdentifier( + any, characteristic, Uint8List.fromList([1, 2, 3, 4]), false, "a456"), + ); + }); + + test( + "write invokes manager with expected params when transactionId is not specified", + () { + //when + characteristic.write(Uint8List.fromList([1, 2, 3, 4]), false); + + //then + verify( + managerForCharacteristic.writeCharacteristicForIdentifier( + any, + characteristic, + Uint8List.fromList([1, 2, 3, 4]), + false, + argThat(isNotNull)), + ); + }); + + test( + "write invokes manager with unique transactionId when transactionId is not specified", + () { + //when + characteristic.write(Uint8List.fromList([1, 2, 3, 4]), false); + characteristic.write(Uint8List.fromList([1, 2, 3, 4]), false); + + //then + var transactionIds = verify( + managerForCharacteristic.writeCharacteristicForIdentifier( + any, + characteristic, + Uint8List.fromList([1, 2, 3, 4]), + false, + captureThat(isNotNull))) + .captured; + expect(transactionIds[0], isNot(equals(transactionIds[1]))); + }); + + test("monitor emits expected values", () { + //given + var streamController = StreamController(); + when(managerForCharacteristic.monitorCharacteristicForIdentifier( + any, characteristic, "a123")) + .thenAnswer((_) => streamController.stream); + + //when + var valuesNotifications = characteristic.monitor(transactionId: "a123"); + streamController.sink.add(Uint8List.fromList([1, 2, 3])); + streamController.sink.add(Uint8List.fromList([4, 5, 6])); + streamController.sink.add(Uint8List.fromList([7, 8, 9])); + streamController.close(); + + //then + expect( + valuesNotifications, + emitsInOrder([ + emits(equals(Uint8List.fromList([1, 2, 3]))), + emits(equals(Uint8List.fromList([4, 5, 6]))), + emits(equals(Uint8List.fromList([7, 8, 9]))), + emitsDone + ])); + }); + + test( + "monitor invokes manager with expected params when transactionId is specified", + () { + //when + characteristic.monitor(transactionId: "a123"); + + //then + verify( + managerForCharacteristic.monitorCharacteristicForIdentifier( + any, characteristic, "a123"), + ); + }); + + test( + "monitor invokes manager with expected params when transactionId is not specified", + () { + //when + characteristic.monitor(); + + //then + verify( + managerForCharacteristic.monitorCharacteristicForIdentifier( + any, characteristic, argThat(isNotNull)), + ); + }); + + test( + "monitor invokes manager with unique transactionId when transactionId is not specified", + () { + //when + characteristic.monitor(); + characteristic.monitor(); + + //then + var transactionIds = verify( + managerForCharacteristic.monitorCharacteristicForIdentifier( + any, characteristic, captureThat(isNotNull))) + .captured; + expect(transactionIds[0], isNot(equals(transactionIds[1]))); + }); + + test("readDescriptor returns expected descriptor", () async { + //given + when(managerForCharacteristic.readDescriptorForCharacteristic( + characteristic, "123", "a456")) + .thenAnswer((_) => Future.value(createDescriptor(0))); + + //when + var descriptor = + await characteristic.readDescriptor("123", transactionId: "a456"); + + //then + expect(descriptor, equals(createDescriptor(0))); + }); + + test( + "readDescriptor invokes manager with expected params when transactionId is specified", + () { + //when + characteristic.readDescriptor("123", transactionId: "a456"); + + //then + verify( + managerForCharacteristic.readDescriptorForCharacteristic( + characteristic, "123", "a456"), + ); + }); + + test( + "readDescriptor invokes manager with expected params when transactionId is not specified", + () { + //when + characteristic.readDescriptor("123", transactionId: "a456"); + + //then + verify( + managerForCharacteristic.readDescriptorForCharacteristic( + characteristic, "123", argThat(isNotNull)), + ); + }); + + test( + "readDescriptor invokes manager with unique transactionId when transactionId is not specified", + () { + //when + characteristic.readDescriptor("123"); + characteristic.readDescriptor("123"); + + //then + var transactionIds = verify( + managerForCharacteristic.readDescriptorForCharacteristic( + characteristic, "123", captureThat(isNotNull)), + ).captured; + expect(transactionIds[0], isNot(equals(transactionIds[1]))); + }); + + test("writeDescriptor returns expected descriptor", () async { + //given + when(managerForCharacteristic.writeDescriptorForCharacteristic( + characteristic, "123", Uint8List.fromList([1, 2, 3, 4]), "a456")) + .thenAnswer((_) => Future.value(createDescriptor(0))); + + //when + var descriptor = await characteristic.writeDescriptor( + "123", + Uint8List.fromList([1, 2, 3, 4]), + transactionId: "a456", + ); + + //then + expect(descriptor, equals(createDescriptor(0))); + }); + + test( + "writeDescriptor invokes manager with expected params when transactionId is specified", + () { + //when + characteristic.writeDescriptor( + "123", + Uint8List.fromList([1, 2, 3, 4]), + transactionId: "a456", + ); + + //then + verify( + managerForCharacteristic.writeDescriptorForCharacteristic( + characteristic, "123", Uint8List.fromList([1, 2, 3, 4]), "a456"), + ); + }); + + test( + "writeDescriptor invokes manager with expected params when transactionId is not specified", + () { + //when + characteristic.writeDescriptor("123", Uint8List.fromList([1, 2, 3, 4])); + //then + verify( + managerForCharacteristic.writeDescriptorForCharacteristic( + characteristic, + "123", + Uint8List.fromList([1, 2, 3, 4]), + argThat(isNotNull), + ), + ); + }); + + test( + "writeDescriptor invokes manager with unique transactionId when transactionId is not specified", + () { + //when + characteristic.writeDescriptor("123", Uint8List.fromList([1, 2, 3, 4])); + characteristic.writeDescriptor("123", Uint8List.fromList([1, 2, 3, 4])); + + //then + var transactionIds = verify( + managerForCharacteristic.writeDescriptorForCharacteristic( + characteristic, + "123", + Uint8List.fromList([1, 2, 3, 4]), + captureThat(isNotNull)), + ).captured; + expect(transactionIds[0], isNot(equals(transactionIds[1]))); + }); +} diff --git a/test/service_test.dart b/test/service_test.dart index 1c70e155..4bbf0fa7 100644 --- a/test/service_test.dart +++ b/test/service_test.dart @@ -90,12 +90,12 @@ void main() { test("readCharacteristic returns expected characteristic", () async { //given when(managerForService.readCharacteristicForService( - peripheral, service, "123", "456")) + peripheral, service, "123", "a456")) .thenAnswer((_) => Future.value(createCharacteristic(0))); //when var characteristic = - await service.readCharacteristic("123", transactionId: "456"); + await service.readCharacteristic("123", transactionId: "a456"); //then expect(characteristic, equals(createCharacteristic(0))); @@ -103,12 +103,12 @@ void main() { test("readCharacteristic reads characteristic using manager", () { //when - service.readCharacteristic("123", transactionId: "456"); + service.readCharacteristic("123", transactionId: "a456"); //then verify( managerForService.readCharacteristicForService( - peripheral, service, "123", "456"), + peripheral, service, "123", "a456"), ); }); @@ -128,24 +128,24 @@ void main() { test("readDescriptor returns expected descriptor", () async { //given when(managerForService.readDescriptorForService( - service, "123", "456", "789")) + service, "123", "456", "a789")) .thenAnswer((_) => Future.value(createDescriptor(0))); //when var characteristic = - await service.readDescriptor("123", "456", transactionId: "789"); + await service.readDescriptor("123", "456", transactionId: "a789"); //then expect(characteristic, equals(createDescriptor(0))); }); - test("readDescriptor reads characteristic using manager", () { + test("readDescriptor reads descriptor using manager", () { //when - service.readDescriptor("123", "456", transactionId: "789"); + service.readDescriptor("123", "456", transactionId: "a789"); //then verify( - managerForService.readDescriptorForService(service, "123", "456", "789"), + managerForService.readDescriptorForService(service, "123", "456", "a789"), ); }); @@ -165,7 +165,7 @@ void main() { test("writeCharacteristic returns expected characteristic", () async { //given when(managerForService.writeCharacteristicForService(peripheral, service, - "123", Uint8List.fromList([1, 2, 3, 4]), false, "456")) + "123", Uint8List.fromList([1, 2, 3, 4]), false, "a456")) .thenAnswer((_) => Future.value(createCharacteristic(0))); //when @@ -173,7 +173,7 @@ void main() { "123", Uint8List.fromList([1, 2, 3, 4]), false, - transactionId: "456", + transactionId: "a456", ); //then @@ -183,12 +183,12 @@ void main() { test("writeCharacteristic writes characteristic using manager", () { //when service.writeCharacteristic("123", Uint8List.fromList([1, 2, 3, 4]), false, - transactionId: "456"); + transactionId: "a456"); //then verify( managerForService.writeCharacteristicForService(peripheral, service, - "123", Uint8List.fromList([1, 2, 3, 4]), false, "456"), + "123", Uint8List.fromList([1, 2, 3, 4]), false, "a456"), ); }); @@ -209,27 +209,27 @@ void main() { test("writeDescriptor returns expected descriptor", () async { //given when(managerForService.writeDescriptorForService( - service, "123", "456", Uint8List.fromList([1, 2, 3, 4]), "789")) + service, "123", "456", Uint8List.fromList([1, 2, 3, 4]), "a789")) .thenAnswer((_) => Future.value(createDescriptor(0))); //when - var characteristic = await service.writeDescriptor( + var descriptor = await service.writeDescriptor( "123", "456", Uint8List.fromList([1, 2, 3, 4]), - transactionId: "789"); + transactionId: "a789"); //then - expect(characteristic, equals(createDescriptor(0))); + expect(descriptor, equals(createDescriptor(0))); }); test("writeDescriptor writes descriptor using manager", () { //when service.writeDescriptor("123", "456", Uint8List.fromList([1, 2, 3, 4]), - transactionId: "789"); + transactionId: "a789"); //then verify( managerForService.writeDescriptorForService( - service, "123", "456", Uint8List.fromList([1, 2, 3, 4]), "789"), + service, "123", "456", Uint8List.fromList([1, 2, 3, 4]), "a789"), ); }); @@ -251,12 +251,12 @@ void main() { //given var streamController = StreamController(); when(managerForService.monitorCharacteristicForService( - peripheral, service, "123", "456")) + peripheral, service, "123", "a456")) .thenAnswer((_) => streamController.stream); //when var characteristicNotifications = - service.monitorCharacteristic("123", transactionId: "456"); + service.monitorCharacteristic("123", transactionId: "a456"); streamController.sink.add(createCharacteristic(0)); streamController.sink.add(createCharacteristic(1)); streamController.sink.add(createCharacteristic(2)); @@ -275,12 +275,12 @@ void main() { test("monitorCharacteristic monitors characteristic using manager", () { //when - service.monitorCharacteristic("123", transactionId: "456"); + service.monitorCharacteristic("123", transactionId: "a456"); //then verify( managerForService.monitorCharacteristicForService( - peripheral, service, "123", "456"), + peripheral, service, "123", "a456"), ); }); From 832e18ce991d26671602e5a63f90924d5a091b30 Mon Sep 17 00:00:00 2001 From: Mikolaj Kojdecki Date: Fri, 24 Jul 2020 18:00:59 +0200 Subject: [PATCH 08/31] Release 2.2.9 --- CHANGELOG.md | 4 ++++ android/build.gradle | 2 +- ios/flutter_ble_lib.podspec | 2 +- pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2728ee14..27e33b80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.9 + +* Fixed issue with incorrectly typed Stream + ## 2.2.8 * Formatted all sources according to dartfmt for consistency diff --git a/android/build.gradle b/android/build.gradle index 3b20cada..a1875ce4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ group 'com.polidea.flutter_ble_lib' -version '2.2.8' +version '2.2.9' buildscript { repositories { diff --git a/ios/flutter_ble_lib.podspec b/ios/flutter_ble_lib.podspec index a5eeae54..2fe4a3c8 100644 --- a/ios/flutter_ble_lib.podspec +++ b/ios/flutter_ble_lib.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_ble_lib' - s.version = '2.2.8' + s.version = '2.2.9' s.summary = 'A new flutter plugin project.' s.description = <<-DESC A new flutter plugin project. diff --git a/pubspec.yaml b/pubspec.yaml index e6463a63..5c5565b4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_ble_lib description: FlutterBle Library is a flutter library that supports BLE operations. It uses MultiPlatformBleAdapter as a native backend.. -version: 2.2.8 +version: 2.2.9 homepage: https://github.com/Polidea/FlutterBleLib environment: From 7aa00dfdcdbd6b9bb29fbf5bb680e91e25f29996 Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Wed, 12 Aug 2020 21:53:54 +0200 Subject: [PATCH 09/31] Fixed formatting. --- lib/src/bridge/device_connection_mixin.dart | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/src/bridge/device_connection_mixin.dart b/lib/src/bridge/device_connection_mixin.dart index f8a9061a..c15ef44f 100644 --- a/lib/src/bridge/device_connection_mixin.dart +++ b/lib/src/bridge/device_connection_mixin.dart @@ -37,15 +37,14 @@ mixin DeviceConnectionMixin on FlutterBLE { ), ); - var sourceStream = - _peripheralConnectionStateChanges - .map((jsonString) => - ConnectionStateContainer.fromJson(jsonDecode(jsonString))) - .where((connectionStateContainer) => - connectionStateContainer.peripheralIdentifier == identifier) - .map((connectionStateContainer) => - connectionStateContainer.connectionState) - .map((connectionStateString) { + var sourceStream = _peripheralConnectionStateChanges + .map((jsonString) => + ConnectionStateContainer.fromJson(jsonDecode(jsonString))) + .where((connectionStateContainer) => + connectionStateContainer.peripheralIdentifier == identifier) + .map((connectionStateContainer) => + connectionStateContainer.connectionState) + .map((connectionStateString) { switch (connectionStateString.toLowerCase()) { case NativeConnectionState.connected: return PeripheralConnectionState.connected; From 6d75345c1d922931d0da0e876f29e057190e2512 Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Wed, 19 Aug 2020 15:48:48 +0200 Subject: [PATCH 10/31] =?UTF-8?q?Added=20=E2=80=9CDart/Flutter=20Package?= =?UTF-8?q?=20Analyzer=E2=80=9D=20github=20action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dart-flutter-package-analyzer.yml | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/dart-flutter-package-analyzer.yml diff --git a/.github/workflows/dart-flutter-package-analyzer.yml b/.github/workflows/dart-flutter-package-analyzer.yml new file mode 100644 index 00000000..88e03592 --- /dev/null +++ b/.github/workflows/dart-flutter-package-analyzer.yml @@ -0,0 +1,29 @@ +name: Dart/Flutter Package Analyzer +on: [push, pull_request] + +jobs: + + package-analysis: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - uses: axel-op/dart-package-analyzer@v2 + id: analysis # set an id for the current step + with: + githubToken: ${{ secrets.GITHUB_TOKEN }} + + # You can then use this id to retrieve the outputs in the next steps. + # The following step shows how to exit the workflow with an error if a score is below 100: + - name: Check scores + # NB: "analysis" is the id set above. Replace it with the one you used if different. + run: | + MAINTENANCE_SCORE=${{ steps.analysis.outputs.maintenance }} + HEALTH_SCORE=${{ steps.analysis.outputs.health }} + if (( $(echo "$MAINTENANCE_SCORE < 100" | bc) )) || (( $(echo "$HEALTH_SCORE < 100" | bc) )) + then + echo "Scores are not both equal to 100" + exit 1 + fi From b959b0acfd8c2350d2ad5e573a1a224d6592484f Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Wed, 19 Aug 2020 15:56:38 +0200 Subject: [PATCH 11/31] Revert "Fixed formatting." This reverts commit 7aa00dfdcdbd6b9bb29fbf5bb680e91e25f29996. --- lib/src/bridge/device_connection_mixin.dart | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/src/bridge/device_connection_mixin.dart b/lib/src/bridge/device_connection_mixin.dart index c15ef44f..f8a9061a 100644 --- a/lib/src/bridge/device_connection_mixin.dart +++ b/lib/src/bridge/device_connection_mixin.dart @@ -37,14 +37,15 @@ mixin DeviceConnectionMixin on FlutterBLE { ), ); - var sourceStream = _peripheralConnectionStateChanges - .map((jsonString) => - ConnectionStateContainer.fromJson(jsonDecode(jsonString))) - .where((connectionStateContainer) => - connectionStateContainer.peripheralIdentifier == identifier) - .map((connectionStateContainer) => - connectionStateContainer.connectionState) - .map((connectionStateString) { + var sourceStream = + _peripheralConnectionStateChanges + .map((jsonString) => + ConnectionStateContainer.fromJson(jsonDecode(jsonString))) + .where((connectionStateContainer) => + connectionStateContainer.peripheralIdentifier == identifier) + .map((connectionStateContainer) => + connectionStateContainer.connectionState) + .map((connectionStateString) { switch (connectionStateString.toLowerCase()) { case NativeConnectionState.connected: return PeripheralConnectionState.connected; From ad1c0323424c82901c0d1120ebfb53195356c198 Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Wed, 19 Aug 2020 16:47:16 +0200 Subject: [PATCH 12/31] Bumped action version --- .../dart-flutter-package-analyzer.yml | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/dart-flutter-package-analyzer.yml b/.github/workflows/dart-flutter-package-analyzer.yml index 88e03592..2393a369 100644 --- a/.github/workflows/dart-flutter-package-analyzer.yml +++ b/.github/workflows/dart-flutter-package-analyzer.yml @@ -10,20 +10,22 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: axel-op/dart-package-analyzer@v2 - id: analysis # set an id for the current step + - uses: axel-op/dart-package-analyzer@v3 + # set an id for the current step + id: analysis with: githubToken: ${{ secrets.GITHUB_TOKEN }} # You can then use this id to retrieve the outputs in the next steps. - # The following step shows how to exit the workflow with an error if a score is below 100: + # The following step shows how to exit the workflow with an error if the total score in percentage is below 50: - name: Check scores - # NB: "analysis" is the id set above. Replace it with the one you used if different. + env: + # NB: "analysis" is the id set above. Replace it with the one you used if different. + TOTAL: ${{ steps.analysis.outputs.total }} + TOTAL_MAX: ${{ steps.analysis.outputs.total_max }} run: | - MAINTENANCE_SCORE=${{ steps.analysis.outputs.maintenance }} - HEALTH_SCORE=${{ steps.analysis.outputs.health }} - if (( $(echo "$MAINTENANCE_SCORE < 100" | bc) )) || (( $(echo "$HEALTH_SCORE < 100" | bc) )) + PERCENTAGE=$(( $TOTAL * 100 / $TOTAL_MAX )) + if (( $PERCENTAGE < 50 )) then - echo "Scores are not both equal to 100" - exit 1 - fi + echo Score too low! + exi From 0da8ecff3efa3ff80e01fc226b28e7706c6f3c0a Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Wed, 19 Aug 2020 16:53:18 +0200 Subject: [PATCH 13/31] Fixed copy-paste error --- .github/workflows/dart-flutter-package-analyzer.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dart-flutter-package-analyzer.yml b/.github/workflows/dart-flutter-package-analyzer.yml index 2393a369..def964a2 100644 --- a/.github/workflows/dart-flutter-package-analyzer.yml +++ b/.github/workflows/dart-flutter-package-analyzer.yml @@ -28,4 +28,5 @@ jobs: if (( $PERCENTAGE < 50 )) then echo Score too low! - exi + exit 1 + fi From a98a620975589b278944bbbddd07892275594420 Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Wed, 19 Aug 2020 17:02:42 +0200 Subject: [PATCH 14/31] Adjusted for target metric (total points) --- .github/workflows/dart-flutter-package-analyzer.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dart-flutter-package-analyzer.yml b/.github/workflows/dart-flutter-package-analyzer.yml index def964a2..beb67851 100644 --- a/.github/workflows/dart-flutter-package-analyzer.yml +++ b/.github/workflows/dart-flutter-package-analyzer.yml @@ -24,9 +24,8 @@ jobs: TOTAL: ${{ steps.analysis.outputs.total }} TOTAL_MAX: ${{ steps.analysis.outputs.total_max }} run: | - PERCENTAGE=$(( $TOTAL * 100 / $TOTAL_MAX )) - if (( $PERCENTAGE < 50 )) + if (( $TOTAL < 100 )) then - echo Score too low! + echo Unacceptable score! Needs to be >=100. exit 1 fi From e6dcd47ea60098462a9415f9a40ed7b7453b9623 Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Wed, 19 Aug 2020 17:13:43 +0200 Subject: [PATCH 15/31] Adjusted checks. --- .github/workflows/dart-flutter-package-analyzer.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dart-flutter-package-analyzer.yml b/.github/workflows/dart-flutter-package-analyzer.yml index beb67851..ac83500c 100644 --- a/.github/workflows/dart-flutter-package-analyzer.yml +++ b/.github/workflows/dart-flutter-package-analyzer.yml @@ -23,9 +23,15 @@ jobs: # NB: "analysis" is the id set above. Replace it with the one you used if different. TOTAL: ${{ steps.analysis.outputs.total }} TOTAL_MAX: ${{ steps.analysis.outputs.total_max }} + ANALYSIS: ${{ steps.analysis.outputs.analysis }} + ANALYSIS_MAX=$(( steps.analysis.outputs.analysis_max )) run: | - if (( $TOTAL < 100 )) + if (( $ANALYSIS < $ANALYSIS_MAX )) then - echo Unacceptable score! Needs to be >=100. + echo Unacceptable analysis score! Needs to be maxed out! exit 1 + elif (( $TOTAL < ($TOTAL_MAX - 10) )) + then + echo Unacceptable total score! Check dependencies and other factors! + exit 2 fi From bc088fd78efb746865f91f029577eda1620fef74 Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Wed, 19 Aug 2020 17:30:03 +0200 Subject: [PATCH 16/31] Fixed syntax. --- .github/workflows/dart-flutter-package-analyzer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dart-flutter-package-analyzer.yml b/.github/workflows/dart-flutter-package-analyzer.yml index ac83500c..2dca5af0 100644 --- a/.github/workflows/dart-flutter-package-analyzer.yml +++ b/.github/workflows/dart-flutter-package-analyzer.yml @@ -24,7 +24,7 @@ jobs: TOTAL: ${{ steps.analysis.outputs.total }} TOTAL_MAX: ${{ steps.analysis.outputs.total_max }} ANALYSIS: ${{ steps.analysis.outputs.analysis }} - ANALYSIS_MAX=$(( steps.analysis.outputs.analysis_max )) + ANALYSIS_MAX: $(( steps.analysis.outputs.analysis_max )) run: | if (( $ANALYSIS < $ANALYSIS_MAX )) then From d7888b0c04a8b9246efaa2f1cd118f360999ab03 Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Wed, 19 Aug 2020 17:37:53 +0200 Subject: [PATCH 17/31] Fixed syntax. --- .github/workflows/dart-flutter-package-analyzer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dart-flutter-package-analyzer.yml b/.github/workflows/dart-flutter-package-analyzer.yml index 2dca5af0..55c02368 100644 --- a/.github/workflows/dart-flutter-package-analyzer.yml +++ b/.github/workflows/dart-flutter-package-analyzer.yml @@ -24,7 +24,7 @@ jobs: TOTAL: ${{ steps.analysis.outputs.total }} TOTAL_MAX: ${{ steps.analysis.outputs.total_max }} ANALYSIS: ${{ steps.analysis.outputs.analysis }} - ANALYSIS_MAX: $(( steps.analysis.outputs.analysis_max )) + ANALYSIS_MAX: ${{ steps.analysis.outputs.analysis_max }} run: | if (( $ANALYSIS < $ANALYSIS_MAX )) then From 71a800be210fa301aeb469b2789e6f268473ca07 Mon Sep 17 00:00:00 2001 From: Dariusz Seweryn Date: Wed, 19 Aug 2020 17:50:56 +0200 Subject: [PATCH 18/31] Revert "Revert "Fixed formatting."" This reverts commit b959b0acfd8c2350d2ad5e573a1a224d6592484f. --- lib/src/bridge/device_connection_mixin.dart | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/src/bridge/device_connection_mixin.dart b/lib/src/bridge/device_connection_mixin.dart index f8a9061a..c15ef44f 100644 --- a/lib/src/bridge/device_connection_mixin.dart +++ b/lib/src/bridge/device_connection_mixin.dart @@ -37,15 +37,14 @@ mixin DeviceConnectionMixin on FlutterBLE { ), ); - var sourceStream = - _peripheralConnectionStateChanges - .map((jsonString) => - ConnectionStateContainer.fromJson(jsonDecode(jsonString))) - .where((connectionStateContainer) => - connectionStateContainer.peripheralIdentifier == identifier) - .map((connectionStateContainer) => - connectionStateContainer.connectionState) - .map((connectionStateString) { + var sourceStream = _peripheralConnectionStateChanges + .map((jsonString) => + ConnectionStateContainer.fromJson(jsonDecode(jsonString))) + .where((connectionStateContainer) => + connectionStateContainer.peripheralIdentifier == identifier) + .map((connectionStateContainer) => + connectionStateContainer.connectionState) + .map((connectionStateString) { switch (connectionStateString.toLowerCase()) { case NativeConnectionState.connected: return PeripheralConnectionState.connected; From 01d96033cfa22e3545f7deb39635bc109c833702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kojdecki?= Date: Thu, 20 Aug 2020 15:13:02 +0200 Subject: [PATCH 19/31] Add unsafe way of creating peripherals (#502) * Add unsafe way of creating peripherals * Update doc --- CHANGELOG.md | 5 +++++ lib/ble_manager.dart | 11 +++++++++++ lib/src/internal_ble_manager.dart | 10 ++++++++++ 3 files changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27e33b80..c0942b1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.3.0 + +* add `BleManager.createUnsafePeripheral()` to allow for connecting to known peripheral without launching scan first + **NOTE:** this change will not work with BLEmulator below 1.2.0 + ## 2.2.9 * Fixed issue with incorrectly typed Stream diff --git a/lib/ble_manager.dart b/lib/ble_manager.dart index 5b0def9b..5a353eb4 100644 --- a/lib/ble_manager.dart +++ b/lib/ble_manager.dart @@ -137,6 +137,17 @@ abstract class BleManager { /// /// If [serviceUUIDs] is empty, this will return an empty list. Future> connectedPeripherals(List serviceUUIDs); + + /// Creates a peripheral which may not exist or be available. Since the + /// [peripheralId] might be a UUID or a MAC address, + /// depending on the platform, its format is not validated. + /// + /// On iOS [peripheralId] is unique for a particular device + /// and will not be recognized on any different device. + /// On Android [peripheralId] scanned on one device may or may not be + /// recognized on a different Android device depending on peripheral’s + /// implementation and changes in future OS releases. + Peripheral createUnsafePeripheral(String peripheralId, {String name}); } /// State of the Bluetooth Adapter. diff --git a/lib/src/internal_ble_manager.dart b/lib/src/internal_ble_manager.dart index 5234e89c..888d1a8e 100644 --- a/lib/src/internal_ble_manager.dart +++ b/lib/src/internal_ble_manager.dart @@ -62,6 +62,16 @@ class InternalBleManager @override Future stopPeripheralScan() => _bleLib.stopDeviceScan(); + @override + Peripheral createUnsafePeripheral(String peripheralId, {String name}) { + const nameField = 'name'; + const identifierField = 'id'; + return Peripheral.fromJson({ + nameField: name, + identifierField: peripheralId, + }, this); + } + @override Future connectToPeripheral( String identifier, { From 8a6e48ae6e674db2d0c1a855b15893226cbdc965 Mon Sep 17 00:00:00 2001 From: Mikolaj Kojdecki Date: Tue, 25 Aug 2020 08:46:54 +0200 Subject: [PATCH 20/31] Release 2.3.0 --- README.md | 9 +++++++++ android/build.gradle | 2 +- ios/flutter_ble_lib.podspec | 2 +- pubspec.yaml | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4d375893..ebf51ac1 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,15 @@ It filters the scan results to those that advertise a service with specified UUI **NOTE:** `isConnectable` and `overflowServiceUuids` fields of `ScanResult` are iOS-only and remain `null` on Android. + +### Connecting to saved peripheral + +You can try to connect to a peripheral with known ID, be it previously scanned UUID on iOS or a MAC address on Android, and avoid the whole scanning operation in your application. To do so, you need to create an instance of `Peripheral` using: +```dart +Peripheral myPeripheral = bleManager.createUnsafePeripheral("< known id >"); +``` +Once you have the instance of the peripheral, you may proceed with the connection. + ### Connecting to peripheral First you must obtain a _ScanResult_ from _BleManager.startPeripheralScan()_. diff --git a/android/build.gradle b/android/build.gradle index a1875ce4..815ef735 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ group 'com.polidea.flutter_ble_lib' -version '2.2.9' +version '2.3.0' buildscript { repositories { diff --git a/ios/flutter_ble_lib.podspec b/ios/flutter_ble_lib.podspec index 2fe4a3c8..11158c64 100644 --- a/ios/flutter_ble_lib.podspec +++ b/ios/flutter_ble_lib.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_ble_lib' - s.version = '2.2.9' + s.version = '2.3.0' s.summary = 'A new flutter plugin project.' s.description = <<-DESC A new flutter plugin project. diff --git a/pubspec.yaml b/pubspec.yaml index 5c5565b4..c988fafa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_ble_lib description: FlutterBle Library is a flutter library that supports BLE operations. It uses MultiPlatformBleAdapter as a native backend.. -version: 2.2.9 +version: 2.3.0 homepage: https://github.com/Polidea/FlutterBleLib environment: From d155791894acf77edec0f352819809c1be3223d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kojdecki?= Date: Tue, 25 Aug 2020 11:34:00 +0200 Subject: [PATCH 21/31] Update README.md Co-authored-by: Dariusz Seweryn --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ebf51ac1..84d6651b 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,8 @@ You can try to connect to a peripheral with known ID, be it previously scanned U ```dart Peripheral myPeripheral = bleManager.createUnsafePeripheral("< known id >"); ``` -Once you have the instance of the peripheral, you may proceed with the connection. +Once you have the instance of the peripheral, you may proceed with the connection. But keep in mind +that [Android may still not find the peripheral without scanning it first](https://stackoverflow.com/questions/43476369/android-save-ble-device-to-reconnect-after-app-close/43482099#43482099). ### Connecting to peripheral From 92648f6e67807196e962603f4bd8ee215d1d8239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kojdecki?= Date: Mon, 16 Nov 2020 11:19:33 +0100 Subject: [PATCH 22/31] Minor doc fixes (#544) --- lib/peripheral.dart | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/peripheral.dart b/lib/peripheral.dart index 4c22fd4c..61863b05 100644 --- a/lib/peripheral.dart +++ b/lib/peripheral.dart @@ -35,18 +35,17 @@ class Peripheral { /// guaranteed to get it after connection is successful. (Android only) /// iOS by default requests about 186 MTU size and there's nothing anyone can /// do about it. - /// **NOTE**: if MTU has been request on this step, then there's no way - /// to retrieve it's value later on. + /// **NOTE**: if MTU has been requested on this step, then there's no way + /// to retrieve its value later on. /// - /// Optional [requestMtu] means that the MTU will not be negotiated - /// Passing `true` as [refreshGatt] leads reset services cache. This option may + /// Passing `true` as [refreshGatt] will reset services cache. This option may /// be useful when a peripheral's firmware was updated and it's /// services/characteristics were added/removed/altered. (Android only) /// - /// Optional [timeout] is used to define time after connection is - /// automatically timed out. In case of race condition were connection + /// Optional [timeout] is used to define delay after which the connection is + /// automatically cancelled. In case of race condition were connection /// is established right after timeout event, peripheral will be disconnected - /// immediately. Timeout may happen earlier then specified due to OS + /// immediately. Timeout may happen earlier than specified due to OS /// specific behavior. Future connect( {bool isAutoConnect = false, From ef8c1b0dcd0061179d43492565515fbb799fa202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kojdecki?= Date: Mon, 16 Nov 2020 11:20:06 +0100 Subject: [PATCH 23/31] Regenerate podfile for example (#545) --- example/ios/Podfile | 79 ++++---------------- example/ios/Runner.xcodeproj/project.pbxproj | 24 ++---- 2 files changed, 23 insertions(+), 80 deletions(-) diff --git a/example/ios/Podfile b/example/ios/Podfile index b30a428b..1e8c3c90 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -10,81 +10,32 @@ project 'Runner', { 'Release' => :release, } -def parse_KV_file(file, separator='=') - file_abs_path = File.expand_path(file) - if !File.exists? file_abs_path - return []; +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" end - generated_key_values = {} - skip_line_start_symbols = ["#", "/"] - File.foreach(file_abs_path) do |line| - next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } - plugin = line.split(pattern=separator) - if plugin.length == 2 - podname = plugin[0].strip() - path = plugin[1].strip() - podpath = File.expand_path("#{path}", file_abs_path) - generated_key_values[podname] = podpath - else - puts "Invalid plugin specification: #{line}" - end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches end - generated_key_values + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" end +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + target 'Runner' do use_frameworks! use_modular_headers! - - # Flutter Pod - copied_flutter_dir = File.join(__dir__, 'Flutter') - copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') - copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') - unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) - # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. - # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. - # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. - - generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') - unless File.exist?(generated_xcode_build_settings_path) - raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) - cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; - - unless File.exist?(copied_framework_path) - FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) - end - unless File.exist?(copied_podspec_path) - FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) - end - end - - # Keep pod path relative so it can be checked into Podfile.lock. - pod 'Flutter', :path => 'Flutter' - - # Plugin Pods - - # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock - # referring to absolute paths on developers' machines. - system('rm -rf .symlinks') - system('mkdir -p .symlinks/plugins') - plugin_pods = parse_KV_file('../.flutter-plugins') - plugin_pods.each do |name, path| - symlink = File.join('.symlinks', 'plugins', name) - File.symlink(path, symlink) - pod name, :path => File.join(symlink, 'ios') - end + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end -# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. -install! 'cocoapods', :disable_input_output_paths => true - post_install do |installer| installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['ENABLE_BITCODE'] = 'NO' - end + flutter_additional_ios_build_settings(target) end end diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index f32ba7b6..671f5ab1 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -10,11 +10,7 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1F98025F8F407F461CB357DF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E99B52135973B0C19BFDEF7 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 81E1240A2334F85D005D9563 /* SwiftBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81E124092334F85D005D9563 /* SwiftBridging.swift */; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -30,8 +26,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -45,7 +39,6 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 1AB7F698716680F75BACA8D9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -54,7 +47,6 @@ 96C714B17D5919D3696FE535 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -68,8 +60,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 1F98025F8F407F461CB357DF /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -80,9 +70,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -239,9 +227,16 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/MultiplatformBleAdapter/MultiplatformBleAdapter.framework", + "${BUILT_PRODUCTS_DIR}/flutter_ble_lib/flutter_ble_lib.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MultiplatformBleAdapter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_ble_lib.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -260,7 +255,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; @@ -336,7 +331,6 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -416,7 +410,6 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -472,7 +465,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; From dc3f8f5029b7809496af7b92875691dbff0733ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kojdecki?= Date: Mon, 16 Nov 2020 19:26:17 +0100 Subject: [PATCH 24/31] Fix connection timeout on iOS (#546) Fixes #478 --- CHANGELOG.md | 4 ++++ .../com/polidea/flutter_ble_lib/constant/ArgumentKey.java | 2 +- ios/Classes/Constants/ArgumentKey.m | 2 +- lib/src/_constants.dart | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0942b1f..993dcb26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.1 + +* Fix connection timeout on iOS + ## 2.3.0 * add `BleManager.createUnsafePeripheral()` to allow for connecting to known peripheral without launching scan first diff --git a/android/src/main/java/com/polidea/flutter_ble_lib/constant/ArgumentKey.java b/android/src/main/java/com/polidea/flutter_ble_lib/constant/ArgumentKey.java index 1dd8937b..b7e79c85 100644 --- a/android/src/main/java/com/polidea/flutter_ble_lib/constant/ArgumentKey.java +++ b/android/src/main/java/com/polidea/flutter_ble_lib/constant/ArgumentKey.java @@ -12,7 +12,7 @@ public interface ArgumentKey { String IS_AUTO_CONNECT = "isAutoConnect"; String REQUEST_MTU = "requestMtu"; String REFRESH_GATT = "refreshGatt"; - String TIMEOUT_MILLIS = "timeoutMillis"; + String TIMEOUT_MILLIS = "timeout"; String EMIT_CURRENT_VALUE = "emitCurrentValue"; String LOG_LEVEL = "logLevel"; diff --git a/ios/Classes/Constants/ArgumentKey.m b/ios/Classes/Constants/ArgumentKey.m index 590a4df8..3aa4a836 100644 --- a/ios/Classes/Constants/ArgumentKey.m +++ b/ios/Classes/Constants/ArgumentKey.m @@ -10,7 +10,7 @@ NSString * const ARGUMENT_KEY_IS_AUTO_CONNECT = @"isAutoConnect"; NSString * const ARGUMENT_KEY_REQUEST_MTU = @"requestMtu"; NSString * const ARGUMENT_KEY_REFRESH_GATT = @"refreshGatt"; -NSString * const ARGUMENT_KEY_TIMEOUT_MILLIS = @"timeoutMillis"; +NSString * const ARGUMENT_KEY_TIMEOUT_MILLIS = @"timeout"; NSString * const ARGUMENT_KEY_EMIT_CURRENT_VALUE = @"emitCurrentValue"; NSString * const ARGUMENT_KEY_LOG_LEVEL = @"logLevel"; diff --git a/lib/src/_constants.dart b/lib/src/_constants.dart index 18e53266..567c9ae3 100644 --- a/lib/src/_constants.dart +++ b/lib/src/_constants.dart @@ -97,7 +97,7 @@ abstract class ArgumentName { static const String isAutoConnect = "isAutoConnect"; static const String requestMtu = "requestMtu"; static const String refreshGatt = "refreshGatt"; - static const String timeoutMillis = "timeoutMillis"; + static const String timeoutMillis = "timeout"; static const String emitCurrentValue = "emitCurrentValue"; static const String serviceUuid = "serviceUuid"; From 33afb00065c90f47efec32e6073ce229e27154b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kojdecki?= Date: Tue, 17 Nov 2020 18:53:43 +0100 Subject: [PATCH 25/31] Fix emitting current connection state on iOS (#547) Fixes #540 --- CHANGELOG.md | 1 + ios/Classes/FlutterBleLibPlugin.m | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 993dcb26..e16706e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 2.3.1 * Fix connection timeout on iOS +* Fix emitting current connection state on iOS ## 2.3.0 diff --git a/ios/Classes/FlutterBleLibPlugin.m b/ios/Classes/FlutterBleLibPlugin.m index de3e5f04..f8cfea44 100644 --- a/ios/Classes/FlutterBleLibPlugin.m +++ b/ios/Classes/FlutterBleLibPlugin.m @@ -235,7 +235,7 @@ - (void)observeConnectionState:(FlutterMethodCall *)call result:(FlutterResult)r BOOL emitCurrentValue = ((NSNumber *)call.arguments[ARGUMENT_KEY_EMIT_CURRENT_VALUE]).boolValue; if (emitCurrentValue == YES) { Resolve resolve = ^(id isConnected) { - if ((BOOL)isConnected == YES) { + if ([isConnected boolValue] == YES) { [self.connectionStateStreamHandler onConnectedEvent:call.arguments[ARGUMENT_KEY_DEVICE_IDENTIFIER]]; } else { [self.connectionStateStreamHandler emitDisconnectedEvent:call.arguments[ARGUMENT_KEY_DEVICE_IDENTIFIER]]; From ca72656ed07dddc1bcc22c4c64f5189f790b62b4 Mon Sep 17 00:00:00 2001 From: Mikolaj Kojdecki Date: Thu, 19 Nov 2020 11:26:18 +0100 Subject: [PATCH 26/31] Release 2.3.1 --- CHANGELOG.md | 3 +++ android/build.gradle | 4 ++-- ios/flutter_ble_lib.podspec | 4 ++-- pubspec.yaml | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e16706e2..e4ce27bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ * Fix connection timeout on iOS * Fix emitting current connection state on iOS +* Update MBA to 0.1.7: + * Fix lack of disconnection event on iOS when connection fails to be established + * Fix not all errors being passed through onError callbacks on Android (thanks, @eliaslecomte) ## 2.3.0 diff --git a/android/build.gradle b/android/build.gradle index 815ef735..e50afd53 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ group 'com.polidea.flutter_ble_lib' -version '2.3.0' +version '2.3.1' buildscript { repositories { @@ -36,5 +36,5 @@ android { dependencies { implementation 'androidx.annotation:annotation:1.1.0' - implementation 'com.github.Polidea:MultiPlatformBleAdapter:0.1.6' + implementation 'com.github.Polidea:MultiPlatformBleAdapter:0.1.7' } diff --git a/ios/flutter_ble_lib.podspec b/ios/flutter_ble_lib.podspec index 11158c64..138d92c7 100644 --- a/ios/flutter_ble_lib.podspec +++ b/ios/flutter_ble_lib.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_ble_lib' - s.version = '2.3.0' + s.version = '2.3.1' s.summary = 'A new flutter plugin project.' s.description = <<-DESC A new flutter plugin project. @@ -16,7 +16,7 @@ A new flutter plugin project. s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' s.swift_versions = ['4.0', '4.2', '5.0'] - s.dependency 'MultiplatformBleAdapter', '~> 0.1.6' + s.dependency 'MultiplatformBleAdapter', '~> 0.1.7' s.ios.deployment_target = '8.0' end diff --git a/pubspec.yaml b/pubspec.yaml index c988fafa..a9b4117f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_ble_lib description: FlutterBle Library is a flutter library that supports BLE operations. It uses MultiPlatformBleAdapter as a native backend.. -version: 2.3.0 +version: 2.3.1 homepage: https://github.com/Polidea/FlutterBleLib environment: From 6d150ccc722002202bb126f54b9eeab0c9b82da7 Mon Sep 17 00:00:00 2001 From: Mikolaj Kojdecki Date: Wed, 3 Feb 2021 19:18:39 +0100 Subject: [PATCH 27/31] Release 2.3.2 --- CHANGELOG.md | 4 ++++ android/build.gradle | 4 ++-- ios/flutter_ble_lib.podspec | 4 ++-- pubspec.yaml | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4ce27bf..b0d286fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.2 + +* Fix lack of disconnection event on iOS if connection failed to be established + ## 2.3.1 * Fix connection timeout on iOS diff --git a/android/build.gradle b/android/build.gradle index e50afd53..64e247dc 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ group 'com.polidea.flutter_ble_lib' -version '2.3.1' +version '2.3.2' buildscript { repositories { @@ -36,5 +36,5 @@ android { dependencies { implementation 'androidx.annotation:annotation:1.1.0' - implementation 'com.github.Polidea:MultiPlatformBleAdapter:0.1.7' + implementation 'com.github.Polidea:MultiPlatformBleAdapter:0.1.8' } diff --git a/ios/flutter_ble_lib.podspec b/ios/flutter_ble_lib.podspec index 138d92c7..a7ba7c37 100644 --- a/ios/flutter_ble_lib.podspec +++ b/ios/flutter_ble_lib.podspec @@ -3,7 +3,7 @@ # Pod::Spec.new do |s| s.name = 'flutter_ble_lib' - s.version = '2.3.1' + s.version = '2.3.2' s.summary = 'A new flutter plugin project.' s.description = <<-DESC A new flutter plugin project. @@ -16,7 +16,7 @@ A new flutter plugin project. s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' s.swift_versions = ['4.0', '4.2', '5.0'] - s.dependency 'MultiplatformBleAdapter', '~> 0.1.7' + s.dependency 'MultiplatformBleAdapter', '~> 0.1.8' s.ios.deployment_target = '8.0' end diff --git a/pubspec.yaml b/pubspec.yaml index a9b4117f..1b9363ad 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_ble_lib description: FlutterBle Library is a flutter library that supports BLE operations. It uses MultiPlatformBleAdapter as a native backend.. -version: 2.3.1 +version: 2.3.2 homepage: https://github.com/Polidea/FlutterBleLib environment: From 8e6cf7236b22b79f4bacbc0ac29bd6f459f06b64 Mon Sep 17 00:00:00 2001 From: James McIntosh Date: Sat, 20 Mar 2021 05:20:13 +1300 Subject: [PATCH 28/31] Improve example app (#583) * Update iOS config * Only create client once Change refresh order to stop iOS crash Code tidy * Track end of stream in iOS --- example/ios/Podfile | 2 +- example/ios/Runner/Info.plist | 8 +- example/lib/devices_list/devices_bloc.dart | 102 ++++++++++++--------- ios/Classes/Event/ScanningStreamHandler.m | 15 ++- 4 files changed, 80 insertions(+), 47 deletions(-) diff --git a/example/ios/Podfile b/example/ios/Podfile index 1e8c3c90..252d9ec7 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +platform :ios, '9.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 90e38bbd..90dea4bb 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -22,6 +22,8 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + NSBluetoothAlwaysUsageDescription + App would like to use bluetooth for communication purposes UIBackgroundModes bluetooth-central @@ -43,9 +45,11 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + NSBonjourServices + + _dartobservatory._tcp + UIViewControllerBasedStatusBarAppearance - NSBluetoothAlwaysUsageDescription - App would like to use bluetooth for communication purposes diff --git a/example/lib/devices_list/devices_bloc.dart b/example/lib/devices_list/devices_bloc.dart index b626cce6..4c57c66b 100644 --- a/example/lib/devices_list/devices_bloc.dart +++ b/example/lib/devices_list/devices_bloc.dart @@ -11,17 +11,14 @@ import 'package:rxdart/rxdart.dart'; class DevicesBloc { final List bleDevices = []; - BehaviorSubject> _visibleDevicesController = - BehaviorSubject>.seeded([]); + BehaviorSubject> _visibleDevicesController = BehaviorSubject>.seeded([]); - StreamController _devicePickerController = - StreamController(); + StreamController _devicePickerController = StreamController(); StreamSubscription _scanSubscription; StreamSubscription _devicePickerSubscription; - ValueObservable> get visibleDevices => - _visibleDevicesController.stream; + ValueObservable> get visibleDevices => _visibleDevicesController.stream; Sink get devicePicker => _devicePickerController.sink; @@ -34,6 +31,8 @@ class DevicesBloc { DevicesBloc(this._deviceRepository, this._bleManager); + bool clientCreated = false; + void _handlePickedDevice(BleDevice bleDevice) { _deviceRepository.pickDevice(bleDevice); } @@ -44,24 +43,18 @@ class DevicesBloc { _visibleDevicesController.close(); _devicePickerController.close(); _scanSubscription?.cancel(); + _bleManager.destroyClient(); } void init() { Fimber.d("Init devices bloc"); bleDevices.clear(); - _bleManager - .createClient( - restoreStateIdentifier: "example-restore-state-identifier", - restoreStateAction: (peripherals) { - peripherals?.forEach((peripheral) { - Fimber.d("Restored peripheral: ${peripheral.name}"); - }); - }) - .catchError((e) => Fimber.d("Couldn't create BLE client", ex: e)) - .then((_) => _checkPermissions()) - .catchError((e) => Fimber.d("Permission check error", ex: e)) - .then((_) => _waitForBluetoothPoweredOn()) - .then((_) => _startScan()); + + maybeCreateClient() + .then((_) => _checkPermissions()) + .catchError((e) => Fimber.d("Permission check error", ex: e)) + .then((_) => _waitForBluetoothPoweredOn()) + .then((_) => _startScan()); if (_visibleDevicesController.isClosed) { _visibleDevicesController = @@ -73,8 +66,33 @@ class DevicesBloc { } Fimber.d(" listen to _devicePickerController.stream"); - _devicePickerSubscription = - _devicePickerController.stream.listen(_handlePickedDevice); + _devicePickerSubscription = _devicePickerController.stream.listen(_handlePickedDevice); + } + + Future maybeCreateClient() { + if (clientCreated) { + Fimber.d("Client already exists"); + + return Future.value(); + } + + clientCreated = true; + + Fimber.d("Create client"); + + return _bleManager + .createClient( + restoreStateIdentifier: "example-restore-state-identifier", + restoreStateAction: (peripherals) { + peripherals?.forEach((peripheral) { + Fimber.d("Restored peripheral: ${peripheral.name}"); + }); + } + ) + .catchError((e) { + clientCreated = false; + return Fimber.d("Couldn't create BLE client", ex: e); + }); } Future _checkPermissions() async { @@ -94,37 +112,37 @@ class DevicesBloc { Completer completer = Completer(); StreamSubscription subscription; subscription = _bleManager - .observeBluetoothState(emitCurrentValue: true) - .listen((bluetoothState) async { - if (bluetoothState == BluetoothState.POWERED_ON && - !completer.isCompleted) { - await subscription.cancel(); - completer.complete(); - } - }); + .observeBluetoothState(emitCurrentValue: true) + .listen((bluetoothState) async { + if (bluetoothState == BluetoothState.POWERED_ON && !completer.isCompleted) { + await subscription.cancel(); + completer.complete(); + } + }); + return completer.future; } void _startScan() { - Fimber.d("Ble client created"); - _scanSubscription = - _bleManager.startPeripheralScan().listen((ScanResult scanResult) { - var bleDevice = BleDevice(scanResult); - if (scanResult.advertisementData.localName != null && - !bleDevices.contains(bleDevice)) { - Fimber.d( - 'found new device ${scanResult.advertisementData.localName} ${scanResult.peripheral.identifier}'); - bleDevices.add(bleDevice); - _visibleDevicesController.add(bleDevices.sublist(0)); - } - }); + Fimber.d("Ble start scan"); + _scanSubscription = _bleManager.startPeripheralScan() + .listen((ScanResult scanResult) { + var bleDevice = BleDevice(scanResult); + if (scanResult.advertisementData.localName != null && !bleDevices.contains(bleDevice)) { + Fimber.d('found new device ${scanResult.advertisementData.localName} ${scanResult.peripheral.identifier}'); + bleDevices.add(bleDevice); + _visibleDevicesController.add(bleDevices.sublist(0)); + } + }); } Future refresh() async { - _scanSubscription.cancel(); await _bleManager.stopPeripheralScan(); + await _scanSubscription.cancel(); bleDevices.clear(); + _visibleDevicesController.add(bleDevices.sublist(0)); + await _checkPermissions() .then((_) => _startScan()) .catchError((e) => Fimber.d("Couldn't refresh", ex: e)); diff --git a/ios/Classes/Event/ScanningStreamHandler.m b/ios/Classes/Event/ScanningStreamHandler.m index f49f7514..51bfad1c 100644 --- a/ios/Classes/Event/ScanningStreamHandler.m +++ b/ios/Classes/Event/ScanningStreamHandler.m @@ -5,11 +5,20 @@ @implementation ScanningStreamHandler { FlutterEventSink scanResultsSink; + bool ended; +} + +- (ScanningStreamHandler *)init { + if (self = [super init]) { + ended = false; + } + return self; } - (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments { @synchronized (self) { scanResultsSink = nil; + ended = false; return nil; } } @@ -17,13 +26,14 @@ - (FlutterError * _Nullable)onCancelWithArguments:(id _Nullable)arguments { - (FlutterError * _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(nonnull FlutterEventSink)events { @synchronized (self) { scanResultsSink = events; + ended = false; return nil; } } - (void)onScanResult:(NSArray *)scanResult { @synchronized (self) { - if (scanResultsSink != nil) { + if (scanResultsSink != nil && !ended) { if (!(scanResult.count == 2 && (scanResult[0] == [NSNull null] || (scanResult[1] == [NSNull null] && [scanResult[0] isKindOfClass:NSString.class])))) { scanResultsSink([FlutterError errorWithCode:@"-1" message:@"Invalid scanResult format." details:nil]); @@ -42,8 +52,9 @@ - (void)onScanResult:(NSArray *)scanResult { - (void)onComplete { @synchronized (self) { - if (scanResultsSink != nil) { + if (scanResultsSink != nil && !ended) { scanResultsSink(FlutterEndOfEventStream); + ended = true; } } } From 1da3feb70ed4f853145049fe3c5c4ec16080b287 Mon Sep 17 00:00:00 2001 From: Yauheni Pakala Date: Thu, 8 Apr 2021 15:15:08 +0300 Subject: [PATCH 29/31] Add bluetooth-central key to README.md (#562) * Add bluetooth-central key to README.md Based on #530 * Update README.md --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 84d6651b..b678302d 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,19 @@ Add [Privacy - Bluetooth Always Usage Description](https://developer.apple.com/d ... ``` +#### Background mode + +To support background capabilities add [The `bluetooth-central` Background Execution Mode](https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html#//apple_ref/doc/uid/TP40013257-CH7-SW6) key to `[project]/ios/Runner/Info.plist` file. + +```xml +... +UIBackgroundModes + + bluetooth-central + +... +``` + ## Usage The library is organised around a few base entities, which are: From aaa2009274a0b6c48a0eeae7565143f87b89ee2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kojdecki?= Date: Mon, 12 Apr 2021 20:07:59 +0200 Subject: [PATCH 30/31] Add method for checking whether native client exists (#589) --- CHANGELOG.md | 5 +++++ .../flutter_ble_lib/FlutterBleLibPlugin.java | 10 ++++++++++ .../flutter_ble_lib/constant/MethodName.java | 1 + example/ios/Runner.xcodeproj/project.pbxproj | 2 -- .../project.xcworkspace/contents.xcworkspacedata | 2 +- example/lib/devices_list/devices_bloc.dart | 15 ++++++--------- ios/Classes/Constants/MethodName.h | 1 + ios/Classes/Constants/MethodName.m | 1 + ios/Classes/FlutterBleLibPlugin.m | 9 +++++++++ lib/ble_manager.dart | 3 +++ lib/src/_constants.dart | 1 + lib/src/bridge/lib_core.dart | 3 +++ lib/src/internal_ble_manager.dart | 3 +++ 13 files changed, 44 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0d286fc..c1b59f07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.4.0 + +* Add `BleManager#isClientCreated()` for checking whether native client exists + + ## 2.3.2 * Fix lack of disconnection event on iOS if connection failed to be established diff --git a/android/src/main/java/com/polidea/flutter_ble_lib/FlutterBleLibPlugin.java b/android/src/main/java/com/polidea/flutter_ble_lib/FlutterBleLibPlugin.java index e2007224..8176b792 100644 --- a/android/src/main/java/com/polidea/flutter_ble_lib/FlutterBleLibPlugin.java +++ b/android/src/main/java/com/polidea/flutter_ble_lib/FlutterBleLibPlugin.java @@ -116,12 +116,22 @@ public void onMethodCall(MethodCall call, Result result) { case MethodName.CANCEL_TRANSACTION: cancelTransaction(call, result); break; + case MethodName.IS_CLIENT_CREATED: + isClientCreated(result); + break; default: result.notImplemented(); } } + private void isClientCreated(Result result) { + result.success(bleAdapter != null); + } + private void createClient(MethodCall call, Result result) { + if (bleAdapter != null) { + Log.w(TAG, "Overwriting existing native client. Use BleManager#isClientCreated to check whether a client already exists."); + } setupAdapter(context); bleAdapter.createClient(call.argument(ArgumentKey.RESTORE_STATE_IDENTIFIER), new OnEventCallback() { diff --git a/android/src/main/java/com/polidea/flutter_ble_lib/constant/MethodName.java b/android/src/main/java/com/polidea/flutter_ble_lib/constant/MethodName.java index 845585f4..13cd8490 100644 --- a/android/src/main/java/com/polidea/flutter_ble_lib/constant/MethodName.java +++ b/android/src/main/java/com/polidea/flutter_ble_lib/constant/MethodName.java @@ -1,6 +1,7 @@ package com.polidea.flutter_ble_lib.constant; public interface MethodName { + String IS_CLIENT_CREATED = "isClientCreated"; String CREATE_CLIENT = "createClient"; String DESTROY_CLIENT = "destroyClient"; diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 671f5ab1..f9dccdcd 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -228,13 +228,11 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../Flutter/Flutter.framework", "${BUILT_PRODUCTS_DIR}/MultiplatformBleAdapter/MultiplatformBleAdapter.framework", "${BUILT_PRODUCTS_DIR}/flutter_ble_lib/flutter_ble_lib.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MultiplatformBleAdapter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_ble_lib.framework", ); diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a16..919434a6 100644 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/example/lib/devices_list/devices_bloc.dart b/example/lib/devices_list/devices_bloc.dart index 4c57c66b..8b0434ba 100644 --- a/example/lib/devices_list/devices_bloc.dart +++ b/example/lib/devices_list/devices_bloc.dart @@ -31,8 +31,6 @@ class DevicesBloc { DevicesBloc(this._deviceRepository, this._bleManager); - bool clientCreated = false; - void _handlePickedDevice(BleDevice bleDevice) { _deviceRepository.pickDevice(bleDevice); } @@ -43,7 +41,6 @@ class DevicesBloc { _visibleDevicesController.close(); _devicePickerController.close(); _scanSubscription?.cancel(); - _bleManager.destroyClient(); } void init() { @@ -69,15 +66,16 @@ class DevicesBloc { _devicePickerSubscription = _devicePickerController.stream.listen(_handlePickedDevice); } - Future maybeCreateClient() { - if (clientCreated) { - Fimber.d("Client already exists"); + Future maybeCreateClient() async { + Fimber.d('Checking if client exists...'); + final clientAlreadyExists = await _bleManager.isClientCreated(); + Fimber.d('Client exists: $clientAlreadyExists'); + if (clientAlreadyExists) { + Fimber.d("Client already exists"); return Future.value(); } - clientCreated = true; - Fimber.d("Create client"); return _bleManager @@ -90,7 +88,6 @@ class DevicesBloc { } ) .catchError((e) { - clientCreated = false; return Fimber.d("Couldn't create BLE client", ex: e); }); } diff --git a/ios/Classes/Constants/MethodName.h b/ios/Classes/Constants/MethodName.h index 71d34776..28c94b8b 100644 --- a/ios/Classes/Constants/MethodName.h +++ b/ios/Classes/Constants/MethodName.h @@ -1,3 +1,4 @@ +extern NSString * const METHOD_NAME_IS_CLIENT_CREATED; extern NSString * const METHOD_NAME_CREATE_CLIENT; extern NSString * const METHOD_NAME_DESTROY_CLIENT; diff --git a/ios/Classes/Constants/MethodName.m b/ios/Classes/Constants/MethodName.m index eed61449..e30e0f07 100644 --- a/ios/Classes/Constants/MethodName.m +++ b/ios/Classes/Constants/MethodName.m @@ -1,3 +1,4 @@ +NSString * const METHOD_NAME_IS_CLIENT_CREATED = @"isClientCreated"; NSString * const METHOD_NAME_CREATE_CLIENT = @"createClient"; NSString * const METHOD_NAME_DESTROY_CLIENT = @"destroyClient"; diff --git a/ios/Classes/FlutterBleLibPlugin.m b/ios/Classes/FlutterBleLibPlugin.m index f8cfea44..21d1cc4d 100644 --- a/ios/Classes/FlutterBleLibPlugin.m +++ b/ios/Classes/FlutterBleLibPlugin.m @@ -71,6 +71,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result [self createClient:call result:result]; } else if ([METHOD_NAME_DESTROY_CLIENT isEqualToString:call.method]) { [self destroyClient]; + } else if ([METHOD_NAME_IS_CLIENT_CREATED isEqualToString:call.method]) { + [self isClientCreated:call result:result]; } else if ([METHOD_NAME_ENABLE_RADIO isEqualToString:call.method]) { [self enable:call result:result]; } else if ([METHOD_NAME_DISABLE_RADIO isEqualToString:call.method]) { @@ -158,7 +160,14 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result // MARK: - MBA Methods - BleClient lifecycle +- (void)isClientCreated:(FlutterMethodCall *)call result:(FlutterResult)result { + result([NSNumber numberWithBool:_adapter != nil]); +} + - (void)createClient:(FlutterMethodCall *)call result:(FlutterResult)result { + if (_adapter != nil) { + NSLog(@"Overwriting existing native client. Use BleManager#isClientCreated to check whether a client already exists."); + } _adapter = [BleAdapterFactory getNewAdapterWithQueue:dispatch_get_main_queue() restoreIdentifierKey:[ArgumentHandler stringOrNil:call.arguments[ARGUMENT_KEY_RESTORE_STATE_IDENTIFIER]]]; _adapter.delegate = self; diff --git a/lib/ble_manager.dart b/lib/ble_manager.dart index 5a353eb4..3830d700 100644 --- a/lib/ble_manager.dart +++ b/lib/ble_manager.dart @@ -49,6 +49,9 @@ abstract class BleManager { /// for example, the result is no longer useful due to user's actions. Future cancelTransaction(String transactionId); + /// Checks whether the native client exists. + Future isClientCreated(); + /// Allocates native resources. /// /// [restoreStateIdentifier] and [restoreStateAction] are iOS-specific. diff --git a/lib/src/_constants.dart b/lib/src/_constants.dart index 567c9ae3..838ad857 100644 --- a/lib/src/_constants.dart +++ b/lib/src/_constants.dart @@ -1,4 +1,5 @@ abstract class MethodName { + static const String isClientCreated = "isClientCreated"; static const String createClient = "createClient"; static const String destroyClient = "destroyClient"; diff --git a/lib/src/bridge/lib_core.dart b/lib/src/bridge/lib_core.dart index 8589f1de..a8b181d5 100644 --- a/lib/src/bridge/lib_core.dart +++ b/lib/src/bridge/lib_core.dart @@ -51,6 +51,9 @@ class FlutterBleLib extends FlutterBLE .take(1) .single; + Future isClientCreated() => + _methodChannel.invokeMethod(MethodName.isClientCreated); + Future createClient(String restoreStateIdentifier) async { await _methodChannel.invokeMethod(MethodName.createClient, { ArgumentName.restoreStateIdentifier: restoreStateIdentifier diff --git a/lib/src/internal_ble_manager.dart b/lib/src/internal_ble_manager.dart index 888d1a8e..15f29cd8 100644 --- a/lib/src/internal_ble_manager.dart +++ b/lib/src/internal_ble_manager.dart @@ -14,6 +14,9 @@ class InternalBleManager _bleLib.registerManager(this); } + @override + Future isClientCreated() => _bleLib.isClientCreated(); + @override Future createClient({ String restoreStateIdentifier, From 1beac74c7fba5d1b0459f621bb98f26dca50cc59 Mon Sep 17 00:00:00 2001 From: okocsis Date: Tue, 13 Apr 2021 19:53:31 +0200 Subject: [PATCH 31/31] Nullsafety (#586) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Work in progress * lib/** nullability compliant; added linter exceptions for string single quotes and pedantic local variable type rule * Example:: updated to nullsafe-ready dependencies * Example:: nullsafety + refactor WIP * Example:: nullsafe ready * Example/test:: nullsafe ready * test:: using new mocking mechanism additional fixes * Java: added some null checks to make the calls to stop and destroy reentrant * Example:: additional cleanup and refactor Lib:: enchanced error handling, refined nullability on certain types * TestFix:: the injected manager instance is not part of the Characteristic POD so should be ignored when checking for equallity * ble_error seem to have rather nullable fields * TestFix: little refactor on characteristic_mixin regarding clearity and type annotations * TestFix:: lib_core_test:: tests now passing * Example:: removed comment-disabled code * Test:: all tests Passing now! 🎉 * Enhanced BleError nullability, so that reason always has a default value; made fields final * Travis fix * disabled analysis for tests * Travis:: still fixing ios build * vscode:: stopped tracking settings file * travis:: removed my additions to ios example script * Characteristic:: Service field is now final * gitignored andoid related staff xcode noise * Add bluetooth-central key to README.md (#562) * Add bluetooth-central key to README.md Based on #530 * Update README.md * Travis: updated xcode version to 12.2 * BleError:: default reason text added * ScanResult:: added final qualifiers isConnectable is back to nullable since it's only available on iOS on android it will be null which does not mean false!! * CharacteristicsMixin:: _parseCharacteristicWithValueWithTransactionIdResponse():: signature is now type safe moved the rawJsonValue String checking to the caller side * Test:: removed dummy print * ScanningMixin:: _prepareScanEventsStream() renamed to just _scanEvents also _resetScanEvents introduced to null the underlying scream out * ScanningMixin:: small refinement on naming stuff * Characteristic:: refactor on Futures to always complete with something even if withResponse is false additional type safety on invokeMethod calls * Revert "Characteristic:: refactor on Futures to always complete with something even if withResponse is false" This reverts commit 86dafd6d0dc4596b03150aeade1a09dfb29596ac. Co-authored-by: Yauheni Pakala --- .gitignore | 6 +- .travis.yml | 2 +- README.md | 13 + analysis_options.yaml | 11 +- .../flutter_ble_lib/FlutterBleLibPlugin.java | 12 +- .../device_details/device_detail_view.dart | 24 +- .../device_details/device_details_bloc.dart | 195 +++--- .../devices_details_bloc_provider.dart | 18 +- .../lib/device_details/view/button_view.dart | 2 +- .../view/logs_container_view.dart | 18 +- example/lib/devices_list/devices_bloc.dart | 45 +- .../devices_list/devices_bloc_provider.dart | 15 +- .../lib/devices_list/devices_list_view.dart | 35 +- example/lib/devices_list/hex_painter.dart | 4 +- example/lib/model/ble_device.dart | 4 +- example/lib/repository/device_repository.dart | 16 +- .../bluetooth_state_toggle_scenario.dart | 4 +- .../peripheral_test_operations.dart | 10 +- .../sensor_tag_test_scenario.dart | 5 +- example/pubspec.yaml | 12 +- example/test/widget_test.dart | 2 +- lib/ble_manager.dart | 16 +- lib/characteristic.dart | 49 +- lib/descriptor.dart | 28 +- lib/error/ble_error.dart | 26 +- lib/peripheral.dart | 20 +- lib/scan_result.dart | 73 ++- lib/service.dart | 22 +- lib/src/_managers_for_classes.dart | 10 +- lib/src/base_entities.dart | 10 +- lib/src/bridge/characteristics_mixin.dart | 117 ++-- lib/src/bridge/device_connection_mixin.dart | 26 +- lib/src/bridge/lib_core.dart | 40 +- lib/src/bridge/scanning_mixin.dart | 37 +- lib/src/internal_ble_manager.dart | 31 +- lib/src/util/_transaction_id_generator.dart | 5 + pubspec.yaml | 14 +- test/analysis_options.yaml | 8 + test/ble_manager_test.dart | 2 +- test/characteristic_test.dart | 56 +- test/characteristic_test.mocks.dart | 344 ++++++++++ test/descriptor_test.dart | 16 +- test/descriptor_test.mocks.dart | 168 +++++ test/json/ble_error_jsons.dart | 5 +- test/mock/mocks.dart | 1 - test/service_test.dart | 53 +- test/service_test.mocks.dart | 471 ++++++++++++++ test/src/bridge/lib_core_test.dart | 351 ++++++---- test/src/bridge/lib_core_test.mocks.dart | 604 ++++++++++++++++++ test/test_util/characteristic_generator.dart | 13 +- .../characteristic_generator.mocks.dart | 89 +++ test/test_util/descriptor_generator.dart | 2 +- 52 files changed, 2572 insertions(+), 588 deletions(-) create mode 100644 test/analysis_options.yaml create mode 100644 test/characteristic_test.mocks.dart create mode 100644 test/descriptor_test.mocks.dart create mode 100644 test/service_test.mocks.dart create mode 100644 test/src/bridge/lib_core_test.mocks.dart create mode 100644 test/test_util/characteristic_generator.mocks.dart diff --git a/.gitignore b/.gitignore index a8aec041..f980fc98 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,8 @@ lib/generated/ example/lib/generated/ example/.flutter-plugins-dependencies .dart_tool/ -example/ios/Podfile.lock \ No newline at end of file +example/ios/Podfile.lock +.vscode/settings.json +org.eclipse.buildship.core.prefs +.project +.classpath diff --git a/.travis.yml b/.travis.yml index 25478c46..87f9e609 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,7 +47,7 @@ _android_job_template: &android_job_template _ios_job_template: &ios_job_template language: objective-c os: osx - osx_image: xcode11.6 + osx_image: xcode12.2 xcode_workspave: example/ios/Runner.xcworkspace xcode_scheme: Runner before_script: diff --git a/README.md b/README.md index 84d6651b..b678302d 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,19 @@ Add [Privacy - Bluetooth Always Usage Description](https://developer.apple.com/d ... ``` +#### Background mode + +To support background capabilities add [The `bluetooth-central` Background Execution Mode](https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html#//apple_ref/doc/uid/TP40013257-CH7-SW6) key to `[project]/ios/Runner/Info.plist` file. + +```xml +... +UIBackgroundModes + + bluetooth-central + +... +``` + ## Usage The library is organised around a few base entities, which are: diff --git a/analysis_options.yaml b/analysis_options.yaml index d4fcc1ad..7ddec7bd 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1 +1,10 @@ -include: package:pedantic/analysis_options.yaml \ No newline at end of file +include: package:pedantic/analysis_options.yaml + +analyzer: + exclude: [test/**] + +linter: + rules: + prefer_single_quotes: false + omit_local_variable_types: false + unawaited_futures: false diff --git a/android/src/main/java/com/polidea/flutter_ble_lib/FlutterBleLibPlugin.java b/android/src/main/java/com/polidea/flutter_ble_lib/FlutterBleLibPlugin.java index 8176b792..94b5469f 100644 --- a/android/src/main/java/com/polidea/flutter_ble_lib/FlutterBleLibPlugin.java +++ b/android/src/main/java/com/polidea/flutter_ble_lib/FlutterBleLibPlugin.java @@ -149,7 +149,9 @@ public void onEvent(Integer restoreStateIdentifier) { } private void destroyClient(Result result) { - bleAdapter.destroyClient(); + if (bleAdapter != null) { + bleAdapter.destroyClient(); + } scanningStreamHandler.onComplete(); connectionStateStreamHandler.onComplete(); bleAdapter = null; @@ -177,13 +179,17 @@ public void onError(BleError error) { } private void stopDeviceScan(Result result) { - bleAdapter.stopDeviceScan(); + if (bleAdapter != null) { + bleAdapter.stopDeviceScan(); + } scanningStreamHandler.onComplete(); result.success(null); } private void cancelTransaction(MethodCall call, Result result) { - bleAdapter.cancelTransaction(call.argument(ArgumentKey.TRANSACTION_ID)); + if (bleAdapter != null) { + bleAdapter.cancelTransaction(call.argument(ArgumentKey.TRANSACTION_ID)); + } result.success(null); } } diff --git a/example/lib/device_details/device_detail_view.dart b/example/lib/device_details/device_detail_view.dart index dc8948a7..529314ca 100644 --- a/example/lib/device_details/device_detail_view.dart +++ b/example/lib/device_details/device_detail_view.dart @@ -13,8 +13,8 @@ class DeviceDetailsView extends StatefulWidget { } class DeviceDetailsViewState extends State { - DeviceDetailsBloc _deviceDetailsBloc; - StreamSubscription _appStateSubscription; + DeviceDetailsBloc? _deviceDetailsBloc; + StreamSubscription? _appStateSubscription; bool _shouldRunOnResume = true; @@ -33,9 +33,9 @@ class DeviceDetailsViewState extends State { void _onResume() { Fimber.d("onResume"); - _deviceDetailsBloc.init(); + _deviceDetailsBloc?.init(); _appStateSubscription = - _deviceDetailsBloc.disconnectedDevice.listen((bleDevice) async { + _deviceDetailsBloc?.disconnectedDevice.listen((bleDevice) async { Fimber.d("navigate to details"); _onPause(); Navigator.pop(context); @@ -46,8 +46,8 @@ class DeviceDetailsViewState extends State { void _onPause() { Fimber.d("onPause"); - _appStateSubscription.cancel(); - _deviceDetailsBloc.dispose(); + _appStateSubscription?.cancel(); + _deviceDetailsBloc?.dispose(); } @override @@ -59,9 +59,13 @@ class DeviceDetailsViewState extends State { @override Widget build(BuildContext context) { + final deviceDetailsBloc = _deviceDetailsBloc; return WillPopScope( onWillPop: () { - return _deviceDetailsBloc.disconnect().then((_) { + if (deviceDetailsBloc == null) { + return Future.value(true); + } + return deviceDetailsBloc.disconnect().then((_) { return false; }); }, @@ -86,8 +90,10 @@ class DeviceDetailsViewState extends State { ), body: TabBarView( children: [ - AutoTestView(_deviceDetailsBloc), - ManualTestView(_deviceDetailsBloc), + if (deviceDetailsBloc != null) + AutoTestView(deviceDetailsBloc), + if (deviceDetailsBloc != null) + ManualTestView(deviceDetailsBloc), ], )), ), diff --git a/example/lib/device_details/device_details_bloc.dart b/example/lib/device_details/device_details_bloc.dart index 693de732..b237dc43 100644 --- a/example/lib/device_details/device_details_bloc.dart +++ b/example/lib/device_details/device_details_bloc.dart @@ -7,39 +7,43 @@ import 'package:flutter_ble_lib_example/repository/device_repository.dart'; import 'package:flutter_ble_lib_example/test_scenarios/test_scenarios.dart'; import 'package:rxdart/rxdart.dart'; +import '../model/ble_device.dart'; +import '../repository/device_repository.dart'; + class DeviceDetailsBloc { final BleManager _bleManager; final DeviceRepository _deviceRepository; - BehaviorSubject _deviceController; - - ValueObservable get device => _deviceController.stream; + late final BleDevice _bleDevice; BehaviorSubject _connectionStateController; - ValueObservable get connectionState => + ValueStream get connectionState => _connectionStateController.stream; Subject> _logsController; - Observable> get logs => _logsController.stream; + Stream> get logs => _logsController.stream; - Stream get disconnectedDevice => _deviceRepository.pickedDevice - .skipWhile((bleDevice) => bleDevice != null); + Stream get disconnectedDevice => _deviceRepository.pickedDevice + .skipWhile((bleDevice) => bleDevice != null).cast(); List _logs = []; - Logger log; - Logger logError; - - DeviceDetailsBloc(this._deviceRepository, this._bleManager) { - var device = _deviceRepository.pickedDevice.value; - _deviceController = BehaviorSubject.seeded(device); - + late Logger log; + late Logger logError; + + DeviceDetailsBloc({ + DeviceRepository? deviceRepository, + BleManager? bleManager + }) + : _deviceRepository = deviceRepository ?? DeviceRepository(), + _bleManager = bleManager ?? BleManager(), _connectionStateController = - BehaviorSubject.seeded( - PeripheralConnectionState.disconnected); - - _logsController = PublishSubject>(); + BehaviorSubject.seeded( + PeripheralConnectionState.disconnected + ), + _logsController = PublishSubject>() { + _bleDevice = _deviceRepository.pickedDevice.value!; log = (text) { var now = DateTime.now(); @@ -73,169 +77,132 @@ class DeviceDetailsBloc { Future disconnectManual() async { _clearLogs(); - if (await _deviceController.stream.value.peripheral.isConnected()) { + final bleDevice = _bleDevice; + if (await bleDevice.peripheral.isConnected()) { log("DISCONNECTING..."); - await _deviceController.stream.value.peripheral - .disconnectOrCancelConnection(); + await bleDevice.peripheral.disconnectOrCancelConnection(); } log("Disconnected!"); } void readRssi() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .testReadingRssi(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .testReadingRssi(); } void discovery() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .discovery(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .discovery(); } void fetchConnectedDevices() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .fetchConnectedDevice(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .fetchConnectedDevice(); } void fetchKnownDevices() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .fetchKnownDevice(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .fetchKnownDevice(); } void readCharacteristicForPeripheral() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .readCharacteristicForPeripheral(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .readCharacteristicForPeripheral(); } void readCharacteristicForService() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .readCharacteristicForService(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .readCharacteristicForService(); } void readCharacteristicDirectly() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .readCharacteristic(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .readCharacteristic(); } void writeCharacteristicForPeripheral() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .writeCharacteristicForPeripheral(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .writeCharacteristicForPeripheral(); } void writeCharacteristicForService() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .writeCharacteristicForService(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .writeCharacteristicForService(); } void writeCharacteristicDirectly() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .writeCharacteristic(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .writeCharacteristic(); } void monitorCharacteristicForPeripheral() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) .monitorCharacteristicForPeripheral(); - }); } void monitorCharacteristicForService() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .monitorCharacteristicForService(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .monitorCharacteristicForService(); } void monitorCharacteristicDirectly() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .monitorCharacteristic(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .monitorCharacteristic(); } void disableBluetooth() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .disableBluetooth(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .disableBluetooth(); } void enableBluetooth() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .enableBluetooth(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .enableBluetooth(); } void fetchBluetoothState() { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - PeripheralTestOperations(_bleManager, bleDevice.peripheral, log, logError) - .fetchBluetoothState(); - }); + PeripheralTestOperations(_bleManager, _bleDevice.peripheral, log, logError) + .fetchBluetoothState(); } Future connect() async { _clearLogs(); - _deviceController.stream.listen((bleDevice) async { - var peripheral = bleDevice.peripheral; - - peripheral - .observeConnectionState( - emitCurrentValue: true, completeOnDisconnect: true) - .listen((connectionState) { - log('Observed new connection state: \n$connectionState'); - _connectionStateController.add(connectionState); - }); - - try { - log("Connecting to ${peripheral.name}"); - await peripheral.connect(); - log("Connected!"); - } on BleError catch (e) { - logError(e.toString()); - } + var peripheral = _bleDevice.peripheral; + + peripheral + .observeConnectionState( + emitCurrentValue: true, completeOnDisconnect: true) + .listen((connectionState) { + log('Observed new connection state: \n$connectionState'); + _connectionStateController.add(connectionState); }); + + try { + log("Connecting to ${peripheral.name}"); + await peripheral.connect(); + log("Connected!"); + } on BleError catch (e) { + logError(e.toString()); + } } void dispose() async { - await _deviceController.drain(); - _deviceController.close(); - await _connectionStateController.drain(); _connectionStateController.close(); } @@ -268,16 +235,14 @@ class DeviceDetailsBloc { void startAutoTest() { _clearLogs(); - _deviceController.stream.listen((bleDevice) { - Fimber.d("got bleDevice: $bleDevice"); - bleDevice.peripheral.isConnected().then((isConnected) { - Fimber.d('The device is connected: $isConnected'); - if (!isConnected) { - _connectTo(bleDevice); - } - }).catchError((error) { - logError('Connection problem: ${error.toString()}'); - }); + Fimber.d("got bleDevice: $_bleDevice"); + _bleDevice.peripheral.isConnected().then((isConnected) { + Fimber.d('The device is connected: $isConnected'); + if (!isConnected) { + _connectTo(_bleDevice); + } + }).catchError((error) { + logError('Connection problem: ${error.toString()}'); }); } diff --git a/example/lib/device_details/devices_details_bloc_provider.dart b/example/lib/device_details/devices_details_bloc_provider.dart index 72952eaa..26930f91 100644 --- a/example/lib/device_details/devices_details_bloc_provider.dart +++ b/example/lib/device_details/devices_details_bloc_provider.dart @@ -1,24 +1,22 @@ import 'package:flutter/widgets.dart'; -import 'package:flutter_ble_lib_example/repository/device_repository.dart'; -import 'package:flutter_ble_lib/flutter_ble_lib.dart'; import 'device_details_bloc.dart'; class DeviceDetailsBlocProvider extends InheritedWidget { - final DeviceDetailsBloc deviceDetailsBloc; + final DeviceDetailsBloc _deviceDetailsBloc; DeviceDetailsBlocProvider({ - Key key, - DeviceDetailsBloc deviceDetailsBloc, - Widget child, - }) : deviceDetailsBloc = deviceDetailsBloc ?? - DeviceDetailsBloc(DeviceRepository(), BleManager()), - super(key: key, child: child); + Key? key, + DeviceDetailsBloc? deviceDetailsBloc, + required Widget child, + }) + : _deviceDetailsBloc = deviceDetailsBloc ?? DeviceDetailsBloc(), + super(key: key, child: child); @override bool updateShouldNotify(InheritedWidget oldWidget) => true; static DeviceDetailsBloc of(BuildContext context) => context .dependOnInheritedWidgetOfExactType() - .deviceDetailsBloc; + !._deviceDetailsBloc; } diff --git a/example/lib/device_details/view/button_view.dart b/example/lib/device_details/view/button_view.dart index f9789f61..a5457fda 100644 --- a/example/lib/device_details/view/button_view.dart +++ b/example/lib/device_details/view/button_view.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; class ButtonView extends StatelessWidget { final String _text; - final Function action; + final void Function()? action; ButtonView(this._text, {this.action}); diff --git a/example/lib/device_details/view/logs_container_view.dart b/example/lib/device_details/view/logs_container_view.dart index 37398d9f..81d78ff7 100644 --- a/example/lib/device_details/view/logs_container_view.dart +++ b/example/lib/device_details/view/logs_container_view.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_ble_lib_example/device_details/device_details_bloc.dart'; -import 'package:rxdart/rxdart.dart'; class LogsContainerView extends StatelessWidget { - final Observable> _logs; + final Stream> _logs; LogsContainerView(this._logs); @@ -31,8 +30,9 @@ class LogsContainerView extends StatelessWidget { } Widget _buildLogs(BuildContext context, AsyncSnapshot> logs) { + final data = logs.data; return ListView.builder( - itemCount: logs.data.length, + itemCount: data?.length, shrinkWrap: true, itemBuilder: (buildContext, index) => Container( decoration: BoxDecoration( @@ -55,15 +55,17 @@ class LogsContainerView extends StatelessWidget { Padding( padding: const EdgeInsets.only(right: 8.0), child: Text( - logs.data[index].time, + data?[index].time ?? "", style: TextStyle(fontSize: 9), ), ), Flexible( - child: Text(logs.data[index].content, - overflow: TextOverflow.ellipsis, - softWrap: true, - style: TextStyle(fontSize: 13)), + child: Text( + data?[index].content ?? "", + overflow: TextOverflow.ellipsis, + softWrap: true, + style: TextStyle(fontSize: 13) + ), ), ], ), diff --git a/example/lib/devices_list/devices_bloc.dart b/example/lib/devices_list/devices_bloc.dart index 8b0434ba..7bfeca52 100644 --- a/example/lib/devices_list/devices_bloc.dart +++ b/example/lib/devices_list/devices_bloc.dart @@ -15,21 +15,25 @@ class DevicesBloc { StreamController _devicePickerController = StreamController(); - StreamSubscription _scanSubscription; - StreamSubscription _devicePickerSubscription; + StreamSubscription? _scanSubscription; + StreamSubscription? _devicePickerSubscription; - ValueObservable> get visibleDevices => _visibleDevicesController.stream; + ValueStream> get visibleDevices => _visibleDevicesController.stream; Sink get devicePicker => _devicePickerController.sink; - DeviceRepository _deviceRepository; - BleManager _bleManager; - PermissionStatus _locationPermissionStatus = PermissionStatus.unknown; + final DeviceRepository _deviceRepository; + final BleManager _bleManager; Stream get pickedDevice => _deviceRepository.pickedDevice - .skipWhile((bleDevice) => bleDevice == null); + .skipWhile((bleDevice) => bleDevice == null).cast(); - DevicesBloc(this._deviceRepository, this._bleManager); + DevicesBloc({ + DeviceRepository? deviceRepository, + BleManager? bleManager + }) + : _deviceRepository = deviceRepository ?? DeviceRepository(), + _bleManager = bleManager ?? BleManager(); void _handlePickedDevice(BleDevice bleDevice) { _deviceRepository.pickDevice(bleDevice); @@ -37,7 +41,7 @@ class DevicesBloc { void dispose() { Fimber.d("cancel _devicePickerSubscription"); - _devicePickerSubscription.cancel(); + _devicePickerSubscription?.cancel(); _visibleDevicesController.close(); _devicePickerController.close(); _scanSubscription?.cancel(); @@ -82,7 +86,7 @@ class DevicesBloc { .createClient( restoreStateIdentifier: "example-restore-state-identifier", restoreStateAction: (peripherals) { - peripherals?.forEach((peripheral) { + peripherals.forEach((peripheral) { Fimber.d("Restored peripheral: ${peripheral.name}"); }); } @@ -94,12 +98,11 @@ class DevicesBloc { Future _checkPermissions() async { if (Platform.isAndroid) { - var permissionStatus = await PermissionHandler() - .requestPermissions([PermissionGroup.location]); - - _locationPermissionStatus = permissionStatus[PermissionGroup.location]; - - if (_locationPermissionStatus != PermissionStatus.granted) { + var locGranted = await Permission.location.isGranted; + if (locGranted == false) { + locGranted = (await Permission.location.request()).isGranted; + } + if (locGranted == false) { return Future.error(Exception("Location permission not granted")); } } @@ -107,12 +110,12 @@ class DevicesBloc { Future _waitForBluetoothPoweredOn() async { Completer completer = Completer(); - StreamSubscription subscription; + StreamSubscription? subscription; subscription = _bleManager .observeBluetoothState(emitCurrentValue: true) .listen((bluetoothState) async { if (bluetoothState == BluetoothState.POWERED_ON && !completer.isCompleted) { - await subscription.cancel(); + await subscription?.cancel(); completer.complete(); } }); @@ -123,9 +126,9 @@ class DevicesBloc { void _startScan() { Fimber.d("Ble start scan"); _scanSubscription = _bleManager.startPeripheralScan() - .listen((ScanResult scanResult) { + .listen((scanResult) { var bleDevice = BleDevice(scanResult); - if (scanResult.advertisementData.localName != null && !bleDevices.contains(bleDevice)) { + if (!bleDevices.contains(bleDevice)) { Fimber.d('found new device ${scanResult.advertisementData.localName} ${scanResult.peripheral.identifier}'); bleDevices.add(bleDevice); _visibleDevicesController.add(bleDevices.sublist(0)); @@ -135,7 +138,7 @@ class DevicesBloc { Future refresh() async { await _bleManager.stopPeripheralScan(); - await _scanSubscription.cancel(); + await _scanSubscription?.cancel(); bleDevices.clear(); _visibleDevicesController.add(bleDevices.sublist(0)); diff --git a/example/lib/devices_list/devices_bloc_provider.dart b/example/lib/devices_list/devices_bloc_provider.dart index 61156c86..bc37ae17 100644 --- a/example/lib/devices_list/devices_bloc_provider.dart +++ b/example/lib/devices_list/devices_bloc_provider.dart @@ -1,17 +1,14 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_ble_lib_example/devices_list/devices_bloc.dart'; -import 'package:flutter_ble_lib_example/repository/device_repository.dart'; -import 'package:flutter_ble_lib/flutter_ble_lib.dart'; class DevicesBlocProvider extends InheritedWidget { - final DevicesBloc devicesBloc; + final DevicesBloc _devicesBloc; DevicesBlocProvider({ - Key key, - DevicesBloc devicesBloc, - Widget child, - }) : devicesBloc = - devicesBloc ?? DevicesBloc(DeviceRepository(), BleManager()), + Key? key, + DevicesBloc? devicesBloc, + required Widget child, + }) : _devicesBloc = devicesBloc ?? DevicesBloc(), super(key: key, child: child); @override @@ -19,5 +16,5 @@ class DevicesBlocProvider extends InheritedWidget { static DevicesBloc of(BuildContext context) => context .dependOnInheritedWidgetOfExactType() - .devicesBloc; + !._devicesBloc; } diff --git a/example/lib/devices_list/devices_list_view.dart b/example/lib/devices_list/devices_list_view.dart index e5ebadc1..a73a90ce 100644 --- a/example/lib/devices_list/devices_list_view.dart +++ b/example/lib/devices_list/devices_list_view.dart @@ -17,8 +17,8 @@ class DevicesListScreen extends StatefulWidget { } class DeviceListScreenState extends State { - DevicesBloc _devicesBloc; - StreamSubscription _appStateSubscription; + DevicesBloc? _devicesBloc; + StreamSubscription? _appStateSubscription; bool _shouldRunOnResume = true; @override @@ -29,14 +29,19 @@ class DeviceListScreenState extends State { void _onPause() { Fimber.d("onPause"); - _appStateSubscription.cancel(); - _devicesBloc.dispose(); + _appStateSubscription?.cancel(); + _devicesBloc?.dispose(); } void _onResume() { Fimber.d("onResume"); - _devicesBloc.init(); - _appStateSubscription = _devicesBloc.pickedDevice.listen((bleDevice) async { + final devicesBloc = _devicesBloc; + if (devicesBloc == null) { + Fimber.d("onResume:: no devicesBloc present"); + return; + } + devicesBloc.init(); + _appStateSubscription = devicesBloc.pickedDevice.listen((bleDevice) async { Fimber.d("navigate to details"); _onPause(); await Navigator.pushNamed(context, "/details"); @@ -67,16 +72,20 @@ class DeviceListScreenState extends State { _shouldRunOnResume = false; _onResume(); } + final devicesBloc = _devicesBloc; + if (devicesBloc == null) { + throw Exception(); + } return Scaffold( appBar: AppBar( title: Text('Bluetooth devices'), ), body: StreamBuilder>( - initialData: _devicesBloc.visibleDevices.value, - stream: _devicesBloc.visibleDevices, + initialData: devicesBloc.visibleDevices.valueWrapper?.value ?? [], + stream: devicesBloc.visibleDevices, builder: (context, snapshot) => RefreshIndicator( - onRefresh: _devicesBloc.refresh, - child: DevicesList(_devicesBloc, snapshot.data), + onRefresh: devicesBloc.refresh , + child: DevicesList(devicesBloc, snapshot.data), ), ), ); @@ -103,17 +112,17 @@ class DeviceListScreenState extends State { } class DevicesList extends ListView { - DevicesList(DevicesBloc devicesBloc, List devices) + DevicesList(DevicesBloc devicesBloc, List? devices) : super.separated( separatorBuilder: (context, index) => Divider( color: Colors.grey[300], height: 0, indent: 0, ), - itemCount: devices.length, + itemCount: devices?.length ?? 0, itemBuilder: (context, i) { Fimber.d("Build row for $i"); - return _buildRow(context, devices[i], + return _buildRow(context, devices![i], _createTapListener(devicesBloc, devices[i])); }); diff --git a/example/lib/devices_list/hex_painter.dart b/example/lib/devices_list/hex_painter.dart index b2416c73..d8ea409d 100644 --- a/example/lib/devices_list/hex_painter.dart +++ b/example/lib/devices_list/hex_painter.dart @@ -5,8 +5,8 @@ class HexPainter extends CustomPainter { final Color _backgroundColor; HexPainter({ - Color backgroundColor, - Color foregroundColor, + Color? backgroundColor, + Color? foregroundColor, }) : _backgroundColor = backgroundColor ?? Colors.white, _foregroundColor = foregroundColor ?? Colors.black, super(); diff --git a/example/lib/model/ble_device.dart b/example/lib/model/ble_device.dart index eb8c1d7a..c97e068b 100644 --- a/example/lib/model/ble_device.dart +++ b/example/lib/model/ble_device.dart @@ -19,8 +19,6 @@ class BleDevice { @override bool operator ==(other) => other is BleDevice && - this.name != null && - other.name != null && compareAsciiLowerCase(this.name, other.name) == 0 && this.id == other.id; @@ -39,7 +37,7 @@ extension on ScanResult { DeviceCategory get category { if (name == "SensorTag") { return DeviceCategory.sensorTag; - } else if (name != null && name.startsWith("Hex")) { + } else if (name.startsWith("Hex")) { return DeviceCategory.hex; } else { return DeviceCategory.other; diff --git a/example/lib/repository/device_repository.dart b/example/lib/repository/device_repository.dart index b9fa1978..e06dca12 100644 --- a/example/lib/repository/device_repository.dart +++ b/example/lib/repository/device_repository.dart @@ -4,26 +4,26 @@ import 'package:rxdart/rxdart.dart'; class MissingPickedDeviceException implements Exception {} class DeviceRepository { - static BleDevice _bleDevice; - BehaviorSubject _deviceController; + static BleDevice? _bleDevice; + BehaviorSubject _deviceController; static final DeviceRepository _deviceRepository = DeviceRepository._internal(); - factory DeviceRepository() { + factory DeviceRepository() { return _deviceRepository; } - DeviceRepository._internal() { - _deviceController = BehaviorSubject.seeded(_bleDevice); - } + DeviceRepository._internal() + : _deviceController = BehaviorSubject.seeded(_bleDevice); + - void pickDevice(BleDevice bleDevice) { + void pickDevice(BleDevice? bleDevice) { _bleDevice = bleDevice; _deviceController.add(_bleDevice); } - ValueObservable get pickedDevice => + ValueStream get pickedDevice => _deviceController.stream.shareValueSeeded(_bleDevice); bool get hasPickedDevice => _bleDevice != null; diff --git a/example/lib/test_scenarios/bluetooth_state_toggle_scenario.dart b/example/lib/test_scenarios/bluetooth_state_toggle_scenario.dart index 043582c8..c6310e4c 100644 --- a/example/lib/test_scenarios/bluetooth_state_toggle_scenario.dart +++ b/example/lib/test_scenarios/bluetooth_state_toggle_scenario.dart @@ -1,7 +1,7 @@ part of test_scenarios; class BluetoothStateTestScenario implements TestScenario { - StreamSubscription _radioStateSubscription; + StreamSubscription? _radioStateSubscription; @override Future runTestScenario(Logger log, Logger errorLogger) async { @@ -24,7 +24,7 @@ class BluetoothStateTestScenario implements TestScenario { try { await bleManager.enableRadio(); } catch (e) { - errorLogger(e); + errorLogger(e.toString()); } log("Enabled radio!"); diff --git a/example/lib/test_scenarios/peripheral_test_operations.dart b/example/lib/test_scenarios/peripheral_test_operations.dart index dffe5d65..295f6292 100644 --- a/example/lib/test_scenarios/peripheral_test_operations.dart +++ b/example/lib/test_scenarios/peripheral_test_operations.dart @@ -6,7 +6,7 @@ class PeripheralTestOperations { final Peripheral peripheral; final Logger log; final Logger logError; - StreamSubscription monitoringStreamSubscription; + StreamSubscription? _monitoringStreamSubscription; final BleManager bleManager; PeripheralTestOperations( @@ -60,9 +60,9 @@ class PeripheralTestOperations { log("Found characteristic \n ${characteristic.uuid}")); //------------ descriptors - List descriptors; + List? descriptors; - var printDescriptors = () => descriptors.forEach((descriptor) { + var printDescriptors = () => descriptors?.forEach((descriptor) { log("Descriptor: ${descriptor.uuid}"); }); @@ -711,8 +711,8 @@ class PeripheralTestOperations { void _startMonitoringTemperature( Stream characteristicUpdates, Function log) async { - await monitoringStreamSubscription?.cancel(); - monitoringStreamSubscription = + await _monitoringStreamSubscription?.cancel(); + _monitoringStreamSubscription = characteristicUpdates.map(_convertToTemperature).listen( (temperature) { log("Temperature updated: ${temperature}C"); diff --git a/example/lib/test_scenarios/sensor_tag_test_scenario.dart b/example/lib/test_scenarios/sensor_tag_test_scenario.dart index 9ce95c4b..b9bde9a2 100644 --- a/example/lib/test_scenarios/sensor_tag_test_scenario.dart +++ b/example/lib/test_scenarios/sensor_tag_test_scenario.dart @@ -4,10 +4,9 @@ class SensorTagTestScenario { PeripheralTestOperations _peripheralTestOperations; SensorTagTestScenario(BleManager bleManager, Peripheral peripheral, - Logger log, Logger logError) { - _peripheralTestOperations = + Logger log, Logger logError) + : _peripheralTestOperations = PeripheralTestOperations(bleManager, peripheral, log, logError); - } Future runTestScenario() async { _peripheralTestOperations diff --git a/example/pubspec.yaml b/example/pubspec.yaml index d10740c4..72b65611 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,10 +1,10 @@ name: flutter_ble_lib_example description: Demonstrates how to use the flutter_ble_lib plugin. -version: 0.0.1+1 +version: 0.1.0+2 publish_to: "none" environment: - sdk: ">=2.6.0 <3.0.0" + sdk: ">=2.12.0-0 <3.0.0" dependencies: flutter: @@ -12,12 +12,12 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 + cupertino_icons: ^1.0.2 - rxdart: ^0.22.0 - fimber: ^0.1.10 + rxdart: ^0.26.0 + fimber: ^0.6.1 - permission_handler: ^4.2.0+hotfix.3 + permission_handler: ^6.1.1 dev_dependencies: flutter_test: diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index def6a68d..bb48b547 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -19,7 +19,7 @@ void main() { expect( find.byWidgetPredicate( (Widget widget) => - widget is Text && widget.data.startsWith('Running on:'), + widget is Text, ), findsOneWidget, ); diff --git a/lib/ble_manager.dart b/lib/ble_manager.dart index 3830d700..97ae9eb8 100644 --- a/lib/ble_manager.dart +++ b/lib/ble_manager.dart @@ -31,12 +31,16 @@ enum LogLevel { none, verbose, debug, info, warning, error } ///}); ///``` abstract class BleManager { - static BleManager _instance; + static BleManager? _instance; factory BleManager() { - _instance ??= InternalBleManager(); + var instance = _instance; + if (instance == null) { + instance = InternalBleManager(); + _instance = instance; + } - return _instance; + return instance; } /// Cancels transaction's return, resulting in [BleError] with @@ -62,8 +66,8 @@ abstract class BleManager { /// await BleManager().createClient(); /// ``` Future createClient({ - String restoreStateIdentifier, - RestoreStateAction restoreStateAction, + String? restoreStateIdentifier, + RestoreStateAction? restoreStateAction, }); /// Frees native resources. @@ -150,7 +154,7 @@ abstract class BleManager { /// On Android [peripheralId] scanned on one device may or may not be /// recognized on a different Android device depending on peripheral’s /// implementation and changes in future OS releases. - Peripheral createUnsafePeripheral(String peripheralId, {String name}); + Peripheral createUnsafePeripheral(String peripheralId, {String? name}); } /// State of the Bluetooth Adapter. diff --git a/lib/characteristic.dart b/lib/characteristic.dart index 070de610..8356f45b 100644 --- a/lib/characteristic.dart +++ b/lib/characteristic.dart @@ -18,9 +18,9 @@ abstract class _CharacteristicMetadata { /// a characteristic’s value, and how you access the descriptors. class Characteristic extends InternalCharacteristic { /// The [Service] containing this characteristic. - Service service; + final Service service; - ManagerForCharacteristic _manager; + final ManagerForCharacteristic _manager; /// The UUID of this characteristic. String uuid; @@ -42,23 +42,22 @@ class Characteristic extends InternalCharacteristic { Characteristic.fromJson(Map jsonObject, Service service, ManagerForCharacteristic manager) - : super(jsonObject[_CharacteristicMetadata.id]) { - _manager = manager; - this.service = service; - uuid = jsonObject[_CharacteristicMetadata.uuid]; - isReadable = jsonObject[_CharacteristicMetadata.isReadable]; - isWritableWithResponse = - jsonObject[_CharacteristicMetadata.isWritableWithResponse]; - isWritableWithoutResponse = - jsonObject[_CharacteristicMetadata.isWritableWithoutResponse]; - isNotifiable = jsonObject[_CharacteristicMetadata.isNotifiable]; - isIndicatable = jsonObject[_CharacteristicMetadata.isIndicatable]; - } + : _manager = manager, + service = service, + uuid = jsonObject[_CharacteristicMetadata.uuid], + isReadable = jsonObject[_CharacteristicMetadata.isReadable], + isWritableWithResponse = + jsonObject[_CharacteristicMetadata.isWritableWithResponse], + isWritableWithoutResponse = + jsonObject[_CharacteristicMetadata.isWritableWithoutResponse], + isNotifiable = jsonObject[_CharacteristicMetadata.isNotifiable], + isIndicatable = jsonObject[_CharacteristicMetadata.isIndicatable], + super(jsonObject[_CharacteristicMetadata.id]); /// Reads the value of this characteristic. /// /// The value can be read only if [isReadable] is `true`. - Future read({String transactionId}) => + Future read({String? transactionId}) => _manager.readCharacteristicForIdentifier( service.peripheral, this, @@ -73,7 +72,7 @@ class Characteristic extends InternalCharacteristic { Future write( Uint8List value, bool withResponse, { - String transactionId, + String? transactionId, }) => _manager.writeCharacteristicForIdentifier( service.peripheral, @@ -91,7 +90,7 @@ class Characteristic extends InternalCharacteristic { /// Subscribing to the returned object enables the notifications/indications /// on the peripheral. Cancelling the last subscription disables the /// notifications/indications on this characteristic. - Stream monitor({String transactionId}) => + Stream monitor({String? transactionId}) => _manager.monitorCharacteristicForIdentifier( service.peripheral, this, @@ -105,7 +104,7 @@ class Characteristic extends InternalCharacteristic { /// Reads the value of a [Descriptor] identified by [descriptorUuid]. Future readDescriptor( String descriptorUuid, { - String transactionId, + String? transactionId, }) => _manager.readDescriptorForCharacteristic( this, @@ -117,7 +116,7 @@ class Characteristic extends InternalCharacteristic { Future writeDescriptor( String descriptorUuid, Uint8List value, { - String transactionId, + String? transactionId, }) => _manager.writeDescriptorForCharacteristic( this, @@ -132,7 +131,6 @@ class Characteristic extends InternalCharacteristic { other is Characteristic && runtimeType == other.runtimeType && service == other.service && - _manager == other._manager && uuid == other.uuid && isReadable == other.isReadable && isWritableWithResponse == other.isWritableWithResponse && @@ -170,21 +168,22 @@ class Characteristic extends InternalCharacteristic { /// /// This type is created to support chaining of operations on the characteristic /// when it was first read from [Peripheral] or [Service]. -class CharacteristicWithValue extends Characteristic with WithValue { +class CharacteristicWithValue extends Characteristic { + Uint8List value; + CharacteristicWithValue.fromJson( Map jsonObject, Service service, ManagerForCharacteristic manager, - ) : super.fromJson(jsonObject, service, manager) { - value = base64Decode(jsonObject[_CharacteristicMetadata.value]); - } + ) : value = base64Decode(jsonObject[_CharacteristicMetadata.value]), + super.fromJson(jsonObject, service, manager); @override bool operator ==(Object other) { return identical(this, other) || super == other && other is CharacteristicWithValue && - value?.toString() == other.value?.toString() && + value.toString() == other.value.toString() && runtimeType == other.runtimeType; } diff --git a/lib/descriptor.dart b/lib/descriptor.dart index 6e43cee5..b1e4f0ad 100644 --- a/lib/descriptor.dart +++ b/lib/descriptor.dart @@ -7,27 +7,26 @@ abstract class _DescriptorMetadata { } class Descriptor extends InternalDescriptor { - ManagerForDescriptor _manager; - Characteristic characteristic; - String uuid; + final ManagerForDescriptor _manager; + final Characteristic characteristic; + final String uuid; Descriptor.fromJson( Map jsonObject, Characteristic characteristic, ManagerForDescriptor manager, - ) : super(jsonObject[_DescriptorMetadata.id]) { - _manager = manager; - this.characteristic = characteristic; - uuid = jsonObject[_DescriptorMetadata.uuid]; - } + ) : _manager = manager, + characteristic = characteristic, + uuid = jsonObject[_DescriptorMetadata.uuid], + super(jsonObject[_DescriptorMetadata.id]); - Future read({String transactionId}) => + Future read({String? transactionId}) => _manager.readDescriptorForIdentifier( this, transactionId ?? TransactionIdGenerator.getNextId(), ); - Future write(Uint8List value, {String transactionId}) => + Future write(Uint8List value, {String? transactionId}) => _manager.writeDescriptorForIdentifier( this, value, @@ -48,12 +47,13 @@ class Descriptor extends InternalDescriptor { _manager.hashCode ^ characteristic.hashCode ^ uuid.hashCode; } -class DescriptorWithValue extends Descriptor with WithValue { +class DescriptorWithValue extends Descriptor { + Uint8List value; + DescriptorWithValue.fromJson( Map jsonObject, Characteristic characteristic, ManagerForDescriptor manager, - ) : super.fromJson(jsonObject, characteristic, manager) { - value = base64Decode(jsonObject[_DescriptorMetadata.value]); - } + ) : value = base64Decode(jsonObject[_DescriptorMetadata.value]), + super.fromJson(jsonObject, characteristic, manager); } diff --git a/lib/error/ble_error.dart b/lib/error/ble_error.dart index dde9b639..2054e5dd 100644 --- a/lib/error/ble_error.dart +++ b/lib/error/ble_error.dart @@ -3,6 +3,7 @@ part of flutter_ble_lib; abstract class _BleErrorMetadata { static const String errorCode = "errorCode"; static const String attErrorCode = "attErrorCode"; + static const String iosErrorCode = "iosErrorCode"; static const String androidErrorCode = "androidErrorCode"; static const String reason = "reason"; static const String deviceId = "deviceID"; @@ -13,23 +14,24 @@ abstract class _BleErrorMetadata { } class BleError { - BleErrorCode errorCode; - int attErrorCode; - int iosErrorCode; - int androidErrorCode; - String reason; + final BleErrorCode errorCode; + final int? attErrorCode; + final int? iosErrorCode; + final int? androidErrorCode; + final String reason; - String deviceID; - String serviceUuid; - String characteristicUuid; - String descriptorUuid; - String internalMessage; + final String? deviceID; + final String? serviceUuid; + final String? characteristicUuid; + final String? descriptorUuid; + final String? internalMessage; BleError.fromJson(Map json) - : errorCode = BleErrorCode(json[_BleErrorMetadata.errorCode]), + : errorCode = BleErrorCode(json[_BleErrorMetadata.errorCode] ?? 0), attErrorCode = json[_BleErrorMetadata.attErrorCode], + iosErrorCode = json[_BleErrorMetadata.iosErrorCode], androidErrorCode = json[_BleErrorMetadata.androidErrorCode], - reason = json[_BleErrorMetadata.reason], + reason = json[_BleErrorMetadata.reason] ?? "Reason not provided", deviceID = json[_BleErrorMetadata.deviceId], serviceUuid = json[_BleErrorMetadata.serviceUuid], characteristicUuid = json[_BleErrorMetadata.characteristicUuid], diff --git a/lib/peripheral.dart b/lib/peripheral.dart index 61863b05..0d1134e0 100644 --- a/lib/peripheral.dart +++ b/lib/peripheral.dart @@ -17,7 +17,7 @@ class Peripheral { static const int NO_MTU_NEGOTIATION = 0; final ManagerForPeripheral _manager; - String name; + String? name; String identifier; Peripheral.fromJson(Map json, ManagerForPeripheral manager) @@ -51,7 +51,7 @@ class Peripheral { {bool isAutoConnect = false, int requestMtu = NO_MTU_NEGOTIATION, bool refreshGatt = false, - Duration timeout}) => + Duration? timeout}) => _manager.connectToPeripheral(identifier, isAutoConnect: isAutoConnect, requestMtu: requestMtu, @@ -79,7 +79,7 @@ class Peripheral { /// Must be done prior to any other operation concerning those. /// /// Optional [transactionId] could be used to cancel operation. - Future discoverAllServicesAndCharacteristics({String transactionId}) => + Future discoverAllServicesAndCharacteristics({String? transactionId}) => _manager.discoverAllServicesAndCharacteristics( this, transactionId ?? TransactionIdGenerator.getNextId()); @@ -101,7 +101,7 @@ class Peripheral { /// Reads RSSI for the peripheral. /// /// Optional [transactionId] could be used to cancel operation. - Future rssi({String transactionId}) => + Future rssi({String? transactionId}) => _manager.rssi(this, transactionId ?? TransactionIdGenerator.getNextId()); /// Requests new MTU value for current connection and return the negotiation @@ -115,7 +115,7 @@ class Peripheral { /// Optional [transactionId] could be used to cancel operation. /// /// If MTU has been requested in [connect()] this method will end with [BleError]. - Future requestMtu(int mtu, {String transactionId}) => + Future requestMtu(int mtu, {String? transactionId}) => _manager.requestMtu( this, mtu, transactionId ?? TransactionIdGenerator.getNextId()); @@ -128,7 +128,7 @@ class Peripheral { Future readCharacteristic( String serviceUuid, String characteristicUuid, { - String transactionId, + String? transactionId, }) => _manager.readCharacteristicForDevice( this, @@ -148,7 +148,7 @@ class Peripheral { String characteristicUuid, Uint8List value, bool withResponse, { - String transactionId, + String? transactionId, }) => _manager.writeCharacteristicForDevice( this, @@ -183,7 +183,7 @@ class Peripheral { String serviceUuid, String characteristicUuid, String descriptorUuid, { - String transactionId, + String? transactionId, }) => _manager.readDescriptorForPeripheral( this, @@ -205,7 +205,7 @@ class Peripheral { String characteristicUuid, String descriptorUuid, Uint8List value, { - String transactionId, + String? transactionId, }) => _manager.writeDescriptorForPeripheral( this, @@ -229,7 +229,7 @@ class Peripheral { Stream monitorCharacteristic( String serviceUuid, String characteristicUuid, { - String transactionId, + String? transactionId, }) => _manager.monitorCharacteristicForDevice( this, diff --git a/lib/scan_result.dart b/lib/scan_result.dart index 3ae7ce6d..8f29805c 100644 --- a/lib/scan_result.dart +++ b/lib/scan_result.dart @@ -14,49 +14,65 @@ abstract class _ScanResultMetadata { /// A scan result emitted by the scanning operation, containing [Peripheral] and [AdvertisementData]. class ScanResult { - Peripheral peripheral; + final Peripheral peripheral; /// Signal strength of the peripheral in dBm. - int rssi; + final int rssi; /// An indicator whether the peripheral is connectable (iOS only). - bool isConnectable; + final bool? isConnectable; /// A list of UUIDs found in the overflow area of the advertisement data (iOS only). - List overflowServiceUuids; + final List overflowServiceUuids; /// A packet of data advertised by the peripheral. - AdvertisementData advertisementData; - - ScanResult.fromJson(Map json, ManagerForPeripheral manager) - : peripheral = Peripheral.fromJson(json, manager), - rssi = json[_ScanResultMetadata.rssi], - isConnectable = json[_ScanResultMetadata.isConnectable], - overflowServiceUuids = json[_ScanResultMetadata.overflowServiceUuids], - advertisementData = AdvertisementData._fromJson(json); + final AdvertisementData advertisementData; + + ScanResult._( + this.peripheral, + this.rssi, + this.advertisementData, + {this.isConnectable, + List? overflowServiceUuids, + }) : overflowServiceUuids = overflowServiceUuids ?? []; + + + factory ScanResult.fromJson( + Map json, + ManagerForPeripheral manager + ) { + assert(json[_ScanResultMetadata.rssi] is int); + return ScanResult._( + Peripheral.fromJson(json, manager), + json[_ScanResultMetadata.rssi], + AdvertisementData._fromJson(json), + isConnectable: json[_ScanResultMetadata.isConnectable], + overflowServiceUuids: json[_ScanResultMetadata.overflowServiceUuids] + ); + } } /// Data advertised by the [Peripheral]: power level, local name, /// manufacturer's data, advertised [Service]s class AdvertisementData { /// The manufacturer data of the peripheral. - Uint8List manufacturerData; + final Uint8List? manufacturerData; /// A dictionary that contains service-specific advertisement data. - Map serviceData; + final Map? serviceData; /// A list of service UUIDs. - List serviceUuids; + final List? serviceUuids; /// The local name of the [Peripheral]. Might be different than /// [Peripheral.name]. - String localName; + final String? localName; /// The transmit power of the peripheral. - int txPowerLevel; + final int? txPowerLevel; /// A list of solicited service UUIDs. - List solicitedServiceUuids; + final List? solicitedServiceUuids; AdvertisementData._fromJson(Map json) : manufacturerData = @@ -67,23 +83,26 @@ class AdvertisementData { _mapToListOfStringsOrNull(json[_ScanResultMetadata.serviceUuids]), localName = json[_ScanResultMetadata.localName], txPowerLevel = json[_ScanResultMetadata.txPowerLevel], - solicitedServiceUuids = _mapToListOfStringsOrNull( - json[_ScanResultMetadata.solicitedServiceUuids]); + solicitedServiceUuids = + _mapToListOfStringsOrNull( + json[_ScanResultMetadata.solicitedServiceUuids] + ); - static Map _getServiceDataOrNull( - Map serviceData) { + static Map? _getServiceDataOrNull( + Map? serviceData) { return serviceData?.map( (key, value) => MapEntry(key, base64Decode(value)), ); } - static Uint8List _decodeBase64OrNull(String base64Value) { - if (base64Value != null) + static Uint8List? _decodeBase64OrNull(String? base64Value) { + if (base64Value != null) { return base64.decode(base64Value); - else + } else { return null; + } } - static List _mapToListOfStringsOrNull(List values) => - values?.cast(); + static List? _mapToListOfStringsOrNull(List? values) => + values?.cast(); } diff --git a/lib/service.dart b/lib/service.dart index 2b00d663..409ded39 100644 --- a/lib/service.dart +++ b/lib/service.dart @@ -10,7 +10,7 @@ class Service extends InternalService { /// [Peripheral] containing this service. Peripheral peripheral; - ManagerForService _manager; + final ManagerForService _manager; /// The UUID of this service. String uuid; @@ -18,12 +18,10 @@ class Service extends InternalService { Service.fromJson( Map jsonObject, Peripheral peripheral, - ManagerForService managerForService, - ) : super(jsonObject[_ServiceMetadata.id]) { - this.peripheral = peripheral; - uuid = jsonObject[_ServiceMetadata.uuid]; - _manager = managerForService; - } + this._manager, + ) : peripheral = peripheral, + uuid = jsonObject[_ServiceMetadata.uuid], + super(jsonObject[_ServiceMetadata.id]); /// Returns a list of [Characteristic]s of this service. Future> characteristics() => @@ -43,7 +41,7 @@ class Service extends InternalService { String characteristicUuid, Uint8List value, bool withResponse, { - String transactionId, + String? transactionId, }) => _manager.writeCharacteristicForService( peripheral, @@ -61,7 +59,7 @@ class Service extends InternalService { /// [Characteristic.isReadable] is `true` can be read. Future readCharacteristic( String characteristicUuid, { - String transactionId, + String? transactionId, }) => _manager.readCharacteristicForService( peripheral, @@ -80,7 +78,7 @@ class Service extends InternalService { /// monitored. Stream monitorCharacteristic( String characteristicUuid, { - String transactionId, + String? transactionId, }) => _manager.monitorCharacteristicForService( peripheral, @@ -108,7 +106,7 @@ class Service extends InternalService { Future readDescriptor( String characteristicUuid, String descriptorUuid, { - String transactionId, + String? transactionId, }) => _manager.readDescriptorForService( this, @@ -125,7 +123,7 @@ class Service extends InternalService { String characteristicUuid, String descriptorUuid, Uint8List value, { - String transactionId, + String? transactionId, }) => _manager.writeDescriptorForService( this, diff --git a/lib/src/_managers_for_classes.dart b/lib/src/_managers_for_classes.dart index 69a92814..9f951a27 100644 --- a/lib/src/_managers_for_classes.dart +++ b/lib/src/_managers_for_classes.dart @@ -7,10 +7,10 @@ import '_internal.dart'; abstract class ManagerForPeripheral { Future connectToPeripheral( String peripheralIdentifier, { - bool isAutoConnect, - int requestMtu, - bool refreshGatt, - Duration timeout, + required bool isAutoConnect, + required int requestMtu, + required bool refreshGatt, + Duration? timeout, }); Future isPeripheralConnected(String peripheralIdentifier); @@ -39,7 +39,7 @@ abstract class ManagerForPeripheral { String transactionId, ); - Future requestMtu( + Future requestMtu( Peripheral peripheral, int mtu, String transactionId, diff --git a/lib/src/base_entities.dart b/lib/src/base_entities.dart index 15a30288..f332e3a0 100644 --- a/lib/src/base_entities.dart +++ b/lib/src/base_entities.dart @@ -1,24 +1,20 @@ part of _internal; class InternalService { - int _id; + final int _id; InternalService(this._id); } class InternalCharacteristic { - int _id; + final int _id; InternalCharacteristic(this._id); } class InternalDescriptor { - int _id; + final int _id; InternalDescriptor(this._id); } -mixin WithValue { - /// The value of this object. - Uint8List value; -} diff --git a/lib/src/bridge/characteristics_mixin.dart b/lib/src/bridge/characteristics_mixin.dart index 1f1bfcd5..12a80d52 100644 --- a/lib/src/bridge/characteristics_mixin.dart +++ b/lib/src/bridge/characteristics_mixin.dart @@ -20,10 +20,16 @@ mixin CharacteristicsMixin on FlutterBLE { ) .catchError((errorJson) => Future.error(BleError.fromJson(jsonDecode(errorJson.details)))) - .then((rawJsonValue) => - _parseCharacteristicWithValueWithTransactionIdResponse( - peripheral, rawJsonValue) - .value); + .then((rawValue) { + String rawJsonValue = ""; + if (rawValue is String) { + rawJsonValue = rawValue; + } + return _parseCharacteristicWithValueWithTransactionIdResponse( + peripheral, + rawJsonValue + ).value; + }); Future readCharacteristicForDevice( Peripheral peripheral, @@ -43,11 +49,16 @@ mixin CharacteristicsMixin on FlutterBLE { ) .catchError((errorJson) => Future.error(BleError.fromJson(jsonDecode(errorJson.details)))) - .then( - (rawJsonValue) => - _parseCharacteristicWithValueWithTransactionIdResponse( - peripheral, rawJsonValue), - ); + .then((rawValue) { + String rawJsonValue = ""; + if (rawValue is String) { + rawJsonValue = rawValue; + } + return _parseCharacteristicWithValueWithTransactionIdResponse( + peripheral, + rawJsonValue + ); + }); Future readCharacteristicForService( Peripheral peripheral, @@ -66,11 +77,16 @@ mixin CharacteristicsMixin on FlutterBLE { ) .catchError((errorJson) => Future.error(BleError.fromJson(jsonDecode(errorJson.details)))) - .then( - (rawJsonValue) => - _parseCharacteristicWithValueWithTransactionIdResponse( - peripheral, rawJsonValue), - ); + .then((rawValue) { + String rawJsonValue = ""; + if (rawValue is String) { + rawJsonValue = rawValue; + } + return _parseCharacteristicWithValueWithTransactionIdResponse( + peripheral, + rawJsonValue + ); + }); Future writeCharacteristicForIdentifier( Peripheral peripheral, @@ -155,11 +171,17 @@ mixin CharacteristicsMixin on FlutterBLE { }, ); - bool Function(CharacteristicWithValueAndTransactionId) - characteristicFilter = (characteristic) => - characteristic._id == characteristicIdentifier && - equalsIgnoreAsciiCase( - transactionId ?? "", characteristic.transactionId ?? ""); + bool characteristicFilter( + CharacteristicWithValueAndTransactionId characteristic, + ) { + if (characteristic._id != characteristicIdentifier) { + return false; + } + return equalsIgnoreAsciiCase( + transactionId, + characteristic._transactionId ?? "", + ); + }; return _createMonitoringStream( startMonitoring, @@ -190,7 +212,7 @@ mixin CharacteristicsMixin on FlutterBLE { equalsIgnoreAsciiCase(characteristicUuid, characteristic.uuid) && equalsIgnoreAsciiCase(serviceUuid, characteristic.service.uuid) && equalsIgnoreAsciiCase( - transactionId ?? "", characteristic.transactionId ?? ""); + transactionId, characteristic._transactionId ?? ""); return _createMonitoringStream( startMonitoring, @@ -220,7 +242,7 @@ mixin CharacteristicsMixin on FlutterBLE { equalsIgnoreAsciiCase(characteristicUuid, characteristic.uuid) && serviceIdentifier == characteristic.service._id && equalsIgnoreAsciiCase( - transactionId ?? "", characteristic.transactionId ?? ""); + transactionId, characteristic._transactionId ?? ""); return _createMonitoringStream( startMonitoring, @@ -230,24 +252,32 @@ mixin CharacteristicsMixin on FlutterBLE { ); } - Stream _createMonitoringStream( + Stream _createMonitoringStream( void Function() onListen, bool Function(CharacteristicWithValueAndTransactionId) filter, Peripheral peripheral, String transactionId, ) { - Stream stream = _characteristicsMonitoringEvents - .map( - (rawJsonValue) => - _parseCharacteristicWithValueWithTransactionIdResponse( - peripheral, rawJsonValue), - ) + Stream stream = + _characteristicsMonitoringEvents + .map((rawValue) { + String rawJsonValue = ""; + if (rawValue is String) { + rawJsonValue = rawValue; + } + return _parseCharacteristicWithValueWithTransactionIdResponse( + peripheral, + rawJsonValue + ); + }) .where(filter) .handleError((errorJson) => _throwErrorIfMatchesWithTransactionId(errorJson, transactionId)) - .transform(CancelOnErrorStreamTransformer()); + .transform( + CancelOnErrorStreamTransformer() + ); - StreamController streamController = + StreamController streamController = StreamController.broadcast( onListen: onListen, onCancel: () => cancelTransaction(transactionId), @@ -255,14 +285,14 @@ mixin CharacteristicsMixin on FlutterBLE { streamController .addStream(stream, cancelOnError: true) - .then((_) => streamController?.close()); + .then((_) => streamController.close()); return streamController.stream; } CharacteristicWithValueAndTransactionId _parseCharacteristicWithValueWithTransactionIdResponse( - Peripheral peripheral, rawJsonValue) { + Peripheral peripheral, String rawJsonValue) { Map rootObject = jsonDecode(rawJsonValue); Service service = Service.fromJson(rootObject, peripheral, _manager); @@ -281,17 +311,20 @@ mixin CharacteristicsMixin on FlutterBLE { rootObject["characteristic"], service, _manager); } - void _throwErrorIfMatchesWithTransactionId(errorJson, transactionId) { - var errorDetails = jsonDecode(errorJson.details); - if (transactionId == errorDetails["transactionId"]) - throw BleError.fromJson(errorDetails); - else + void _throwErrorIfMatchesWithTransactionId(PlatformException errorJson, String transactionId) { + if (errorJson is PlatformException == false) { + return; + } + final errorDetails = jsonDecode(errorJson.details); + if (transactionId != errorDetails["transactionId"]) { return; + } + throw BleError.fromJson(errorDetails); } } class CharacteristicWithValueAndTransactionId extends CharacteristicWithValue { - String transactionId; + String? _transactionId; CharacteristicWithValueAndTransactionId.fromJson( Map jsonObject, @@ -301,7 +334,7 @@ class CharacteristicWithValueAndTransactionId extends CharacteristicWithValue { CharacteristicWithValueAndTransactionId setTransactionId( String transactionId) { - this.transactionId = transactionId; + _transactionId = transactionId; return this; } @@ -311,13 +344,13 @@ class CharacteristicWithValueAndTransactionId extends CharacteristicWithValue { super == other && other is CharacteristicWithValueAndTransactionId && runtimeType == other.runtimeType && - transactionId == other.transactionId; + _transactionId == other._transactionId; @override - int get hashCode => super.hashCode ^ transactionId.hashCode; + int get hashCode => super.hashCode ^ _transactionId.hashCode; @override String toString() => super.toString() + - ' CharacteristicWithValueAndTransactionId{transactionId: $transactionId}'; + ' CharacteristicWithValueAndTransactionId{transactionId: $_transactionId}'; } diff --git a/lib/src/bridge/device_connection_mixin.dart b/lib/src/bridge/device_connection_mixin.dart index c15ef44f..b1eadd99 100644 --- a/lib/src/bridge/device_connection_mixin.dart +++ b/lib/src/bridge/device_connection_mixin.dart @@ -5,8 +5,13 @@ mixin DeviceConnectionMixin on FlutterBLE { const EventChannel(ChannelName.connectionStateChangeEvents) .receiveBroadcastStream(); - Future connectToPeripheral(String deviceIdentifier, bool isAutoConnect, - int requestMtu, bool refreshGatt, Duration timeout) async { + Future connectToPeripheral( + String deviceIdentifier, + bool isAutoConnect, + int requestMtu, + bool refreshGatt, + Duration? timeout + ) async { return await _methodChannel.invokeMethod( MethodName.connectToDevice, { @@ -25,7 +30,7 @@ mixin DeviceConnectionMixin on FlutterBLE { Stream observePeripheralConnectionState( String identifier, bool emitCurrentValue) { - var controller = StreamController( + final controller = StreamController( onListen: () => _methodChannel.invokeMethod( MethodName.observeConnectionState, { @@ -37,7 +42,7 @@ mixin DeviceConnectionMixin on FlutterBLE { ), ); - var sourceStream = _peripheralConnectionStateChanges + final sourceStream = _peripheralConnectionStateChanges .map((jsonString) => ConnectionStateContainer.fromJson(jsonDecode(jsonString))) .where((connectionStateContainer) => @@ -66,7 +71,7 @@ mixin DeviceConnectionMixin on FlutterBLE { sourceStream, cancelOnError: true, ) - .then((value) => controller?.close()); + .then((value) => controller.close()); return controller.stream; } @@ -76,9 +81,14 @@ mixin DeviceConnectionMixin on FlutterBLE { .invokeMethod(MethodName.isDeviceConnected, { ArgumentName.deviceIdentifier: peripheralIdentifier, }).catchError( - (errorJson) => Future.error( - BleError.fromJson(jsonDecode(errorJson.details)), - ), + (errorJson) { + if (errorJson is MissingPluginException) { + return Future.error(errorJson); + } + return Future.error( + BleError.fromJson(jsonDecode(errorJson.details)) + ); + } ); } diff --git a/lib/src/bridge/lib_core.dart b/lib/src/bridge/lib_core.dart index a8b181d5..318f004a 100644 --- a/lib/src/bridge/lib_core.dart +++ b/lib/src/bridge/lib_core.dart @@ -1,7 +1,9 @@ part of _internal; abstract class FlutterBLE { - InternalBleManager _manager; + final InternalBleManager _manager; + + FlutterBLE._(this._manager); final MethodChannel _methodChannel = const MethodChannel(ChannelName.flutterBleLib); @@ -29,40 +31,42 @@ class FlutterBleLib extends FlutterBLE const EventChannel(ChannelName.stateRestoreEvents) .receiveBroadcastStream(); - void registerManager(InternalBleManager manager) { - _manager = manager; - } + FlutterBleLib(InternalBleManager manager) : super._(manager); - Future> restoredState() => _restoreStateEvents + Future> restoredState() async { + final peripherals = await _restoreStateEvents .map( (jsonString) { - if (jsonString == null) + if (jsonString == null || + jsonString is String == false) { return null; - else { - List> restoredPeripheralsJson = - (jsonDecode(jsonString) as List).cast(); - return restoredPeripheralsJson - .map((peripheralJson) => - Peripheral.fromJson(peripheralJson, _manager)) - .toList(); } + final restoredPeripheralsJson = + (jsonDecode(jsonString) as List) + .cast>(); + return restoredPeripheralsJson + .map((peripheralJson) => + Peripheral.fromJson(peripheralJson, _manager)) + .toList(); + }, ) .take(1) .single; + return peripherals ?? []; + } Future isClientCreated() => - _methodChannel.invokeMethod(MethodName.isClientCreated); + _methodChannel.invokeMethod(MethodName.isClientCreated) + .then((value) => value!); - Future createClient(String restoreStateIdentifier) async { - await _methodChannel.invokeMethod(MethodName.createClient, { + Future createClient(String? restoreStateIdentifier) async { + await _methodChannel.invokeMethod(MethodName.createClient, { ArgumentName.restoreStateIdentifier: restoreStateIdentifier }); - return; } Future destroyClient() async { await _methodChannel.invokeMethod(MethodName.destroyClient); - return; } } diff --git a/lib/src/bridge/scanning_mixin.dart b/lib/src/bridge/scanning_mixin.dart index d05a9809..84d18410 100644 --- a/lib/src/bridge/scanning_mixin.dart +++ b/lib/src/bridge/scanning_mixin.dart @@ -1,19 +1,28 @@ part of _internal; mixin ScanningMixin on FlutterBLE { - Stream _scanEvents; - - void _prepareScanEventsStream() { - _scanEvents = const EventChannel(ChannelName.scanningEvents) - .receiveBroadcastStream() - .handleError( - (errorJson) => throw BleError.fromJson(jsonDecode(errorJson.details)), + Stream? _activeScanEvents; + Stream get _scanEvents { + var scanEvents = _activeScanEvents; + if (scanEvents == null) { + scanEvents = + const EventChannel( + ChannelName.scanningEvents + ).receiveBroadcastStream().handleError( + (errorJson) => throw BleError.fromJson( + jsonDecode(errorJson.details) + ), test: (error) => error is PlatformException, - ) - .map( + ).map( (scanResultJson) => ScanResult.fromJson(jsonDecode(scanResultJson), _manager), ); + _activeScanEvents = scanEvents; + } + return scanEvents; + } + void _resetScanEvents() { + _activeScanEvents = null; } Stream startDeviceScan( @@ -22,11 +31,7 @@ mixin ScanningMixin on FlutterBLE { List uuids, bool allowDuplicates, ) { - if (_scanEvents == null) { - _prepareScanEventsStream(); - } - - StreamController streamController = StreamController.broadcast( + final streamController = StreamController.broadcast( onListen: () => _methodChannel.invokeMethod( MethodName.startDeviceScan, { @@ -41,14 +46,14 @@ mixin ScanningMixin on FlutterBLE { streamController .addStream(_scanEvents, cancelOnError: true) - .then((_) => streamController?.close()); + .then((_) => streamController.close()); return streamController.stream; } Future stopDeviceScan() async { await _methodChannel.invokeMethod(MethodName.stopDeviceScan); - _scanEvents = null; + _resetScanEvents(); return; } } diff --git a/lib/src/internal_ble_manager.dart b/lib/src/internal_ble_manager.dart index 15f29cd8..442bc4df 100644 --- a/lib/src/internal_ble_manager.dart +++ b/lib/src/internal_ble_manager.dart @@ -7,11 +7,10 @@ class InternalBleManager ManagerForService, ManagerForCharacteristic, ManagerForDescriptor { - FlutterBleLib _bleLib; - + late FlutterBleLib _bleLib; + InternalBleManager() { - _bleLib = FlutterBleLib(); - _bleLib.registerManager(this); + _bleLib = FlutterBleLib(this); } @override @@ -19,8 +18,8 @@ class InternalBleManager @override Future createClient({ - String restoreStateIdentifier, - RestoreStateAction restoreStateAction, + String? restoreStateIdentifier, + RestoreStateAction? restoreStateAction, }) { if (restoreStateAction != null) { _bleLib.restoredState().then((devices) { @@ -38,11 +37,11 @@ class InternalBleManager _bleLib.cancelTransaction(transactionId); @override - Future enableRadio({String transactionId}) => + Future enableRadio({String? transactionId}) => _bleLib.enableRadio(transactionId ?? TransactionIdGenerator.getNextId()); @override - Future disableRadio({String transactionId}) => + Future disableRadio({String? transactionId}) => _bleLib.disableRadio(transactionId ?? TransactionIdGenerator.getNextId()); @override @@ -66,7 +65,7 @@ class InternalBleManager Future stopPeripheralScan() => _bleLib.stopDeviceScan(); @override - Peripheral createUnsafePeripheral(String peripheralId, {String name}) { + Peripheral createUnsafePeripheral(String peripheralId, {String? name}) { const nameField = 'name'; const identifierField = 'id'; return Peripheral.fromJson({ @@ -78,10 +77,10 @@ class InternalBleManager @override Future connectToPeripheral( String identifier, { - bool isAutoConnect, - int requestMtu, - bool refreshGatt, - Duration timeout, + required bool isAutoConnect, + required int requestMtu, + required bool refreshGatt, + Duration? timeout, }) async => _bleLib.connectToPeripheral( identifier, isAutoConnect, requestMtu, refreshGatt, timeout); @@ -185,7 +184,7 @@ class InternalBleManager } @override - Future requestMtu( + Future requestMtu( Peripheral peripheral, int mtu, String transactionId) { return _bleLib.requestMtu(peripheral, mtu, transactionId); } @@ -193,12 +192,12 @@ class InternalBleManager @override Future> knownPeripherals( List peripheralIdentifiers) { - return _bleLib.knownDevices(peripheralIdentifiers ?? []); + return _bleLib.knownDevices(peripheralIdentifiers); } @override Future> connectedPeripherals(List serviceUuids) { - return _bleLib.connectedDevices(serviceUuids ?? []); + return _bleLib.connectedDevices(serviceUuids); } @override diff --git a/lib/src/util/_transaction_id_generator.dart b/lib/src/util/_transaction_id_generator.dart index b5270479..df261165 100644 --- a/lib/src/util/_transaction_id_generator.dart +++ b/lib/src/util/_transaction_id_generator.dart @@ -1,6 +1,11 @@ +import 'package:meta/meta.dart'; + abstract class TransactionIdGenerator { static int _id = 0; + @visibleForTesting + static int get id => _id; + static String getNextId() { _id++; return _id.toString(); diff --git a/pubspec.yaml b/pubspec.yaml index 1b9363ad..08664996 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,23 +1,23 @@ name: flutter_ble_lib description: FlutterBle Library is a flutter library that supports BLE operations. It uses MultiPlatformBleAdapter as a native backend.. -version: 2.3.2 +version: 2.4.0 homepage: https://github.com/Polidea/FlutterBleLib environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.12.0-0 <3.0.0" # Flutter versions prior to 1.10 did not support the flutter.plugin.platforms map. flutter: ">=1.10.0 <2.0.0" dependencies: - collection: ^1.14.11 - async: ^2.2.0 + collection: ^1.15.0 + async: ^2.5.0 flutter: sdk: flutter dev_dependencies: - test: ^1.5.3 - mockito: ^4.0.0 - pedantic: ^1.9.0 + mockito: ^5.0.3 + build_runner: ^1.12.2 + pedantic: ^1.11.0 flutter_test: sdk: flutter diff --git a/test/analysis_options.yaml b/test/analysis_options.yaml new file mode 100644 index 00000000..6b24a314 --- /dev/null +++ b/test/analysis_options.yaml @@ -0,0 +1,8 @@ +include: ../analysis_options.yaml + +analyzer: + exclude: [./**] + +linter: + rules: + unawaited_futures: false diff --git a/test/ble_manager_test.dart b/test/ble_manager_test.dart index f4c2c86a..7967dec2 100644 --- a/test/ble_manager_test.dart +++ b/test/ble_manager_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_ble_lib/flutter_ble_lib.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; void main() { test('Ble manager must be singleton', () { diff --git a/test/characteristic_test.dart b/test/characteristic_test.dart index 543e0a13..882dc2a7 100644 --- a/test/characteristic_test.dart +++ b/test/characteristic_test.dart @@ -5,25 +5,55 @@ import 'dart:typed_data'; import 'package:flutter_ble_lib/flutter_ble_lib.dart'; import 'package:flutter_ble_lib/src/_managers_for_classes.dart'; import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:mockito/annotations.dart'; +import './characteristic_test.mocks.dart'; -import 'mock/mocks.dart'; import 'test_util/characteristic_generator.dart'; import 'test_util/descriptor_generator.dart'; -class ServiceMock extends Mock implements Service {} - +@GenerateMocks( + [Peripheral, ManagerForDescriptor, DescriptorWithValue], + customMocks: [ + MockSpec(returnNullOnMissingStub: true), + ] +) void main() { - Peripheral peripheral = PeripheralMock(); - ManagerForCharacteristic managerForCharacteristic = - ManagerForCharacteristicMock(); - CharacteristicGenerator characteristicGenerator = + final peripheral = MockPeripheral(); + when(peripheral.toString()).thenReturn("mocked peripheral toString()"); + final managerForCharacteristic = + MockManagerForCharacteristic(); + when( + managerForCharacteristic.readCharacteristicForIdentifier(any, any, any) + ).thenAnswer( + (_) async => Uint8List.fromList([]) + ); + when( + managerForCharacteristic.monitorCharacteristicForIdentifier(any, any, any) + ).thenAnswer( + (_) => Stream.value(Uint8List.fromList([])) + ); + when( + managerForCharacteristic.readDescriptorForCharacteristic(any, any, any) + ).thenAnswer( + (_) async => MockDescriptorWithValue() + ); + when( + managerForCharacteristic.writeDescriptorForCharacteristic(any, any, any, any) + ).thenAnswer( + (_) async => MockDescriptorWithValue() + ); + final characteristicGenerator = CharacteristicGenerator(managerForCharacteristic); - DescriptorGenerator descriptorGenerator = - DescriptorGenerator(ManagerForDescriptorMock()); + final descriptorGenerator = + DescriptorGenerator(MockManagerForDescriptor()); + final service = MockService(); + when(service.peripheral).thenReturn(peripheral); + when(service.toString()).thenReturn("mocked service toString()"); - Characteristic characteristic = - characteristicGenerator.create(123, ServiceMock()); + final characteristic = + characteristicGenerator.create(123, service); DescriptorWithValue createDescriptor(int seed) => descriptorGenerator.create(seed, characteristic); @@ -104,7 +134,7 @@ void main() { characteristic.read(); //then - var transactionIds = verify( + final transactionIds = verify( managerForCharacteristic.readCharacteristicForIdentifier( any, characteristic, captureThat(isNotNull)), ).captured; diff --git a/test/characteristic_test.mocks.dart b/test/characteristic_test.mocks.dart new file mode 100644 index 00000000..ee004248 --- /dev/null +++ b/test/characteristic_test.mocks.dart @@ -0,0 +1,344 @@ +// Mocks generated by Mockito 5.0.3 from annotations +// in flutter_ble_lib/test/characteristic_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; +import 'dart:typed_data' as _i3; + +import 'package:flutter_ble_lib/flutter_ble_lib.dart' as _i2; +import 'package:flutter_ble_lib/src/_managers_for_classes.dart' as _i5; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: comment_references +// ignore_for_file: unnecessary_parenthesis + +class _FakeCharacteristicWithValue extends _i1.Fake + implements _i2.CharacteristicWithValue {} + +class _FakeCharacteristic extends _i1.Fake implements _i2.Characteristic {} + +class _FakeDescriptorWithValue extends _i1.Fake + implements _i2.DescriptorWithValue {} + +class _FakeDescriptor extends _i1.Fake implements _i2.Descriptor {} + +class _FakeUint8List extends _i1.Fake implements _i3.Uint8List {} + +class _FakePeripheral extends _i1.Fake implements _i2.Peripheral {} + +/// A class which mocks [Peripheral]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPeripheral extends _i1.Mock implements _i2.Peripheral { + MockPeripheral() { + _i1.throwOnMissingStub(this); + } + + @override + String get identifier => + (super.noSuchMethod(Invocation.getter(#identifier), returnValue: '') + as String); + @override + set identifier(String? _identifier) => + super.noSuchMethod(Invocation.setter(#identifier, _identifier), + returnValueForMissingStub: null); + @override + _i4.Future connect( + {bool? isAutoConnect = false, + int? requestMtu = 0, + bool? refreshGatt = false, + Duration? timeout}) => + (super.noSuchMethod( + Invocation.method(#connect, [], { + #isAutoConnect: isAutoConnect, + #requestMtu: requestMtu, + #refreshGatt: refreshGatt, + #timeout: timeout + }), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Stream<_i2.PeripheralConnectionState> observeConnectionState( + {bool? emitCurrentValue = false, + bool? completeOnDisconnect = false}) => + (super.noSuchMethod( + Invocation.method(#observeConnectionState, [], { + #emitCurrentValue: emitCurrentValue, + #completeOnDisconnect: completeOnDisconnect + }), + returnValue: Stream<_i2.PeripheralConnectionState>.empty()) + as _i4.Stream<_i2.PeripheralConnectionState>); + @override + _i4.Future isConnected() => + (super.noSuchMethod(Invocation.method(#isConnected, []), + returnValue: Future.value(false)) as _i4.Future); + @override + _i4.Future disconnectOrCancelConnection() => + (super.noSuchMethod(Invocation.method(#disconnectOrCancelConnection, []), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future discoverAllServicesAndCharacteristics( + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#discoverAllServicesAndCharacteristics, [], + {#transactionId: transactionId}), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future> services() => + (super.noSuchMethod(Invocation.method(#services, []), + returnValue: Future.value(<_i2.Service>[])) + as _i4.Future>); + @override + _i4.Future> characteristics(String? servicedUuid) => + (super.noSuchMethod(Invocation.method(#characteristics, [servicedUuid]), + returnValue: Future.value(<_i2.Characteristic>[])) + as _i4.Future>); + @override + _i4.Future rssi({String? transactionId}) => (super.noSuchMethod( + Invocation.method(#rssi, [], {#transactionId: transactionId}), + returnValue: Future.value(0)) as _i4.Future); + @override + _i4.Future requestMtu(int? mtu, {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #requestMtu, [mtu], {#transactionId: transactionId}), + returnValue: Future.value(0)) as _i4.Future); + @override + _i4.Future<_i2.CharacteristicWithValue> readCharacteristic( + String? serviceUuid, String? characteristicUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #readCharacteristic, + [serviceUuid, characteristicUuid], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeCharacteristicWithValue())) + as _i4.Future<_i2.CharacteristicWithValue>); + @override + _i4.Future<_i2.Characteristic> writeCharacteristic(String? serviceUuid, + String? characteristicUuid, _i3.Uint8List? value, bool? withResponse, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #writeCharacteristic, + [serviceUuid, characteristicUuid, value, withResponse], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeCharacteristic())) + as _i4.Future<_i2.Characteristic>); + @override + _i4.Future> descriptorsForCharacteristic( + String? serviceUuid, String? characteristicUuid) => + (super.noSuchMethod( + Invocation.method(#descriptorsForCharacteristic, + [serviceUuid, characteristicUuid]), + returnValue: Future.value(<_i2.Descriptor>[])) + as _i4.Future>); + @override + _i4.Future<_i2.DescriptorWithValue> readDescriptor(String? serviceUuid, + String? characteristicUuid, String? descriptorUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #readDescriptor, + [serviceUuid, characteristicUuid, descriptorUuid], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptorWithValue())) + as _i4.Future<_i2.DescriptorWithValue>); + @override + _i4.Future<_i2.Descriptor> writeDescriptor( + String? serviceUuid, + String? characteristicUuid, + String? descriptorUuid, + _i3.Uint8List? value, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #writeDescriptor, + [serviceUuid, characteristicUuid, descriptorUuid, value], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptor())) + as _i4.Future<_i2.Descriptor>); + @override + _i4.Stream<_i2.CharacteristicWithValue> monitorCharacteristic( + String? serviceUuid, String? characteristicUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #monitorCharacteristic, + [serviceUuid, characteristicUuid], + {#transactionId: transactionId}), + returnValue: Stream<_i2.CharacteristicWithValue>.empty()) + as _i4.Stream<_i2.CharacteristicWithValue>); + @override + String toString() => + (super.noSuchMethod(Invocation.method(#toString, []), returnValue: '') + as String); +} + +/// A class which mocks [ManagerForDescriptor]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockManagerForDescriptor extends _i1.Mock + implements _i5.ManagerForDescriptor { + MockManagerForDescriptor() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future<_i3.Uint8List> readDescriptorForIdentifier( + _i2.Descriptor? descriptor, String? transactionId) => + (super.noSuchMethod( + Invocation.method( + #readDescriptorForIdentifier, [descriptor, transactionId]), + returnValue: Future.value(_FakeUint8List())) + as _i4.Future<_i3.Uint8List>); + @override + _i4.Future writeDescriptorForIdentifier(_i2.Descriptor? descriptor, + _i3.Uint8List? value, String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeDescriptorForIdentifier, + [descriptor, value, transactionId]), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); +} + +/// A class which mocks [DescriptorWithValue]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockDescriptorWithValue extends _i1.Mock + implements _i2.DescriptorWithValue { + MockDescriptorWithValue() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Uint8List get value => (super.noSuchMethod(Invocation.getter(#value), + returnValue: _FakeUint8List()) as _i3.Uint8List); + @override + set value(_i3.Uint8List? _value) => + super.noSuchMethod(Invocation.setter(#value, _value), + returnValueForMissingStub: null); + @override + _i2.Characteristic get characteristic => + (super.noSuchMethod(Invocation.getter(#characteristic), + returnValue: _FakeCharacteristic()) as _i2.Characteristic); + @override + String get uuid => + (super.noSuchMethod(Invocation.getter(#uuid), returnValue: '') as String); + @override + int get hashCode => + (super.noSuchMethod(Invocation.getter(#hashCode), returnValue: 0) as int); + @override + _i4.Future<_i3.Uint8List> read({String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#read, [], {#transactionId: transactionId}), + returnValue: Future.value(_FakeUint8List())) + as _i4.Future<_i3.Uint8List>); + @override + _i4.Future write(_i3.Uint8List? value, {String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#write, [value], {#transactionId: transactionId}), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + bool operator ==(Object? other) => + (super.noSuchMethod(Invocation.method(#==, [other]), returnValue: false) + as bool); +} + +/// A class which mocks [Service]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockService extends _i1.Mock implements _i2.Service { + @override + _i2.Peripheral get peripheral => + (super.noSuchMethod(Invocation.getter(#peripheral), + returnValue: _FakePeripheral()) as _i2.Peripheral); + @override + set peripheral(_i2.Peripheral? _peripheral) => + super.noSuchMethod(Invocation.setter(#peripheral, _peripheral), + returnValueForMissingStub: null); + @override + String get uuid => + (super.noSuchMethod(Invocation.getter(#uuid), returnValue: '') as String); + @override + set uuid(String? _uuid) => super.noSuchMethod(Invocation.setter(#uuid, _uuid), + returnValueForMissingStub: null); + @override + int get hashCode => + (super.noSuchMethod(Invocation.getter(#hashCode), returnValue: 0) as int); + @override + _i4.Future> characteristics() => + (super.noSuchMethod(Invocation.method(#characteristics, []), + returnValue: Future.value(<_i2.Characteristic>[])) + as _i4.Future>); + @override + _i4.Future<_i2.Characteristic> writeCharacteristic( + String? characteristicUuid, _i3.Uint8List? value, bool? withResponse, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #writeCharacteristic, + [characteristicUuid, value, withResponse], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeCharacteristic())) + as _i4.Future<_i2.Characteristic>); + @override + _i4.Future<_i2.CharacteristicWithValue> readCharacteristic( + String? characteristicUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#readCharacteristic, [characteristicUuid], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeCharacteristicWithValue())) + as _i4.Future<_i2.CharacteristicWithValue>); + @override + _i4.Stream<_i2.CharacteristicWithValue> monitorCharacteristic( + String? characteristicUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#monitorCharacteristic, [characteristicUuid], + {#transactionId: transactionId}), + returnValue: Stream<_i2.CharacteristicWithValue>.empty()) + as _i4.Stream<_i2.CharacteristicWithValue>); + @override + _i4.Future> descriptorsForCharacteristic( + String? characteristicUuid) => + (super.noSuchMethod( + Invocation.method( + #descriptorsForCharacteristic, [characteristicUuid]), + returnValue: Future.value(<_i2.Descriptor>[])) + as _i4.Future>); + @override + _i4.Future<_i2.DescriptorWithValue> readDescriptor( + String? characteristicUuid, String? descriptorUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #readDescriptor, + [characteristicUuid, descriptorUuid], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptorWithValue())) + as _i4.Future<_i2.DescriptorWithValue>); + @override + _i4.Future<_i2.Descriptor> writeDescriptor(String? characteristicUuid, + String? descriptorUuid, _i3.Uint8List? value, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #writeDescriptor, + [characteristicUuid, descriptorUuid, value], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptor())) + as _i4.Future<_i2.Descriptor>); + @override + bool operator ==(Object? other) => + (super.noSuchMethod(Invocation.method(#==, [other]), returnValue: false) + as bool); + @override + String toString() => + (super.noSuchMethod(Invocation.method(#toString, []), returnValue: '') + as String); +} diff --git a/test/descriptor_test.dart b/test/descriptor_test.dart index a38de706..fa42207a 100644 --- a/test/descriptor_test.dart +++ b/test/descriptor_test.dart @@ -4,18 +4,26 @@ import 'dart:typed_data'; import 'package:flutter_ble_lib/flutter_ble_lib.dart'; import 'package:flutter_ble_lib/src/_managers_for_classes.dart'; import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:flutter_test/flutter_test.dart'; -import 'mock/mocks.dart'; import 'test_util/descriptor_generator.dart'; +import './descriptor_test.mocks.dart'; + +@GenerateMocks([ManagerForDescriptor, Characteristic]) void main() { - ManagerForDescriptor managerForDescriptor = ManagerForDescriptorMock(); + final managerForDescriptor = MockManagerForDescriptor(); + when( + managerForDescriptor.readDescriptorForIdentifier(any, any) + ).thenAnswer( + (_) async => Uint8List.fromList([]) + ); DescriptorGenerator descriptorGenerator = DescriptorGenerator(managerForDescriptor); DescriptorWithValue createDescriptor(int seed) => - descriptorGenerator.create(seed, CharacteristicMock()); + descriptorGenerator.create(seed, MockCharacteristic()); Descriptor descriptor = createDescriptor(123); diff --git a/test/descriptor_test.mocks.dart b/test/descriptor_test.mocks.dart new file mode 100644 index 00000000..9809d530 --- /dev/null +++ b/test/descriptor_test.mocks.dart @@ -0,0 +1,168 @@ +// Mocks generated by Mockito 5.0.3 from annotations +// in flutter_ble_lib/test/descriptor_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i5; +import 'dart:typed_data' as _i2; + +import 'package:flutter_ble_lib/flutter_ble_lib.dart' as _i3; +import 'package:flutter_ble_lib/src/_managers_for_classes.dart' as _i4; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: comment_references +// ignore_for_file: unnecessary_parenthesis + +class _FakeUint8List extends _i1.Fake implements _i2.Uint8List {} + +class _FakeService extends _i1.Fake implements _i3.Service {} + +class _FakeDescriptorWithValue extends _i1.Fake + implements _i3.DescriptorWithValue {} + +class _FakeDescriptor extends _i1.Fake implements _i3.Descriptor {} + +/// A class which mocks [ManagerForDescriptor]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockManagerForDescriptor extends _i1.Mock + implements _i4.ManagerForDescriptor { + MockManagerForDescriptor() { + _i1.throwOnMissingStub(this); + } + + @override + _i5.Future<_i2.Uint8List> readDescriptorForIdentifier( + _i3.Descriptor? descriptor, String? transactionId) => + (super.noSuchMethod( + Invocation.method( + #readDescriptorForIdentifier, [descriptor, transactionId]), + returnValue: Future.value(_FakeUint8List())) + as _i5.Future<_i2.Uint8List>); + @override + _i5.Future writeDescriptorForIdentifier(_i3.Descriptor? descriptor, + _i2.Uint8List? value, String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeDescriptorForIdentifier, + [descriptor, value, transactionId]), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i5.Future); +} + +/// A class which mocks [Characteristic]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCharacteristic extends _i1.Mock implements _i3.Characteristic { + MockCharacteristic() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Service get service => (super.noSuchMethod(Invocation.getter(#service), + returnValue: _FakeService()) as _i3.Service); + @override + set service(_i3.Service? _service) => + super.noSuchMethod(Invocation.setter(#service, _service), + returnValueForMissingStub: null); + @override + String get uuid => + (super.noSuchMethod(Invocation.getter(#uuid), returnValue: '') as String); + @override + set uuid(String? _uuid) => super.noSuchMethod(Invocation.setter(#uuid, _uuid), + returnValueForMissingStub: null); + @override + bool get isReadable => + (super.noSuchMethod(Invocation.getter(#isReadable), returnValue: false) + as bool); + @override + set isReadable(bool? _isReadable) => + super.noSuchMethod(Invocation.setter(#isReadable, _isReadable), + returnValueForMissingStub: null); + @override + bool get isWritableWithResponse => + (super.noSuchMethod(Invocation.getter(#isWritableWithResponse), + returnValue: false) as bool); + @override + set isWritableWithResponse(bool? _isWritableWithResponse) => + super.noSuchMethod( + Invocation.setter(#isWritableWithResponse, _isWritableWithResponse), + returnValueForMissingStub: null); + @override + bool get isWritableWithoutResponse => + (super.noSuchMethod(Invocation.getter(#isWritableWithoutResponse), + returnValue: false) as bool); + @override + set isWritableWithoutResponse(bool? _isWritableWithoutResponse) => + super.noSuchMethod( + Invocation.setter( + #isWritableWithoutResponse, _isWritableWithoutResponse), + returnValueForMissingStub: null); + @override + bool get isNotifiable => + (super.noSuchMethod(Invocation.getter(#isNotifiable), returnValue: false) + as bool); + @override + set isNotifiable(bool? _isNotifiable) => + super.noSuchMethod(Invocation.setter(#isNotifiable, _isNotifiable), + returnValueForMissingStub: null); + @override + bool get isIndicatable => + (super.noSuchMethod(Invocation.getter(#isIndicatable), returnValue: false) + as bool); + @override + set isIndicatable(bool? _isIndicatable) => + super.noSuchMethod(Invocation.setter(#isIndicatable, _isIndicatable), + returnValueForMissingStub: null); + @override + int get hashCode => + (super.noSuchMethod(Invocation.getter(#hashCode), returnValue: 0) as int); + @override + _i5.Future<_i2.Uint8List> read({String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#read, [], {#transactionId: transactionId}), + returnValue: Future.value(_FakeUint8List())) + as _i5.Future<_i2.Uint8List>); + @override + _i5.Future write(_i2.Uint8List? value, bool? withResponse, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #write, [value, withResponse], {#transactionId: transactionId}), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Stream<_i2.Uint8List> monitor({String? transactionId}) => (super + .noSuchMethod( + Invocation.method(#monitor, [], {#transactionId: transactionId}), + returnValue: Stream<_i2.Uint8List>.empty()) + as _i5.Stream<_i2.Uint8List>); + @override + _i5.Future> descriptors() => + (super.noSuchMethod(Invocation.method(#descriptors, []), + returnValue: Future.value(<_i3.Descriptor>[])) + as _i5.Future>); + @override + _i5.Future<_i3.DescriptorWithValue> readDescriptor(String? descriptorUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#readDescriptor, [descriptorUuid], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptorWithValue())) + as _i5.Future<_i3.DescriptorWithValue>); + @override + _i5.Future<_i3.Descriptor> writeDescriptor( + String? descriptorUuid, _i2.Uint8List? value, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#writeDescriptor, [descriptorUuid, value], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptor())) + as _i5.Future<_i3.Descriptor>); + @override + bool operator ==(Object? other) => + (super.noSuchMethod(Invocation.method(#==, [other]), returnValue: false) + as bool); + @override + String toString() => + (super.noSuchMethod(Invocation.method(#toString, []), returnValue: '') + as String); +} diff --git a/test/json/ble_error_jsons.dart b/test/json/ble_error_jsons.dart index ca3cf6df..6a46ebc0 100644 --- a/test/json/ble_error_jsons.dart +++ b/test/json/ble_error_jsons.dart @@ -1,6 +1,7 @@ -String cancellationErrorJson = """ +String cancellationErrorJson(String transactionId) => """ { "errorCode": 2, - "reason": "Operation cancelled" + "reason": "Operation cancelled", + "transactionId": "$transactionId" } """; diff --git a/test/mock/mocks.dart b/test/mock/mocks.dart index 0bfb4388..d985738b 100644 --- a/test/mock/mocks.dart +++ b/test/mock/mocks.dart @@ -2,7 +2,6 @@ import 'package:flutter_ble_lib/flutter_ble_lib.dart'; import 'package:flutter_ble_lib/src/_managers_for_classes.dart'; import 'package:mockito/mockito.dart'; -class ManagerForServiceMock extends Mock implements ManagerForService {} class ManagerForCharacteristicMock extends Mock implements ManagerForCharacteristic {} diff --git a/test/service_test.dart b/test/service_test.dart index 4bbf0fa7..1842b983 100644 --- a/test/service_test.dart +++ b/test/service_test.dart @@ -4,21 +4,56 @@ import 'dart:typed_data'; import 'package:flutter_ble_lib/flutter_ble_lib.dart'; import 'package:flutter_ble_lib/src/_managers_for_classes.dart'; import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:flutter_test/flutter_test.dart'; -import 'mock/mocks.dart'; +import './service_test.mocks.dart'; import 'test_util/characteristic_generator.dart'; import 'test_util/descriptor_generator.dart'; +@GenerateMocks([ + Peripheral, + ManagerForService, + ManagerForDescriptor, + CharacteristicWithValue, + DescriptorWithValue +]) void main() { - Peripheral peripheral = PeripheralMock(); - ManagerForService managerForService = ManagerForServiceMock(); - ManagerForCharacteristic managerForCharacteristic = - ManagerForCharacteristicMock(); - ManagerForDescriptor managerForDescriptor = ManagerForDescriptorMock(); - CharacteristicGenerator characteristicGenerator = + final peripheral = MockPeripheral(); + when(peripheral.toString()).thenReturn("mocked peripheral toString()"); + when(peripheral.identifier).thenReturn("mocked peripheral id"); + final managerForService = MockManagerForService(); + when( + managerForService.readCharacteristicForService(any, any, any, any) + ).thenAnswer( + (_) async => MockCharacteristicWithValue() + ); + when( + managerForService.readDescriptorForService(any, any, any, any) + ).thenAnswer( + (_) async => MockDescriptorWithValue() + ); + when( + managerForService.writeCharacteristicForService(any, any, any, any, any, any) + ).thenAnswer( + (_) async => MockCharacteristicWithValue() + ); + when( + managerForService.writeDescriptorForService(any, any, any, any, any) + ).thenAnswer( + (_) async => MockDescriptorWithValue() + ); + when( + managerForService.monitorCharacteristicForService(any, any, any, any) + ).thenAnswer( + (_) => Stream.value(MockCharacteristicWithValue()) + ); + final managerForCharacteristic = + MockManagerForCharacteristic(); + final managerForDescriptor = MockManagerForDescriptor(); + final characteristicGenerator = CharacteristicGenerator(managerForCharacteristic); - DescriptorGenerator descriptorGenerator = + final descriptorGenerator = DescriptorGenerator(managerForDescriptor); Service service = Service.fromJson({ diff --git a/test/service_test.mocks.dart b/test/service_test.mocks.dart new file mode 100644 index 00000000..90516a8a --- /dev/null +++ b/test/service_test.mocks.dart @@ -0,0 +1,471 @@ +// Mocks generated by Mockito 5.0.3 from annotations +// in flutter_ble_lib/test/service_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; +import 'dart:typed_data' as _i3; + +import 'package:flutter_ble_lib/flutter_ble_lib.dart' as _i2; +import 'package:flutter_ble_lib/src/_internal.dart' as _i6; +import 'package:flutter_ble_lib/src/_managers_for_classes.dart' as _i5; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: comment_references +// ignore_for_file: unnecessary_parenthesis + +class _FakeCharacteristicWithValue extends _i1.Fake + implements _i2.CharacteristicWithValue {} + +class _FakeCharacteristic extends _i1.Fake implements _i2.Characteristic {} + +class _FakeDescriptorWithValue extends _i1.Fake + implements _i2.DescriptorWithValue {} + +class _FakeDescriptor extends _i1.Fake implements _i2.Descriptor {} + +class _FakeUint8List extends _i1.Fake implements _i3.Uint8List {} + +class _FakeService extends _i1.Fake implements _i2.Service {} + +/// A class which mocks [Peripheral]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPeripheral extends _i1.Mock implements _i2.Peripheral { + MockPeripheral() { + _i1.throwOnMissingStub(this); + } + + @override + String get identifier => + (super.noSuchMethod(Invocation.getter(#identifier), returnValue: '') + as String); + @override + set identifier(String? _identifier) => + super.noSuchMethod(Invocation.setter(#identifier, _identifier), + returnValueForMissingStub: null); + @override + _i4.Future connect( + {bool? isAutoConnect = false, + int? requestMtu = 0, + bool? refreshGatt = false, + Duration? timeout}) => + (super.noSuchMethod( + Invocation.method(#connect, [], { + #isAutoConnect: isAutoConnect, + #requestMtu: requestMtu, + #refreshGatt: refreshGatt, + #timeout: timeout + }), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Stream<_i2.PeripheralConnectionState> observeConnectionState( + {bool? emitCurrentValue = false, + bool? completeOnDisconnect = false}) => + (super.noSuchMethod( + Invocation.method(#observeConnectionState, [], { + #emitCurrentValue: emitCurrentValue, + #completeOnDisconnect: completeOnDisconnect + }), + returnValue: Stream<_i2.PeripheralConnectionState>.empty()) + as _i4.Stream<_i2.PeripheralConnectionState>); + @override + _i4.Future isConnected() => + (super.noSuchMethod(Invocation.method(#isConnected, []), + returnValue: Future.value(false)) as _i4.Future); + @override + _i4.Future disconnectOrCancelConnection() => + (super.noSuchMethod(Invocation.method(#disconnectOrCancelConnection, []), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future discoverAllServicesAndCharacteristics( + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#discoverAllServicesAndCharacteristics, [], + {#transactionId: transactionId}), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future> services() => + (super.noSuchMethod(Invocation.method(#services, []), + returnValue: Future.value(<_i2.Service>[])) + as _i4.Future>); + @override + _i4.Future> characteristics(String? servicedUuid) => + (super.noSuchMethod(Invocation.method(#characteristics, [servicedUuid]), + returnValue: Future.value(<_i2.Characteristic>[])) + as _i4.Future>); + @override + _i4.Future rssi({String? transactionId}) => (super.noSuchMethod( + Invocation.method(#rssi, [], {#transactionId: transactionId}), + returnValue: Future.value(0)) as _i4.Future); + @override + _i4.Future requestMtu(int? mtu, {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #requestMtu, [mtu], {#transactionId: transactionId}), + returnValue: Future.value(0)) as _i4.Future); + @override + _i4.Future<_i2.CharacteristicWithValue> readCharacteristic( + String? serviceUuid, String? characteristicUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #readCharacteristic, + [serviceUuid, characteristicUuid], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeCharacteristicWithValue())) + as _i4.Future<_i2.CharacteristicWithValue>); + @override + _i4.Future<_i2.Characteristic> writeCharacteristic(String? serviceUuid, + String? characteristicUuid, _i3.Uint8List? value, bool? withResponse, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #writeCharacteristic, + [serviceUuid, characteristicUuid, value, withResponse], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeCharacteristic())) + as _i4.Future<_i2.Characteristic>); + @override + _i4.Future> descriptorsForCharacteristic( + String? serviceUuid, String? characteristicUuid) => + (super.noSuchMethod( + Invocation.method(#descriptorsForCharacteristic, + [serviceUuid, characteristicUuid]), + returnValue: Future.value(<_i2.Descriptor>[])) + as _i4.Future>); + @override + _i4.Future<_i2.DescriptorWithValue> readDescriptor(String? serviceUuid, + String? characteristicUuid, String? descriptorUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #readDescriptor, + [serviceUuid, characteristicUuid, descriptorUuid], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptorWithValue())) + as _i4.Future<_i2.DescriptorWithValue>); + @override + _i4.Future<_i2.Descriptor> writeDescriptor( + String? serviceUuid, + String? characteristicUuid, + String? descriptorUuid, + _i3.Uint8List? value, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #writeDescriptor, + [serviceUuid, characteristicUuid, descriptorUuid, value], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptor())) + as _i4.Future<_i2.Descriptor>); + @override + _i4.Stream<_i2.CharacteristicWithValue> monitorCharacteristic( + String? serviceUuid, String? characteristicUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #monitorCharacteristic, + [serviceUuid, characteristicUuid], + {#transactionId: transactionId}), + returnValue: Stream<_i2.CharacteristicWithValue>.empty()) + as _i4.Stream<_i2.CharacteristicWithValue>); + @override + String toString() => + (super.noSuchMethod(Invocation.method(#toString, []), returnValue: '') + as String); +} + +/// A class which mocks [ManagerForService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockManagerForService extends _i1.Mock implements _i5.ManagerForService { + MockManagerForService() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future> characteristicsForService( + _i2.Service? service) => + (super.noSuchMethod( + Invocation.method(#characteristicsForService, [service]), + returnValue: Future.value(<_i2.Characteristic>[])) + as _i4.Future>); + @override + _i4.Future<_i2.CharacteristicWithValue> readCharacteristicForService( + _i2.Peripheral? peripheral, + _i6.InternalService? service, + String? characteristicUuid, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#readCharacteristicForService, + [peripheral, service, characteristicUuid, transactionId]), + returnValue: Future.value(_FakeCharacteristicWithValue())) + as _i4.Future<_i2.CharacteristicWithValue>); + @override + _i4.Future<_i2.Characteristic> writeCharacteristicForService( + _i2.Peripheral? peripheral, + _i6.InternalService? service, + String? characteristicUuid, + _i3.Uint8List? value, + bool? withResponse, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeCharacteristicForService, [ + peripheral, + service, + characteristicUuid, + value, + withResponse, + transactionId + ]), + returnValue: Future.value(_FakeCharacteristic())) + as _i4.Future<_i2.Characteristic>); + @override + _i4.Stream<_i2.CharacteristicWithValue> monitorCharacteristicForService( + _i2.Peripheral? peripheral, + _i6.InternalService? service, + String? characteristicUuid, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#monitorCharacteristicForService, + [peripheral, service, characteristicUuid, transactionId]), + returnValue: Stream<_i2.CharacteristicWithValue>.empty()) + as _i4.Stream<_i2.CharacteristicWithValue>); + @override + _i4.Future> descriptorsForService( + _i2.Service? service, String? characteristicUuid) => + (super.noSuchMethod( + Invocation.method( + #descriptorsForService, [service, characteristicUuid]), + returnValue: Future.value(<_i2.Descriptor>[])) + as _i4.Future>); + @override + _i4.Future<_i2.DescriptorWithValue> readDescriptorForService( + _i2.Service? service, + String? characteristicUuid, + String? descriptorUuid, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#readDescriptorForService, + [service, characteristicUuid, descriptorUuid, transactionId]), + returnValue: Future.value(_FakeDescriptorWithValue())) + as _i4.Future<_i2.DescriptorWithValue>); + @override + _i4.Future<_i2.Descriptor> writeDescriptorForService( + _i2.Service? service, + String? characteristicUuid, + String? descriptorUuid, + _i3.Uint8List? value, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeDescriptorForService, [ + service, + characteristicUuid, + descriptorUuid, + value, + transactionId + ]), + returnValue: Future.value(_FakeDescriptor())) + as _i4.Future<_i2.Descriptor>); +} + +/// A class which mocks [ManagerForDescriptor]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockManagerForDescriptor extends _i1.Mock + implements _i5.ManagerForDescriptor { + MockManagerForDescriptor() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future<_i3.Uint8List> readDescriptorForIdentifier( + _i2.Descriptor? descriptor, String? transactionId) => + (super.noSuchMethod( + Invocation.method( + #readDescriptorForIdentifier, [descriptor, transactionId]), + returnValue: Future.value(_FakeUint8List())) + as _i4.Future<_i3.Uint8List>); + @override + _i4.Future writeDescriptorForIdentifier(_i2.Descriptor? descriptor, + _i3.Uint8List? value, String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeDescriptorForIdentifier, + [descriptor, value, transactionId]), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); +} + +/// A class which mocks [CharacteristicWithValue]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCharacteristicWithValue extends _i1.Mock + implements _i2.CharacteristicWithValue { + MockCharacteristicWithValue() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Uint8List get value => (super.noSuchMethod(Invocation.getter(#value), + returnValue: _FakeUint8List()) as _i3.Uint8List); + @override + set value(_i3.Uint8List? _value) => + super.noSuchMethod(Invocation.setter(#value, _value), + returnValueForMissingStub: null); + @override + int get hashCode => + (super.noSuchMethod(Invocation.getter(#hashCode), returnValue: 0) as int); + @override + _i2.Service get service => (super.noSuchMethod(Invocation.getter(#service), + returnValue: _FakeService()) as _i2.Service); + @override + set service(_i2.Service? _service) => + super.noSuchMethod(Invocation.setter(#service, _service), + returnValueForMissingStub: null); + @override + String get uuid => + (super.noSuchMethod(Invocation.getter(#uuid), returnValue: '') as String); + @override + set uuid(String? _uuid) => super.noSuchMethod(Invocation.setter(#uuid, _uuid), + returnValueForMissingStub: null); + @override + bool get isReadable => + (super.noSuchMethod(Invocation.getter(#isReadable), returnValue: false) + as bool); + @override + set isReadable(bool? _isReadable) => + super.noSuchMethod(Invocation.setter(#isReadable, _isReadable), + returnValueForMissingStub: null); + @override + bool get isWritableWithResponse => + (super.noSuchMethod(Invocation.getter(#isWritableWithResponse), + returnValue: false) as bool); + @override + set isWritableWithResponse(bool? _isWritableWithResponse) => + super.noSuchMethod( + Invocation.setter(#isWritableWithResponse, _isWritableWithResponse), + returnValueForMissingStub: null); + @override + bool get isWritableWithoutResponse => + (super.noSuchMethod(Invocation.getter(#isWritableWithoutResponse), + returnValue: false) as bool); + @override + set isWritableWithoutResponse(bool? _isWritableWithoutResponse) => + super.noSuchMethod( + Invocation.setter( + #isWritableWithoutResponse, _isWritableWithoutResponse), + returnValueForMissingStub: null); + @override + bool get isNotifiable => + (super.noSuchMethod(Invocation.getter(#isNotifiable), returnValue: false) + as bool); + @override + set isNotifiable(bool? _isNotifiable) => + super.noSuchMethod(Invocation.setter(#isNotifiable, _isNotifiable), + returnValueForMissingStub: null); + @override + bool get isIndicatable => + (super.noSuchMethod(Invocation.getter(#isIndicatable), returnValue: false) + as bool); + @override + set isIndicatable(bool? _isIndicatable) => + super.noSuchMethod(Invocation.setter(#isIndicatable, _isIndicatable), + returnValueForMissingStub: null); + @override + bool operator ==(Object? other) => + (super.noSuchMethod(Invocation.method(#==, [other]), returnValue: false) + as bool); + @override + String toString() => + (super.noSuchMethod(Invocation.method(#toString, []), returnValue: '') + as String); + @override + _i4.Future<_i3.Uint8List> read({String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#read, [], {#transactionId: transactionId}), + returnValue: Future.value(_FakeUint8List())) + as _i4.Future<_i3.Uint8List>); + @override + _i4.Future write(_i3.Uint8List? value, bool? withResponse, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #write, [value, withResponse], {#transactionId: transactionId}), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Stream<_i3.Uint8List> monitor({String? transactionId}) => (super + .noSuchMethod( + Invocation.method(#monitor, [], {#transactionId: transactionId}), + returnValue: Stream<_i3.Uint8List>.empty()) + as _i4.Stream<_i3.Uint8List>); + @override + _i4.Future> descriptors() => + (super.noSuchMethod(Invocation.method(#descriptors, []), + returnValue: Future.value(<_i2.Descriptor>[])) + as _i4.Future>); + @override + _i4.Future<_i2.DescriptorWithValue> readDescriptor(String? descriptorUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#readDescriptor, [descriptorUuid], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptorWithValue())) + as _i4.Future<_i2.DescriptorWithValue>); + @override + _i4.Future<_i2.Descriptor> writeDescriptor( + String? descriptorUuid, _i3.Uint8List? value, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#writeDescriptor, [descriptorUuid, value], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptor())) + as _i4.Future<_i2.Descriptor>); +} + +/// A class which mocks [DescriptorWithValue]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockDescriptorWithValue extends _i1.Mock + implements _i2.DescriptorWithValue { + MockDescriptorWithValue() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Uint8List get value => (super.noSuchMethod(Invocation.getter(#value), + returnValue: _FakeUint8List()) as _i3.Uint8List); + @override + set value(_i3.Uint8List? _value) => + super.noSuchMethod(Invocation.setter(#value, _value), + returnValueForMissingStub: null); + @override + _i2.Characteristic get characteristic => + (super.noSuchMethod(Invocation.getter(#characteristic), + returnValue: _FakeCharacteristic()) as _i2.Characteristic); + @override + String get uuid => + (super.noSuchMethod(Invocation.getter(#uuid), returnValue: '') as String); + @override + int get hashCode => + (super.noSuchMethod(Invocation.getter(#hashCode), returnValue: 0) as int); + @override + _i4.Future<_i3.Uint8List> read({String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#read, [], {#transactionId: transactionId}), + returnValue: Future.value(_FakeUint8List())) + as _i4.Future<_i3.Uint8List>); + @override + _i4.Future write(_i3.Uint8List? value, {String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#write, [value], {#transactionId: transactionId}), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + bool operator ==(Object? other) => + (super.noSuchMethod(Invocation.method(#==, [other]), returnValue: false) + as bool); +} diff --git a/test/src/bridge/lib_core_test.dart b/test/src/bridge/lib_core_test.dart index 15e9bd93..7ed3679f 100644 --- a/test/src/bridge/lib_core_test.dart +++ b/test/src/bridge/lib_core_test.dart @@ -9,179 +9,309 @@ import 'package:flutter_ble_lib/src/_internal.dart'; import 'package:flutter_ble_lib/src/_constants.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; +import 'package:mockito/annotations.dart'; +import './lib_core_test.mocks.dart'; import '../../json/ble_error_jsons.dart'; -import '../../mock/mocks.dart'; const flutterBleLibMethodChannelName = 'flutter_ble_lib'; const monitorCharacteristicEventChannelName = flutterBleLibMethodChannelName + '/monitorCharacteristic'; - +const mockServiceUuid = "A0C8AAC8-2C37-4CE4-9110-EA7E09704D54"; +const mockCharacteristicUuid = "A56CCE6A-2178-4710-81CA-7895309A1350"; +const mockCharacteristicId = 44; +const mockServiceId = 42; +const mockId = 43; +const mockTransactionId = "asdasd123asd"; + +@GenerateMocks([Peripheral, InternalBleManager]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); - FlutterBleLib bleLib; - Peripheral peripheral = PeripheralMock(); + late FlutterBleLib bleLib; + final peripheral = MockPeripheral(); MethodChannel methodChannel = MethodChannel(flutterBleLibMethodChannelName); MethodChannel eventMethodChannel = MethodChannel(monitorCharacteristicEventChannelName); setUp(() { - bleLib = FlutterBleLib(); + bleLib = FlutterBleLib(MockInternalBleManager()); when(peripheral.identifier).thenReturn("4B:99:4C:34:DE:77"); - methodChannel.setMockMethodCallHandler((call) => Future.value("")); - eventMethodChannel.setMockMethodCallHandler((call) => Future.value("")); + methodChannel.setMockMethodCallHandler((call) { + return Future.value(""); + }); + eventMethodChannel.setMockMethodCallHandler((call) { + return Future.value(""); + }); }); - Future emitPlatformError(String errorJson) => - ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( - monitorCharacteristicEventChannelName, - const StandardMethodCodec() - .encodeErrorEnvelope(code: "irrelevant", details: errorJson), - (ByteData data) {}); - - Future emitMonitoringEvent(String eventJson) => - ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( - monitorCharacteristicEventChannelName, - const StandardMethodCodec().encodeSuccessEnvelope(eventJson), - (ByteData data) {}); - - Future emitStreamCompletion() => - ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( - monitorCharacteristicEventChannelName, - null, - (ByteData data) {}, - ); + Future _emitPlatformError(String errorJson) async { + final serBinding = ServicesBinding.instance; + if (serBinding == null) { + return; + } + await serBinding.defaultBinaryMessenger.handlePlatformMessage( + monitorCharacteristicEventChannelName, + const StandardMethodCodec().encodeErrorEnvelope( + code: "irrelevant", + details: errorJson + ), + (data) { + print(data); + } + ); + } + + Future _emitMonitoringEvent(String eventJson) async { + final serBinding = ServicesBinding.instance; + if (serBinding == null) { + return; + } + await serBinding.defaultBinaryMessenger.handlePlatformMessage( + monitorCharacteristicEventChannelName, + const StandardMethodCodec().encodeSuccessEnvelope(eventJson), + (data) {} + ); + } + + Future _emitStreamCompletion() async { + final serBinding = ServicesBinding.instance; + if (serBinding == null) { + return; + } + await serBinding.defaultBinaryMessenger.handlePlatformMessage( + monitorCharacteristicEventChannelName, + null, + (data) {}, + ); + } CharacteristicWithValueAndTransactionId createCharacteristicFromDecodedJson( - Map decodedRoot) { - Map decodedCharacteristic = decodedRoot["characteristic"]; + Map decodedRoot + ) { + Map decodedCharacteristic = decodedRoot["characteristic"]; String transactionId = decodedRoot["transactionId"]; return CharacteristicWithValueAndTransactionId.fromJson( decodedCharacteristic, - Service.fromJson(decodedRoot, peripheral, null), - null, + Service.fromJson(decodedRoot, peripheral, MockInternalBleManager()), + MockInternalBleManager(), ).setTransactionId(transactionId); } - Map createRawCharacteristic( - {int id, - int serviceId, - String serviceUuid, - String characteristicUuid, - String transactionId, - String base64value}) => - { - "serviceUuid": serviceUuid, - "serviceId": serviceId, - "transactionId": transactionId, - "characteristic": { - "characteristicUuid": characteristicUuid, - "id": id, - "isReadable": true, - "isWritableWithResponse": false, - "isWritableWithoutResponse": false, - "isNotifiable": true, - "isIndicatable": false, - "value": base64value ?? "" - } - }; + Map createRawCharacteristic( + { + bool mockIds = false, + int? id, + int? serviceId, + String? serviceUuid, + String? characteristicUuid, + String? transactionId, + String base64value = "" + }) { + if (mockIds) { + serviceUuid ??= mockServiceUuid; + serviceId ??= mockServiceId; + characteristicUuid ??= mockCharacteristicUuid; + id ??= mockCharacteristicId; + } + + return { + "serviceUuid": serviceUuid, + "serviceId": serviceId, + "transactionId": transactionId, + "characteristic": { + "characteristicUuid": characteristicUuid, + "id": id, + "isReadable": true, + "isWritableWithResponse": false, + "isWritableWithoutResponse": false, + "isNotifiable": true, + "isIndicatable": false, + "value": base64value, + } + }; + } test('monitorCharacteristicForIdentifier cancels on stream error', () async { - expectLater( - bleLib.monitorCharacteristicForIdentifier(peripheral, 123, null), - emitsInOrder([ - emitsError(isInstanceOf()), - emitsDone, - ])); - await emitPlatformError(cancellationErrorJson); + final mockTransId = "asdasd"; + + final fut = expectLater( + bleLib.monitorCharacteristicForIdentifier(peripheral, 123, mockTransId), + emitsInOrder([ + emitsError(isInstanceOf()), + emitsDone, + ]) + ); + await _emitPlatformError(cancellationErrorJson(mockTransId)); + + await fut; }); test('monitorCharacteristicForDevice cancels on stream error', () async { - expectLater( + String mockTransId = ""; + final fut = expectLater( bleLib.monitorCharacteristicForDevice( - peripheral, "serviceUuid", "characteristicUuid", null), + peripheral, + "serviceUuid", + "characteristicUuid", + mockTransId + ), emitsInOrder([ emitsError(isInstanceOf()), emitsDone, ])); - await emitPlatformError(cancellationErrorJson); + await _emitPlatformError(cancellationErrorJson(mockTransId)); + await fut; }); test('monitorCharacteristicForService cancels on stream error', () async { - expectLater( + String mockTransId = ""; + final fut = expectLater( bleLib.monitorCharacteristicForService( - peripheral, 123, "characteristicUuid", null), + peripheral, 123, "characteristicUuid", mockTransId), emitsInOrder([ emitsError(isInstanceOf()), emitsDone, ])); - await emitPlatformError(cancellationErrorJson); + await _emitPlatformError(cancellationErrorJson(mockTransId)); + await fut; }); test( 'monitorCharacteristicForIdentifier streams events with matching characteristic id and transaction id', () async { - expectLater( - bleLib.monitorCharacteristicForIdentifier(peripheral, 1, "1"), - emitsInOrder([ - emits(equals(Uint8List.fromList([1, 0, 0, 0]))), - emitsDone - ])); - - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( - id: 1, transactionId: "1", base64value: "AQAAAA=="))); //[1,0,0,0] - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( - id: 1, transactionId: "2", base64value: "AAEAAA=="))); //[0,1,0,0] - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( - id: 2, transactionId: "1", base64value: "AAABAA=="))); //[0,0,1,0] - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( - id: 2, transactionId: "2", base64value: "AAAAAQ=="))); //[0,0,0,1] - await emitStreamCompletion(); + final fut = expectLater( + bleLib.monitorCharacteristicForIdentifier(peripheral, 1, "1"), + emitsInOrder([ + emits( + equals( + Uint8List.fromList([1, 0, 0, 0]) + ) + ), + emitsDone + ]) + ); + + await _emitMonitoringEvent( + jsonEncode( + createRawCharacteristic( + mockIds: true, + id: 1, + transactionId: "1", + base64value: "AQAAAA==" + ) + ) + ); //[1,0,0,0] + await _emitMonitoringEvent( + jsonEncode( + createRawCharacteristic( + mockIds: true, + id: 1, + transactionId: "2", + base64value: "AAEAAA==" + ) + ) + ); //[0,1,0,0] + await _emitMonitoringEvent( + jsonEncode( + createRawCharacteristic( + mockIds: true, + id: 2, + transactionId: "1", + base64value: "AAABAA==" + ) + ) + ); //[0,0,1,0] + await _emitMonitoringEvent( + jsonEncode( + createRawCharacteristic( + mockIds: true, + id: 2, + transactionId: "2", + base64value: "AAAAAQ==" + ) + ) + ); //[0,0,0,1] + await _emitStreamCompletion(); + await fut; }); test( 'monitorCharacteristicForDevice streams events with matching characteristic uuid, service uuid and transaction id', () async { - expectLater( - bleLib.monitorCharacteristicForDevice( - peripheral, "serviceUuid", "characteristicUuid", "1"), - emitsInOrder([ - emits(equals(createCharacteristicFromDecodedJson( - createRawCharacteristic( - serviceUuid: "serviceUuid", - characteristicUuid: "characteristicUuid", - transactionId: "1")))), - emitsDone - ])); - - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( + final expected = + createRawCharacteristic( + mockIds: true, serviceUuid: "serviceUuid", characteristicUuid: "characteristicUuid", - transactionId: "1"))); - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( + transactionId: "1" + ); + final fut = expectLater( + bleLib.monitorCharacteristicForDevice( + peripheral, + "serviceUuid", + "characteristicUuid", + "1" + ), + emitsInOrder([ + emits( + equals( + createCharacteristicFromDecodedJson(expected) + ) + ), + emitsDone + ]) + ); + final valid = + createRawCharacteristic( + mockIds: true, + serviceUuid: "serviceUuid", + characteristicUuid: "characteristicUuid", + transactionId: "1" + ); + final invalid1 = + createRawCharacteristic( + mockIds: true, serviceUuid: "serviceUuid", characteristicUuid: "fakeUuid", - transactionId: "1"))); - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( + transactionId: "1" + ); + final invalid2 = + createRawCharacteristic( + mockIds: true, serviceUuid: "fakeUuid", characteristicUuid: "characteristicUuid", - transactionId: "1"))); - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( + transactionId: "1" + ); + final invalid3 = + createRawCharacteristic( + mockIds: true, serviceUuid: "serviceUuid", characteristicUuid: "characteristicUuid", - transactionId: "2"))); - await emitStreamCompletion(); + transactionId: "2" + ); + expect(expected, equals(valid)); + expect(expected, isNot(equals(invalid1))); + expect(expected, isNot(equals(invalid2))); + expect(expected, isNot(equals(invalid3))); + + await _emitMonitoringEvent(jsonEncode(valid)); + await _emitMonitoringEvent(jsonEncode(invalid1)); + await _emitMonitoringEvent(jsonEncode(invalid2)); + await _emitMonitoringEvent(jsonEncode(invalid3)); + await _emitStreamCompletion(); + await fut; }); test( 'monitorCharacteristicForService streams events with matching service id, characteristic uuid and transaction id', () async { - expectLater( + final fut = expectLater( bleLib.monitorCharacteristicForService( peripheral, 1, "characteristicUuid", "1"), emitsInOrder([ emits(equals( createCharacteristicFromDecodedJson(createRawCharacteristic( + mockIds: true, serviceId: 1, characteristicUuid: "characteristicUuid", transactionId: "1", @@ -189,21 +319,26 @@ void main() { emitsDone ])); - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( + await _emitMonitoringEvent(jsonEncode(createRawCharacteristic( + mockIds: true, serviceId: 1, characteristicUuid: "characteristicUuid", transactionId: "1"))); - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( + await _emitMonitoringEvent(jsonEncode(createRawCharacteristic( + mockIds: true, serviceId: 1, characteristicUuid: "fakeUuid", transactionId: "1"))); - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( + await _emitMonitoringEvent(jsonEncode(createRawCharacteristic( + mockIds: true, serviceId: 2, characteristicUuid: "characteristicUuid", transactionId: "1"))); - await emitMonitoringEvent(jsonEncode(createRawCharacteristic( + await _emitMonitoringEvent(jsonEncode(createRawCharacteristic( + mockIds: true, serviceId: 1, characteristicUuid: "characteristicUuid", transactionId: "2"))); - await emitStreamCompletion(); + await _emitStreamCompletion(); + await fut; }); test( diff --git a/test/src/bridge/lib_core_test.mocks.dart b/test/src/bridge/lib_core_test.mocks.dart new file mode 100644 index 00000000..4cf52bfd --- /dev/null +++ b/test/src/bridge/lib_core_test.mocks.dart @@ -0,0 +1,604 @@ +// Mocks generated by Mockito 5.0.3 from annotations +// in flutter_ble_lib/test/src/bridge/lib_core_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i4; +import 'dart:typed_data' as _i3; + +import 'package:flutter_ble_lib/flutter_ble_lib.dart' as _i2; +import 'package:flutter_ble_lib/src/_internal.dart' as _i5; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: comment_references +// ignore_for_file: unnecessary_parenthesis + +class _FakeCharacteristicWithValue extends _i1.Fake + implements _i2.CharacteristicWithValue {} + +class _FakeCharacteristic extends _i1.Fake implements _i2.Characteristic {} + +class _FakeDescriptorWithValue extends _i1.Fake + implements _i2.DescriptorWithValue {} + +class _FakeDescriptor extends _i1.Fake implements _i2.Descriptor {} + +class _FakePeripheral extends _i1.Fake implements _i2.Peripheral {} + +class _FakeUint8List extends _i1.Fake implements _i3.Uint8List {} + +/// A class which mocks [Peripheral]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPeripheral extends _i1.Mock implements _i2.Peripheral { + MockPeripheral() { + _i1.throwOnMissingStub(this); + } + + @override + String get identifier => + (super.noSuchMethod(Invocation.getter(#identifier), returnValue: '') + as String); + @override + set identifier(String? _identifier) => + super.noSuchMethod(Invocation.setter(#identifier, _identifier), + returnValueForMissingStub: null); + @override + _i4.Future connect( + {bool? isAutoConnect = false, + int? requestMtu = 0, + bool? refreshGatt = false, + Duration? timeout}) => + (super.noSuchMethod( + Invocation.method(#connect, [], { + #isAutoConnect: isAutoConnect, + #requestMtu: requestMtu, + #refreshGatt: refreshGatt, + #timeout: timeout + }), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Stream<_i2.PeripheralConnectionState> observeConnectionState( + {bool? emitCurrentValue = false, + bool? completeOnDisconnect = false}) => + (super.noSuchMethod( + Invocation.method(#observeConnectionState, [], { + #emitCurrentValue: emitCurrentValue, + #completeOnDisconnect: completeOnDisconnect + }), + returnValue: Stream<_i2.PeripheralConnectionState>.empty()) + as _i4.Stream<_i2.PeripheralConnectionState>); + @override + _i4.Future isConnected() => + (super.noSuchMethod(Invocation.method(#isConnected, []), + returnValue: Future.value(false)) as _i4.Future); + @override + _i4.Future disconnectOrCancelConnection() => + (super.noSuchMethod(Invocation.method(#disconnectOrCancelConnection, []), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future discoverAllServicesAndCharacteristics( + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method(#discoverAllServicesAndCharacteristics, [], + {#transactionId: transactionId}), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future> services() => + (super.noSuchMethod(Invocation.method(#services, []), + returnValue: Future.value(<_i2.Service>[])) + as _i4.Future>); + @override + _i4.Future> characteristics(String? servicedUuid) => + (super.noSuchMethod(Invocation.method(#characteristics, [servicedUuid]), + returnValue: Future.value(<_i2.Characteristic>[])) + as _i4.Future>); + @override + _i4.Future rssi({String? transactionId}) => (super.noSuchMethod( + Invocation.method(#rssi, [], {#transactionId: transactionId}), + returnValue: Future.value(0)) as _i4.Future); + @override + _i4.Future requestMtu(int? mtu, {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #requestMtu, [mtu], {#transactionId: transactionId}), + returnValue: Future.value(0)) as _i4.Future); + @override + _i4.Future<_i2.CharacteristicWithValue> readCharacteristic( + String? serviceUuid, String? characteristicUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #readCharacteristic, + [serviceUuid, characteristicUuid], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeCharacteristicWithValue())) + as _i4.Future<_i2.CharacteristicWithValue>); + @override + _i4.Future<_i2.Characteristic> writeCharacteristic(String? serviceUuid, + String? characteristicUuid, _i3.Uint8List? value, bool? withResponse, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #writeCharacteristic, + [serviceUuid, characteristicUuid, value, withResponse], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeCharacteristic())) + as _i4.Future<_i2.Characteristic>); + @override + _i4.Future> descriptorsForCharacteristic( + String? serviceUuid, String? characteristicUuid) => + (super.noSuchMethod( + Invocation.method(#descriptorsForCharacteristic, + [serviceUuid, characteristicUuid]), + returnValue: Future.value(<_i2.Descriptor>[])) + as _i4.Future>); + @override + _i4.Future<_i2.DescriptorWithValue> readDescriptor(String? serviceUuid, + String? characteristicUuid, String? descriptorUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #readDescriptor, + [serviceUuid, characteristicUuid, descriptorUuid], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptorWithValue())) + as _i4.Future<_i2.DescriptorWithValue>); + @override + _i4.Future<_i2.Descriptor> writeDescriptor( + String? serviceUuid, + String? characteristicUuid, + String? descriptorUuid, + _i3.Uint8List? value, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #writeDescriptor, + [serviceUuid, characteristicUuid, descriptorUuid, value], + {#transactionId: transactionId}), + returnValue: Future.value(_FakeDescriptor())) + as _i4.Future<_i2.Descriptor>); + @override + _i4.Stream<_i2.CharacteristicWithValue> monitorCharacteristic( + String? serviceUuid, String? characteristicUuid, + {String? transactionId}) => + (super.noSuchMethod( + Invocation.method( + #monitorCharacteristic, + [serviceUuid, characteristicUuid], + {#transactionId: transactionId}), + returnValue: Stream<_i2.CharacteristicWithValue>.empty()) + as _i4.Stream<_i2.CharacteristicWithValue>); + @override + String toString() => + (super.noSuchMethod(Invocation.method(#toString, []), returnValue: '') + as String); +} + +/// A class which mocks [InternalBleManager]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockInternalBleManager extends _i1.Mock + implements _i5.InternalBleManager { + MockInternalBleManager() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future createClient( + {String? restoreStateIdentifier, + _i2.RestoreStateAction? restoreStateAction}) => + (super.noSuchMethod( + Invocation.method(#createClient, [], { + #restoreStateIdentifier: restoreStateIdentifier, + #restoreStateAction: restoreStateAction + }), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future destroyClient() => + (super.noSuchMethod(Invocation.method(#destroyClient, []), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future cancelTransaction(String? transactionId) => (super + .noSuchMethod(Invocation.method(#cancelTransaction, [transactionId]), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future enableRadio({String? transactionId}) => (super.noSuchMethod( + Invocation.method(#enableRadio, [], {#transactionId: transactionId}), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future disableRadio({String? transactionId}) => (super.noSuchMethod( + Invocation.method(#disableRadio, [], {#transactionId: transactionId}), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future<_i2.BluetoothState> bluetoothState() => + (super.noSuchMethod(Invocation.method(#bluetoothState, []), + returnValue: Future.value(_i2.BluetoothState.UNKNOWN)) + as _i4.Future<_i2.BluetoothState>); + @override + _i4.Stream<_i2.BluetoothState> observeBluetoothState( + {bool? emitCurrentValue = true}) => + (super.noSuchMethod( + Invocation.method(#observeBluetoothState, [], + {#emitCurrentValue: emitCurrentValue}), + returnValue: Stream<_i2.BluetoothState>.empty()) + as _i4.Stream<_i2.BluetoothState>); + @override + _i4.Stream<_i2.ScanResult> startPeripheralScan( + {int? scanMode = 0, + int? callbackType = 1, + List? uuids = const [], + bool? allowDuplicates = false}) => + (super.noSuchMethod( + Invocation.method(#startPeripheralScan, [], { + #scanMode: scanMode, + #callbackType: callbackType, + #uuids: uuids, + #allowDuplicates: allowDuplicates + }), + returnValue: Stream<_i2.ScanResult>.empty()) + as _i4.Stream<_i2.ScanResult>); + @override + _i4.Future stopPeripheralScan() => + (super.noSuchMethod(Invocation.method(#stopPeripheralScan, []), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i2.Peripheral createUnsafePeripheral(String? peripheralId, {String? name}) => + (super.noSuchMethod( + Invocation.method( + #createUnsafePeripheral, [peripheralId], {#name: name}), + returnValue: _FakePeripheral()) as _i2.Peripheral); + @override + _i4.Future connectToPeripheral(String? identifier, + {bool? isAutoConnect, + int? requestMtu, + bool? refreshGatt, + Duration? timeout}) => + (super.noSuchMethod( + Invocation.method(#connectToPeripheral, [ + identifier + ], { + #isAutoConnect: isAutoConnect, + #requestMtu: requestMtu, + #refreshGatt: refreshGatt, + #timeout: timeout + }), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Stream<_i2.PeripheralConnectionState> observePeripheralConnectionState( + String? peripheralIdentifier, + bool? emitCurrentValue, + bool? completeOnDisconnect) => + (super.noSuchMethod( + Invocation.method(#observePeripheralConnectionState, [ + peripheralIdentifier, + emitCurrentValue, + completeOnDisconnect + ]), + returnValue: Stream<_i2.PeripheralConnectionState>.empty()) + as _i4.Stream<_i2.PeripheralConnectionState>); + @override + _i4.Future disconnectOrCancelPeripheralConnection( + String? peripheralIdentifier) => + (super.noSuchMethod( + Invocation.method( + #disconnectOrCancelPeripheralConnection, [peripheralIdentifier]), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future isPeripheralConnected(String? peripheralIdentifier) => + (super.noSuchMethod( + Invocation.method(#isPeripheralConnected, [peripheralIdentifier]), + returnValue: Future.value(false)) as _i4.Future); + @override + _i4.Future<_i2.LogLevel> logLevel() => + (super.noSuchMethod(Invocation.method(#logLevel, []), + returnValue: Future.value(_i2.LogLevel.none)) + as _i4.Future<_i2.LogLevel>); + @override + _i4.Future setLogLevel(_i2.LogLevel? logLevel) => + (super.noSuchMethod(Invocation.method(#setLogLevel, [logLevel]), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future> characteristics( + _i2.Peripheral? peripheral, String? serviceUuid) => + (super.noSuchMethod( + Invocation.method(#characteristics, [peripheral, serviceUuid]), + returnValue: Future.value(<_i2.Characteristic>[])) + as _i4.Future>); + @override + _i4.Future> services(_i2.Peripheral? peripheral) => + (super.noSuchMethod(Invocation.method(#services, [peripheral]), + returnValue: Future.value(<_i2.Service>[])) + as _i4.Future>); + @override + _i4.Future> descriptorsForPeripheral( + _i2.Peripheral? peripheral, + String? serviceUuid, + String? characteristicUuid) => + (super.noSuchMethod( + Invocation.method(#descriptorsForPeripheral, + [peripheral, serviceUuid, characteristicUuid]), + returnValue: Future.value(<_i2.Descriptor>[])) + as _i4.Future>); + @override + _i4.Future> descriptorsForService( + _i2.Service? service, String? characteristicUuid) => + (super.noSuchMethod( + Invocation.method( + #descriptorsForService, [service, characteristicUuid]), + returnValue: Future.value(<_i2.Descriptor>[])) + as _i4.Future>); + @override + _i4.Future> descriptorsForCharacteristic( + _i2.Characteristic? characteristic) => + (super.noSuchMethod( + Invocation.method(#descriptorsForCharacteristic, [characteristic]), + returnValue: + Future.value(<_i2.Descriptor>[])) as _i4 + .Future>); + @override + _i4.Future discoverAllServicesAndCharacteristics( + _i2.Peripheral? peripheral, String? transactionId) => + (super.noSuchMethod( + Invocation.method(#discoverAllServicesAndCharacteristics, + [peripheral, transactionId]), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future> characteristicsForService( + _i2.Service? service) => + (super.noSuchMethod( + Invocation.method(#characteristicsForService, [service]), + returnValue: Future.value(<_i2.Characteristic>[])) + as _i4.Future>); + @override + _i4.Future rssi(_i2.Peripheral? peripheral, String? transactionId) => + (super.noSuchMethod(Invocation.method(#rssi, [peripheral, transactionId]), + returnValue: Future.value(0)) as _i4.Future); + @override + _i4.Future requestMtu( + _i2.Peripheral? peripheral, int? mtu, String? transactionId) => + (super.noSuchMethod( + Invocation.method(#requestMtu, [peripheral, mtu, transactionId]), + returnValue: Future.value(0)) as _i4.Future); + @override + _i4.Future> knownPeripherals( + List? peripheralIdentifiers) => + (super.noSuchMethod( + Invocation.method(#knownPeripherals, [peripheralIdentifiers]), + returnValue: Future.value(<_i2.Peripheral>[])) + as _i4.Future>); + @override + _i4.Future> connectedPeripherals( + List? serviceUuids) => + (super.noSuchMethod( + Invocation.method(#connectedPeripherals, [serviceUuids]), + returnValue: Future.value(<_i2.Peripheral>[])) + as _i4.Future>); + @override + _i4.Future<_i3.Uint8List> readCharacteristicForIdentifier( + _i2.Peripheral? peripheral, + _i5.InternalCharacteristic? characteristic, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#readCharacteristicForIdentifier, + [peripheral, characteristic, transactionId]), + returnValue: Future.value(_FakeUint8List())) + as _i4.Future<_i3.Uint8List>); + @override + _i4.Future<_i2.CharacteristicWithValue> readCharacteristicForDevice( + _i2.Peripheral? peripheral, + String? serviceUuid, + String? characteristicUuid, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#readCharacteristicForDevice, + [peripheral, serviceUuid, characteristicUuid, transactionId]), + returnValue: Future.value(_FakeCharacteristicWithValue())) + as _i4.Future<_i2.CharacteristicWithValue>); + @override + _i4.Future<_i2.CharacteristicWithValue> readCharacteristicForService( + _i2.Peripheral? peripheral, + _i5.InternalService? service, + String? characteristicUuid, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#readCharacteristicForService, + [peripheral, service, characteristicUuid, transactionId]), + returnValue: Future.value(_FakeCharacteristicWithValue())) + as _i4.Future<_i2.CharacteristicWithValue>); + @override + _i4.Future writeCharacteristicForIdentifier( + _i2.Peripheral? peripheral, + _i5.InternalCharacteristic? characteristic, + _i3.Uint8List? value, + bool? withResponse, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeCharacteristicForIdentifier, + [peripheral, characteristic, value, withResponse, transactionId]), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future<_i2.Characteristic> writeCharacteristicForDevice( + _i2.Peripheral? peripheral, + String? serviceUuid, + String? characteristicUuid, + _i3.Uint8List? value, + bool? withResponse, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeCharacteristicForDevice, [ + peripheral, + serviceUuid, + characteristicUuid, + value, + withResponse, + transactionId + ]), + returnValue: Future.value(_FakeCharacteristic())) + as _i4.Future<_i2.Characteristic>); + @override + _i4.Future<_i2.Characteristic> writeCharacteristicForService( + _i2.Peripheral? peripheral, + _i5.InternalService? service, + String? characteristicUuid, + _i3.Uint8List? value, + bool? withResponse, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeCharacteristicForService, [ + peripheral, + service, + characteristicUuid, + value, + withResponse, + transactionId + ]), + returnValue: Future.value(_FakeCharacteristic())) + as _i4.Future<_i2.Characteristic>); + @override + _i4.Stream<_i2.CharacteristicWithValue> monitorCharacteristicForDevice( + _i2.Peripheral? peripheral, + String? serviceUuid, + String? characteristicUuid, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#monitorCharacteristicForDevice, + [peripheral, serviceUuid, characteristicUuid, transactionId]), + returnValue: Stream<_i2.CharacteristicWithValue>.empty()) + as _i4.Stream<_i2.CharacteristicWithValue>); + @override + _i4.Stream<_i2.CharacteristicWithValue> monitorCharacteristicForService( + _i2.Peripheral? peripheral, + _i5.InternalService? service, + String? characteristicUuid, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#monitorCharacteristicForService, + [peripheral, service, characteristicUuid, transactionId]), + returnValue: Stream<_i2.CharacteristicWithValue>.empty()) + as _i4.Stream<_i2.CharacteristicWithValue>); + @override + _i4.Stream<_i3.Uint8List> monitorCharacteristicForIdentifier( + _i2.Peripheral? peripheral, + _i5.InternalCharacteristic? characteristic, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#monitorCharacteristicForIdentifier, + [peripheral, characteristic, transactionId]), + returnValue: Stream<_i3.Uint8List>.empty()) + as _i4.Stream<_i3.Uint8List>); + @override + _i4.Future writeDescriptorForIdentifier(_i2.Descriptor? descriptor, + _i3.Uint8List? value, String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeDescriptorForIdentifier, + [descriptor, value, transactionId]), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i4.Future); + @override + _i4.Future<_i2.Descriptor> writeDescriptorForCharacteristic( + _i2.Characteristic? characteristic, + String? descriptorUuid, + _i3.Uint8List? value, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeDescriptorForCharacteristic, + [characteristic, descriptorUuid, value, transactionId]), + returnValue: Future.value(_FakeDescriptor())) + as _i4.Future<_i2.Descriptor>); + @override + _i4.Future<_i2.Descriptor> writeDescriptorForService( + _i2.Service? service, + String? characteristicUuid, + String? descriptorUuid, + _i3.Uint8List? value, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeDescriptorForService, [ + service, + characteristicUuid, + descriptorUuid, + value, + transactionId + ]), + returnValue: Future.value(_FakeDescriptor())) + as _i4.Future<_i2.Descriptor>); + @override + _i4.Future<_i2.Descriptor> writeDescriptorForPeripheral( + _i2.Peripheral? peripheral, + String? serviceUuid, + String? characteristicUuid, + String? descriptorUuid, + _i3.Uint8List? value, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeDescriptorForPeripheral, [ + peripheral, + serviceUuid, + characteristicUuid, + descriptorUuid, + value, + transactionId + ]), + returnValue: Future.value(_FakeDescriptor())) + as _i4.Future<_i2.Descriptor>); + @override + _i4.Future<_i3.Uint8List> readDescriptorForIdentifier( + _i2.Descriptor? descriptor, String? transactionId) => + (super.noSuchMethod( + Invocation.method( + #readDescriptorForIdentifier, [descriptor, transactionId]), + returnValue: Future.value(_FakeUint8List())) + as _i4.Future<_i3.Uint8List>); + @override + _i4.Future<_i2.DescriptorWithValue> readDescriptorForCharacteristic( + _i2.Characteristic? characteristic, + String? descriptorUuid, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#readDescriptorForCharacteristic, + [characteristic, descriptorUuid, transactionId]), + returnValue: Future.value(_FakeDescriptorWithValue())) + as _i4.Future<_i2.DescriptorWithValue>); + @override + _i4.Future<_i2.DescriptorWithValue> readDescriptorForService( + _i2.Service? service, + String? characteristicUuid, + String? descriptorUuid, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#readDescriptorForService, + [service, characteristicUuid, descriptorUuid, transactionId]), + returnValue: Future.value(_FakeDescriptorWithValue())) + as _i4.Future<_i2.DescriptorWithValue>); + @override + _i4.Future<_i2.DescriptorWithValue> readDescriptorForPeripheral( + _i2.Peripheral? peripheral, + String? serviceUuid, + String? characteristicUuid, + String? descriptorUuid, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#readDescriptorForPeripheral, [ + peripheral, + serviceUuid, + characteristicUuid, + descriptorUuid, + transactionId + ]), + returnValue: Future.value(_FakeDescriptorWithValue())) + as _i4.Future<_i2.DescriptorWithValue>); +} diff --git a/test/test_util/characteristic_generator.dart b/test/test_util/characteristic_generator.dart index 6b979c6b..0bb0bf7d 100644 --- a/test/test_util/characteristic_generator.dart +++ b/test/test_util/characteristic_generator.dart @@ -1,14 +1,23 @@ import 'dart:convert'; import 'package:flutter_ble_lib/flutter_ble_lib.dart'; +import 'package:mockito/annotations.dart'; import 'package:flutter_ble_lib/src/_managers_for_classes.dart'; +import './characteristic_generator.mocks.dart'; +export './characteristic_generator.mocks.dart'; + +@GenerateMocks( + [], + customMocks:[ + MockSpec(returnNullOnMissingStub: true), +]) class CharacteristicGenerator { - ManagerForCharacteristic managerForCharacteristic; + MockManagerForCharacteristic managerForCharacteristic; CharacteristicGenerator(this.managerForCharacteristic); - Map _createRawCharacteristic(int seed) => { + Map _createRawCharacteristic(int seed) => { "characteristicUuid": seed.toString(), "id": seed, "isReadable": seed % 2 == 0, diff --git a/test/test_util/characteristic_generator.mocks.dart b/test/test_util/characteristic_generator.mocks.dart new file mode 100644 index 00000000..6e19f689 --- /dev/null +++ b/test/test_util/characteristic_generator.mocks.dart @@ -0,0 +1,89 @@ +// Mocks generated by Mockito 5.0.3 from annotations +// in flutter_ble_lib/test/test_util/characteristic_generator.dart. +// Do not manually edit this file. + +import 'dart:async' as _i5; +import 'dart:typed_data' as _i2; + +import 'package:flutter_ble_lib/flutter_ble_lib.dart' as _i3; +import 'package:flutter_ble_lib/src/_internal.dart' as _i6; +import 'package:flutter_ble_lib/src/_managers_for_classes.dart' as _i4; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: comment_references +// ignore_for_file: unnecessary_parenthesis + +class _FakeUint8List extends _i1.Fake implements _i2.Uint8List {} + +class _FakeDescriptorWithValue extends _i1.Fake + implements _i3.DescriptorWithValue {} + +class _FakeDescriptor extends _i1.Fake implements _i3.Descriptor {} + +/// A class which mocks [ManagerForCharacteristic]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockManagerForCharacteristic extends _i1.Mock + implements _i4.ManagerForCharacteristic { + @override + _i5.Future<_i2.Uint8List> readCharacteristicForIdentifier( + _i3.Peripheral? peripheral, + _i6.InternalCharacteristic? characteristic, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#readCharacteristicForIdentifier, + [peripheral, characteristic, transactionId]), + returnValue: Future.value(_FakeUint8List())) + as _i5.Future<_i2.Uint8List>); + @override + _i5.Future writeCharacteristicForIdentifier( + _i3.Peripheral? peripheral, + _i6.InternalCharacteristic? characteristic, + _i2.Uint8List? value, + bool? withResponse, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeCharacteristicForIdentifier, + [peripheral, characteristic, value, withResponse, transactionId]), + returnValue: Future.value(null), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override + _i5.Stream<_i2.Uint8List> monitorCharacteristicForIdentifier( + _i3.Peripheral? peripheral, + _i6.InternalCharacteristic? characteristic, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#monitorCharacteristicForIdentifier, + [peripheral, characteristic, transactionId]), + returnValue: Stream<_i2.Uint8List>.empty()) + as _i5.Stream<_i2.Uint8List>); + @override + _i5.Future> descriptorsForCharacteristic( + _i3.Characteristic? characteristic) => + (super.noSuchMethod( + Invocation.method(#descriptorsForCharacteristic, [characteristic]), + returnValue: + Future.value(<_i3.Descriptor>[])) as _i5 + .Future>); + @override + _i5.Future<_i3.DescriptorWithValue> readDescriptorForCharacteristic( + _i3.Characteristic? characteristic, + String? descriptorUuid, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#readDescriptorForCharacteristic, + [characteristic, descriptorUuid, transactionId]), + returnValue: Future.value(_FakeDescriptorWithValue())) + as _i5.Future<_i3.DescriptorWithValue>); + @override + _i5.Future<_i3.Descriptor> writeDescriptorForCharacteristic( + _i3.Characteristic? characteristic, + String? descriptorUuid, + _i2.Uint8List? value, + String? transactionId) => + (super.noSuchMethod( + Invocation.method(#writeDescriptorForCharacteristic, + [characteristic, descriptorUuid, value, transactionId]), + returnValue: Future.value(_FakeDescriptor())) + as _i5.Future<_i3.Descriptor>); +} diff --git a/test/test_util/descriptor_generator.dart b/test/test_util/descriptor_generator.dart index f836854c..6616fdb6 100644 --- a/test/test_util/descriptor_generator.dart +++ b/test/test_util/descriptor_generator.dart @@ -8,7 +8,7 @@ class DescriptorGenerator { DescriptorGenerator(this.managerForDescriptor); - Map _createRawDescriptor(int seed) => { + Map _createRawDescriptor(int seed) => { "descriptorId": seed, "descriptorUuid": seed.toString(), "value": base64Encode([seed])