diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2ff46c0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,80 @@ +# Dependabot configuration file. +# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates +version: 2 + +enable-beta-ecosystems: true +updates: + - package-ecosystem: "pub" + directory: "bluetooth" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "bluetooth_bluez" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "bluetooth_flutter" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "bluetooth_flutter/example" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "bluetooth_flutter_blue" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "bluetooth_flutter_blue/example" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "bluetooth_server" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "bluetooth_server_app" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "bluetooth_server_flutter" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "bluetooth_test" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "bluetooth_test_app" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "bluetooth_web" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "example/java_objc_app" + schedule: + interval: "monthly" + + - package-ecosystem: "pub" + directory: "repo_support" + schedule: + interval: "monthly" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/.github/workflows/run_ci.yml b/.github/workflows/run_ci.yml index 983156c..152b4db 100644 --- a/.github/workflows/run_ci.yml +++ b/.github/workflows/run_ci.yml @@ -18,9 +18,10 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] flutter: [stable, beta] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 with: + distribution: 'zulu' java-version: '12.x' - uses: subosito/flutter-action@v2 with: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index fc04bd8..0000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: dart -sudo: required -dart: - - stable - - beta - - dev -before_script: - - cd repo_support - - dart pub get - - dart pub run tekartik_travis_ci_flutter:install - - source $(dart pub run tekartik_travis_ci_flutter:env) -script: - - dart tool/run_ci.dart -matrix: - allow_failures: - - dart: dev \ No newline at end of file diff --git a/bluetooth/README.md b/bluetooth/README.md index be521df..3d7b1c9 100644 --- a/bluetooth/README.md +++ b/bluetooth/README.md @@ -10,6 +10,6 @@ dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth - ref: dart2_3 - version: '>=0.2.1' + ref: dart3a + version: '>=0.6.0' ``` \ No newline at end of file diff --git a/bluetooth/analysis_options.yaml b/bluetooth/analysis_options.yaml index 1624d5c..e930a89 100644 --- a/bluetooth/analysis_options.yaml +++ b/bluetooth/analysis_options.yaml @@ -10,8 +10,7 @@ linter: rules: - cancel_subscriptions - hash_and_equals - - iterable_contains_unrelated_type - - list_remove_unrelated_type + - collection_methods_unrelated_type - test_types_in_equals - unrelated_type_equality_checks - valid_regexps \ No newline at end of file diff --git a/bluetooth/lib/bluetooth_mock.dart b/bluetooth/lib/bluetooth_mock.dart new file mode 100644 index 0000000..c94b187 --- /dev/null +++ b/bluetooth/lib/bluetooth_mock.dart @@ -0,0 +1,6 @@ +export 'package:tekartik_bluetooth/src/mock/admin_manager_mock.dart' + show BluetoothAdminManagerMock; +export 'package:tekartik_bluetooth/src/mock/manager_mock.dart' + show BluetoothManagerMock; + +export 'src/mock/peripheral_mock.dart' show BluetoothPeripheralMock; diff --git a/bluetooth/lib/bluetooth_service.dart b/bluetooth/lib/bluetooth_service.dart index 2b6f269..d972c4c 100644 --- a/bluetooth/lib/bluetooth_service.dart +++ b/bluetooth/lib/bluetooth_service.dart @@ -1,5 +1,13 @@ import 'package:rxdart/rxdart.dart'; import 'package:tekartik_common_utils/common_utils_import.dart'; +export 'src/platform_channel.dart' + show + EventChannel, + EventChannelMixin, + MethodCall, + MethodCallMixin, + MethodChannel, + MethodChannelMixin; /// An command object representing the invocation of a named method. @immutable diff --git a/bluetooth/lib/bluetooth_state_service.dart b/bluetooth/lib/bluetooth_state_service.dart index b03f07f..7fdd095 100644 --- a/bluetooth/lib/bluetooth_state_service.dart +++ b/bluetooth/lib/bluetooth_state_service.dart @@ -8,5 +8,6 @@ abstract class BluetoothStateService { {@Deprecated('Use androidRequestCode') int? requestCode, int? androidRequestCode}); + /// No longer working on Android SDK33 Future disable(); } diff --git a/bluetooth/lib/src/bluetooth_device.dart b/bluetooth/lib/src/bluetooth_device.dart index a426099..e1d1493 100644 --- a/bluetooth/lib/src/bluetooth_device.dart +++ b/bluetooth/lib/src/bluetooth_device.dart @@ -1,6 +1,6 @@ +import 'package:cv/cv.dart'; import 'package:tekartik_bluetooth/src/common/device_mixin.dart'; import 'package:tekartik_bluetooth/src/device_id.dart'; -import 'package:tekartik_common_utils/model/model_v2.dart'; /// Bluetooth device type. enum BluetoothDeviceType { unknown, classic, le, dual } diff --git a/bluetooth/lib/src/bluetooth_peripheral.dart b/bluetooth/lib/src/bluetooth_peripheral.dart index 96e008c..9508ee3 100644 --- a/bluetooth/lib/src/bluetooth_peripheral.dart +++ b/bluetooth/lib/src/bluetooth_peripheral.dart @@ -1,5 +1,7 @@ import 'dart:typed_data'; +import 'package:collection/collection.dart'; +import 'package:cv/cv.dart'; import 'package:tekartik_bluetooth/ble.dart'; import 'package:tekartik_bluetooth/bluetooth_service.dart'; import 'package:tekartik_bluetooth/uuid.dart'; @@ -238,6 +240,8 @@ class BluetoothPeripheralWriteCharacteristicEvent { Uuid128? characteristicUuid; Uint8List? value; + BluetoothPeripheralWriteCharacteristicEvent( + {this.serviceUuid, this.characteristicUuid, this.value}); void fromMap(Map map) { serviceUuid = Uuid128(map['service'].toString()); characteristicUuid = Uuid128(map['characteristic'].toString()); @@ -247,6 +251,27 @@ class BluetoothPeripheralWriteCharacteristicEvent { @override String toString() => '$serviceUuid $characteristicUuid ${value != null ? toHexString(value) : null}'; + + Model toMap() { + return Model.from({ + 'serviceUuid': serviceUuid?.toString(), + 'characteristicUuid': characteristicUuid?.toString(), + 'value': value != null ? toHexString(value) : null + }); + } + + @override + int get hashCode => toString().hashCode; + + @override + bool operator ==(Object other) { + if (other is BluetoothPeripheralWriteCharacteristicEvent) { + return other.serviceUuid == serviceUuid && + other.characteristicUuid == characteristicUuid && + const ListEquality().equals(other.value, value); + } + return false; + } } class BluetoothPeripheral { @@ -311,7 +336,7 @@ class BluetoothPeripheral { Future setCharacteristicValue( {required Uuid128 serviceUuid, - required Uuid128? characteristicUuid, + required Uuid128 characteristicUuid, required Uint8List? value}) async { await _bluetoothFlutterPlugin!.methodChannel .invokeMethod('peripheralSetCharacteristicValue', { @@ -323,7 +348,7 @@ class BluetoothPeripheral { Future notifyCharacteristicValue( {required Uuid128 serviceUuid, - required Uuid128? characteristicUuid, + required Uuid128 characteristicUuid, Uint8List? value}) async { await _bluetoothFlutterPlugin!.methodChannel .invokeMethod('peripheralNotifyCharacteristicValue', { @@ -333,9 +358,23 @@ class BluetoothPeripheral { }); } + Future setAndNotifyCharacteristicValue( + {required Uuid128 serviceUuid, + required Uuid128 characteristicUuid, + required Uint8List? value}) async { + await setCharacteristicValue( + serviceUuid: serviceUuid, + characteristicUuid: characteristicUuid, + value: value); + await notifyCharacteristicValue( + serviceUuid: serviceUuid, + characteristicUuid: characteristicUuid, + value: value); + } + Future getCharacteristicValue({ required Uuid128 serviceUuid, - required Uuid128? characteristicUuid, + required Uuid128 characteristicUuid, }) async { var bytes = (await _bluetoothFlutterPlugin!.methodChannel .invokeMethod('peripheralGetCharacteristicValue', { diff --git a/bluetooth/lib/src/common/mixin_model.dart b/bluetooth/lib/src/common/mixin_model.dart index 765737d..79cf6b6 100644 --- a/bluetooth/lib/src/common/mixin_model.dart +++ b/bluetooth/lib/src/common/mixin_model.dart @@ -1,4 +1,4 @@ -import 'package:tekartik_common_utils/model/model_v2.dart'; +import 'package:cv/cv.dart'; const methodStartScan = 'startScan'; const methodStopScan = 'stopScan'; diff --git a/bluetooth/lib/src/common/platform_mixin.dart b/bluetooth/lib/src/common/platform_mixin.dart new file mode 100644 index 0000000..03c1c93 --- /dev/null +++ b/bluetooth/lib/src/common/platform_mixin.dart @@ -0,0 +1,10 @@ +import 'dart:io'; + +import 'package:tekartik_bluetooth/src/import.dart'; + +mixin BluetoothManagerPlatformCompatMixin { + /// Need for all implementation for now + bool? get isAndroid => isRunningAsJavascript ? false : Platform.isAndroid; + + bool? get isIOS => isRunningAsJavascript ? false : Platform.isIOS; +} diff --git a/bluetooth/lib/src/device_connection.dart b/bluetooth/lib/src/device_connection.dart index 7f3e86d..09a925d 100644 --- a/bluetooth/lib/src/device_connection.dart +++ b/bluetooth/lib/src/device_connection.dart @@ -26,10 +26,14 @@ class BluetoothDeviceConnectionState { static const disconnected = BluetoothDeviceConnectionState( bluetoothDeviceConnectionStateDisconnected); + + /// No longer supported on Android and iOS static const connecting = BluetoothDeviceConnectionState(bluetoothDeviceConnectionStateConnecting); static const connected = BluetoothDeviceConnectionState(bluetoothDeviceConnectionStateConnected); + + /// No longer supported on Android and iOS static const disconnecting = BluetoothDeviceConnectionState( bluetoothDeviceConnectionStateDisconnecting); static const unknown = diff --git a/bluetooth/lib/src/device_id.dart b/bluetooth/lib/src/device_id.dart index 9f1cbdb..c8ea2f7 100644 --- a/bluetooth/lib/src/device_id.dart +++ b/bluetooth/lib/src/device_id.dart @@ -7,7 +7,7 @@ abstract class BluetoothDeviceId { factory BluetoothDeviceId(String id) => BluetoothDeviceIdImpl(id); } -abstract class BluetoothDeviceIdMixin implements BluetoothDeviceId { +abstract mixin class BluetoothDeviceIdMixin implements BluetoothDeviceId { @override int get hashCode => id.toLowerCase().hashCode; diff --git a/bluetooth/lib/src/info.dart b/bluetooth/lib/src/info.dart index a05a24f..390099a 100644 --- a/bluetooth/lib/src/info.dart +++ b/bluetooth/lib/src/info.dart @@ -1,5 +1,5 @@ +import 'package:cv/cv.dart'; import 'package:tekartik_common_utils/bool_utils.dart'; -import 'package:tekartik_common_utils/model/model_v2.dart'; abstract class BluetoothInfo { bool? get hasBluetooth; @@ -34,7 +34,10 @@ class BluetoothInfoImpl implements BluetoothInfo { bool? isScanning; BluetoothInfoImpl( - {this.hasBluetooth, this.hasBluetoothBle, this.isBluetoothEnabled}); + {this.hasBluetooth, + this.hasBluetoothBle, + this.isBluetoothEnabled, + this.isScanning}); void fromMap(Map result) { var model = asModel(result); @@ -87,4 +90,24 @@ class BluetoothAdminInfoImpl implements BluetoothAdminInfo { ..setValue('isBluetoothEnabled', isBluetoothEnabled); return model; } + + @override + int get hashCode => toString().hashCode; + + @override + bool operator ==(Object other) { + if (other is BluetoothAdminInfoImpl) { + if (other.hasBluetooth != hasBluetooth) { + return false; + } + if (other.hasBluetoothBle != hasBluetoothBle) { + return false; + } + if (other.isBluetoothEnabled != isBluetoothEnabled) { + return false; + } + return true; + } + return false; + } } diff --git a/bluetooth/lib/src/mock/admin_manager_mock.dart b/bluetooth/lib/src/mock/admin_manager_mock.dart new file mode 100644 index 0000000..fecc99e --- /dev/null +++ b/bluetooth/lib/src/mock/admin_manager_mock.dart @@ -0,0 +1,40 @@ +import 'package:tekartik_bluetooth/bluetooth.dart'; +import 'package:tekartik_bluetooth/src/common/platform_mixin.dart'; +import 'package:tekartik_bluetooth/src/import.dart'; +import 'package:tekartik_bluetooth/src/mixin.dart'; + +class BluetoothAdminManagerMock + with BluetoothAdminManagerMixin, BluetoothManagerPlatformCompatMixin + implements BluetoothAdminManager { + @override + Future checkCoarseLocationPermission({int? androidRequestCode}) async { + return true; + } + + @override + Future checkBluetoothPermissions( + {int? androidRequestCode, BluetoothPermissionsOptions? options}) async { + return true; + } + + @override + Future disable() async {} + + @override + Future enable({int? requestCode, int? androidRequestCode}) async {} + + @override + Future invokeMethod(String method, [Object? arguments]) { + // TODO: implement invokeMethod + throw UnimplementedError(); + } + + @override + bool? get supportsEnable => false; + + @override + Future getAdminInfo() async { + return BluetoothAdminInfoImpl( + hasBluetooth: true, hasBluetoothBle: true, isBluetoothEnabled: true); + } +} diff --git a/bluetooth/lib/src/mock/battery_device.dart b/bluetooth/lib/src/mock/battery_device.dart index f1fc60c..db14a01 100644 --- a/bluetooth/lib/src/mock/battery_device.dart +++ b/bluetooth/lib/src/mock/battery_device.dart @@ -10,21 +10,21 @@ import 'package:tekartik_bluetooth/utils/byte_utils.dart'; import 'package:tekartik_bluetooth/uuid.dart'; import 'package:tekartik_common_utils/common_utils_import.dart'; -const int lumiServiceNumber = 0xF011; -const int lumiServiceVersionNumber = 0x0001; -final Uuid128 lumiServiceVersionCharacteristicUuid128 = - lumiServiceUuid128.withUuid16(lumiServiceVersionCharacteristicUuid16); - -// Lumi service - generated -final Uuid128 lumiServiceUuid128 = Uuid128( - '0000${uint16GetString(lumiServiceNumber)}-87ae-41fe-b826-6ad4069efaff'); -final Uuid16 lumiServiceVersionCharacteristicUuid16 = - Uuid16.fromValue(lumiServiceVersionNumber); +const int batteryServiceNumber = 0xF011; +const int batteryServiceVersionNumber = 0x0001; +final Uuid128 batteryServiceVersionCharacteristicUuid128 = + demoServiceUuid128.withUuid16(batteryServiceVersionCharacteristicUuid16); + +// Demo service - generated +final Uuid128 demoServiceUuid128 = Uuid128( + '0000${uint16GetString(batteryServiceNumber)}-87ae-41fe-b826-6ad4069efaff'); +final Uuid16 batteryServiceVersionCharacteristicUuid16 = + Uuid16.fromValue(batteryServiceVersionNumber); // final Uuid16 _invalidServiceUuid16 = Uuid16('ffff'); -final Uuid32 androidLumiDevicesManagerUuid32 = Uuid32('ffffffff'); +final Uuid32 androidDemoDevicesManagerUuid32 = Uuid32('ffffffff'); -final Uuid128 lumiServicePingCharacteristicUuid128 = - lumiServiceUuid128.withUuid16(lumiServicePingCharacteristicUuid16); +final Uuid128 demoServicePingCharacteristicUuid128 = + demoServiceUuid128.withUuid16(demoServicePingCharacteristicUuid16); class BatteryRemoteDevice { BluetoothPeripheral? bluetoothPeripheral; @@ -47,23 +47,9 @@ class BatteryRemoteDevice { BatteryRemoteDevice({this.bluetoothPeripheral}); - static final String deviceIdKey = 'lumiPeripheralDeviceId'; - static final String batteryKey = 'lumiPeripheralBattery'; // int + static final String deviceIdKey = 'peripheralDeviceId'; + static final String batteryKey = 'peripheralBattery'; // int - /* - String initId(Prefs prefs) { - var deviceIdText = prefs?.getString(LumiPeripheral.deviceIdKey); - if (deviceIdText == null) { - var sb = StringBuffer(); - for (int i = 0; i < 4; i++) { - sb.write(Random().nextInt(10).toString()); - } - deviceIdText = sb.toString(); - prefs?.setString(LumiPeripheral.deviceIdKey, deviceIdText); - } - return deviceIdText; - } - */ bool hasCharacteristic(Uuid128 uuid) { for (var service in gattServices) { for (var bs in service.characteristics) { @@ -77,16 +63,16 @@ class BatteryRemoteDevice { List gattServices = [ BluetoothGattService( - uuid: lumiServiceUuid128, + uuid: demoServiceUuid128, characteristics: [ BluetoothGattCharacteristic( - uuid: lumiServiceVersionCharacteristicUuid128, + uuid: batteryServiceVersionCharacteristicUuid128, properties: BluetoothGattCharacteristic.propertyRead, permissions: BluetoothGattCharacteristic.permissionRead, description: 'Version'), BluetoothGattCharacteristic( - uuid: lumiServiceUuid128 - .withUuid16(lumiServicePingCharacteristicUuid16), + uuid: demoServiceUuid128 + .withUuid16(demoServicePingCharacteristicUuid16), properties: BluetoothGattCharacteristic.propertyWrite, permissions: BluetoothGattCharacteristic.permissionWrite, description: 'Ping'), @@ -103,195 +89,6 @@ class BatteryRemoteDevice { ]) ]; - /* - Future init(Prefs prefs) async { - /// An id is generate and saved in prefs - var deviceIdText = initId(prefs); - - BluetoothPeripheral bluetoothPeripheral; - if ((await getDeviceInfo()).isPhysicalDevice) { - try { - bluetoothPeripheral = await BluetoothFlutter.initPeripheral( - services: gattServices, - deviceName: "${LumiPeripheral.namePrefix}${deviceIdText}"); - } catch (e) { - print(e); - } - } - this.bluetoothPeripheral = bluetoothPeripheral; - - // Init listeners - initListeners(); - - // Handle battery - unawaited(() async { - await for (var battery in batteryStream) { - await setAndNotifyCharacteristicValue( - bleBatteryServiceLevelCharacteristic.withValue( - BleBatteryServiceLevel(value: battery.round()).data)); - } - }()); - - if (bluetoothPeripheral != null) { - // Handle peripheral write - unawaited(() async { - await for (var writeEvent - in bluetoothPeripheral.onWriteCharacteristic()) { - writeCharacteristicEvent.sink.add(writeEvent); - } - }()); - } - - await initValues(prefs); - } - - // Init listeners - void initListeners() { - // handle writes - unawaited(() async { - await for (var writeEvent in writeCharacteristicEvent.stream) { - unawaited(() async { - // Lumi service? - if (writeEvent.serviceUuid == lumiServiceUuid128) { - // Ping - var characteristicNumber = - writeEvent.characteristicUuid.shortNumberUuid16.value; - switch (characteristicNumber) { - case lumiServicePingNumber: - { - // We response right await to a ping event - await setAndNotifyCharacteristicValue( - bleLumiServiceLastActionResultCharacteristic.withValue( - BleLumiServiceLastActionResult( - actionId: characteristicNumber, - result: lumiServiceResultNoError) - .data)); - break; - } - case lumiServiceStandbyDurationNumber: - { - if (hasCharacteristic( - lumiServiceStandbyDurationCharacteristicUuid128)) { - /* - // We response right await to a ping event - await setAndNotifyCharacteristicValue( - bleLumiServiceLastActionResultCharacteristic.withValue( - BleLumiServiceLastActionResult( - actionId: characteristicNumber, - result: lumiServiceResultNoError) - .data)); - - */ - // No response in 0.4.0 - print('received stand by duration: ${writeEvent}'); - } - break; - } - case lumiServiceMeasureStartNumber: - { - // Check whether we are already measuring - var state = await getState(); - //TODO handle when calibrating or other state (battery, not ready...) - if (state.measuring) { - // Already measuring - await setAndNotifyCharacteristicValue( - bleLumiServiceLastActionResultCharacteristic.withValue( - BleLumiServiceLastActionResult( - actionId: characteristicNumber, - result: lumiServiceResultInvalidStateError) - .data)); - return; - } - - // Clear the measurement value and notify - await setAndNotifyCharacteristicValue( - bleLumiServiceMeasurementValueCharacteristic - .withValue(null)); - - // Send and notify the state - await updateState(measuring: true); - - var param = - BleLumiServiceMeasurementStart.fromData(writeEvent.value); - - // Send the response - await setAndNotifyCharacteristicValue( - bleLumiServiceLastActionResultCharacteristic.withValue( - BleLumiServiceLastActionResult( - actionId: characteristicNumber, - result: lumiServiceResultNoError) - .data)); - - await sleep(param.step1Duration); - if (!(await getState()).measuring) { - print('measurement cancelled'); - break; - } - //devPrint('--1'); - // await sleep(param.step2Duration); - // await Future.delayed(Duration(milliseconds: step2Duration)); - await sleep(param.step2Duration); - if (!(await getState()).measuring) { - print('measurement cancelled'); - break; - } - //devPrint('--2'); - - // Send the measurement value first - var measurementValue = BleLumiServiceMeasurementValue( - value: 123456000 + Random().nextInt(1000)) - .data; - // Set and notify - await setAndNotifyCharacteristicValue( - bleLumiServiceMeasurementValueCharacteristic - .withValue(measurementValue)); - - // Send the state - await updateState(measuring: false); - - break; - } - case lumiServiceMeasureStopNumber: - { - // Check whether we are already measuring - var state = await getState(); - if (!state.measuring) { - // Already measuring - await setAndNotifyCharacteristicValue( - bleLumiServiceLastActionResultCharacteristic.withValue( - BleLumiServiceLastActionResult( - actionId: characteristicNumber, - result: lumiServiceResultInvalidStateError) - .data)); - return; - } - - // Clear the measurement value and notify - await setAndNotifyCharacteristicValue( - bleLumiServiceMeasurementValueCharacteristic - .withValue(null)); - - // Send and notify the state - await updateState(measuring: false); - - // Send the response - await setAndNotifyCharacteristicValue( - bleLumiServiceLastActionResultCharacteristic.withValue( - BleLumiServiceLastActionResult( - actionId: characteristicNumber, - result: lumiServiceResultNoError) - .data)); - break; - } - } - } - }()); - } - }()); - } - - */ - Future setCharacteristicValue(BleBluetoothCharacteristicValue bcv) async { await bluetoothPeripheral!.setCharacteristicValue( serviceUuid: bcv.service.uuid, @@ -319,128 +116,6 @@ class BatteryRemoteDevice { return BleBluetoothCharacteristicValue(bc: bc, value: value); } - /* - Future initValues(Prefs prefs) async { - // Initialize some battery value - int battery = - prefs?.getInt(LumiPeripheral.batteryKey) ?? 50; // quick test with 50 - batterySink.add(battery); - - powerStateSink.add(PowerState.off); - coverStateSink.add(CoverState.closed); - tubeStateSink.add(TubeState.notPresent); - - // Default value - // From prefs? - int version = 1; - - // Version - await setCharacteristicValue(BleBluetoothCharacteristicValue( - service: bleLumiService, - uuid: lumiServiceVersionCharacteristicUuid128, - value: BleLumiServiceVersion(version: version).data)); - - // State - - // Send notification - unawaited(() async { - await for (var bleNotification in bleNotification.stream) { - await notifyCharacteristicValue(bleNotification); - } - }()); - /* - unawaited(() async { - // Handle UI changed - await for (var state in _inStateSubject.stream) { - var bcv = BleBluetoothCharacteristicValue( - service: bleLumiService, - uuid: lumiServiceStateCharacteristicUuid128, - value: state.data); - - if (notifyState) { - await setAndNotifyCharacteristicValue(bcv); - } else { - await setCharacteristicValue(bcv); - } - } - }()); - - // Handle client changes - unawaited(() async { - await for (var state in _inStateSubject.stream) { - var bcv = BleBluetoothCharacteristicValue( - service: bleLumiService, - uuid: lumiServiceStateCharacteristicUuid128, - value: state.data); - if (notifyState) { - await setAndNotifyCharacteristicValue(bcv); - } else { - await setCharacteristicValue(bcv); - } - } - }()); - */ - - void checkCalibration() { - if (needCalibrationStateSubject.value == - NeedCalibrationState.needCalibration && - calibratingStateSubject.value != CalibratingState.calibrating && - _tubeStateSubject.value != TubeState.present && - _coverStateSubject.value != CoverState.opened) { - () async { - await updateState(calibrating: true); - await sleep(2000); - needCalibrationStateSubject - .add(NeedCalibrationState.notNeedCalibration); - await updateState(calibrating: false); - }(); - } - } - - // From UI - unawaited(() async { - await for (var tubeState in tubeStateStream) { - // Changed? - bool tubePresent = tubeState == TubeState.present; - await updateState(tubePresent: tubePresent); - checkCalibration(); - } - }()); - - unawaited(() async { - await for (var coverState in coverStateStream) { - // Changed? - bool coverOpened = coverState == CoverState.opened; - await updateState(coverOpened: coverOpened); - checkCalibration(); - } - }()); - - unawaited(() async { - await for (var needCalibrationState - in needCalibrationStateSubject.distinct()) { - // Changed? - bool needCalibration = - needCalibrationState == NeedCalibrationState.needCalibration; - await updateState(needCalibration: needCalibration); - checkCalibration(); - } - }()); - - // initial state - await updateState(); - } - - //TODO call it? - void dispose() { - //_state.close(); - unawaited(_stateSubject.close()); - } - - // Prefs? - bool notifyBattery = true; - bool notifyState = true; - */ Future start() async { var advertiseData = AdvertiseData(services: [ // We show 2 services @@ -454,35 +129,9 @@ class BatteryRemoteDevice { await bluetoothPeripheral!.stopAdvertising(); } - /* - final _powerStateSubject = BehaviorSubject(); - - Stream get powerStateStream => _powerStateSubject.distinct(); - - StreamSink get powerStateSink => _powerStateSubject; - - final _tubeStateSubject = BehaviorSubject(); - - Stream get tubeStateStream => _tubeStateSubject.distinct(); - - StreamSink get tubeStateSink => _tubeStateSubject; - - final _coverStateSubject = BehaviorSubject(); - - Stream get coverStateStream => _coverStateSubject.distinct(); - - StreamSink get coverStateSink => _coverStateSubject; - */ final _batterySubject = BehaviorSubject(); Stream get batteryStream => _batterySubject.distinct(); StreamSink get batterySink => _batterySubject; -/* - final calibratingStateSubject = BehaviorSubject(); - final needCalibrationStateSubject = - BehaviorSubject.seeded( - NeedCalibrationState.notNeedCalibration); - - */ } diff --git a/bluetooth/lib/src/mock/battery_device_mock.dart b/bluetooth/lib/src/mock/battery_device_mock.dart index 6d3d118..876e094 100644 --- a/bluetooth/lib/src/mock/battery_device_mock.dart +++ b/bluetooth/lib/src/mock/battery_device_mock.dart @@ -25,19 +25,6 @@ class BatteryRemoteDeviceMock extends BatteryRemoteDevice { BluetoothPeripheralMock(deviceName: 'Battery', services: gattServices); } - //static var deviceId = const DeviceIdentifier('noveo_lumi_android'); - - /* - @override - Future init(Prefs prefs) async { - /// An id is generate and saved in prefs - // var deviceIdText = - initId(prefs); - - await initValues(prefs); - } - */ - final characteristicValueMap = {}; diff --git a/bluetooth/lib/src/mock/device_connection_mock.dart b/bluetooth/lib/src/mock/device_connection_mock.dart new file mode 100644 index 0000000..eedf0ef --- /dev/null +++ b/bluetooth/lib/src/mock/device_connection_mock.dart @@ -0,0 +1,145 @@ +import 'package:rxdart/rxdart.dart'; +import 'package:tekartik_bluetooth/bluetooth_device.dart'; +import 'package:tekartik_bluetooth/bluetooth_peripheral.dart'; +import 'package:tekartik_bluetooth/src/ble.dart'; +import 'package:tekartik_bluetooth/src/mock/manager_mock.dart'; +import 'package:tekartik_bluetooth/src/mock/peripheral_mock.dart'; +import 'package:tekartik_bluetooth/src/rx_utils.dart'; + +import '../../uuid.dart'; + +class BluetoothDeviceConnectionMock implements BluetoothDeviceConnection { + final BluetoothManagerMock manager; + + /// Where to post notification to be sent + final _bleNotificationWrapper = + PublishSubjectWrapper(); + + SubjectInterface get bleNotification => + _bleNotificationWrapper; + final _bleConnectionStateWrapper = + BehaviorSubject.seeded( + BluetoothDeviceConnectionState.disconnected); + + BluetoothDeviceConnectionMock({required this.manager}); + @override + void close() { + disconnect(); + } + + @override + Future connect() async { + _bleConnectionStateWrapper.sink + .add(BluetoothDeviceConnectionState.connected); + } + + @override + Future disconnect() async { + _bleConnectionStateWrapper.sink + .add(BluetoothDeviceConnectionState.disconnected); + } + + @override + Future discoverServices() async {} + + BluetoothPeripheralMock get peripheral => manager.peripheral!; + + BleBluetoothService serviceFromPeripheralService( + BluetoothGattService gattService) { + var service = BleBluetoothServiceImpl(uuid: gattService.uuid); + var characteristics = gattService.characteristics.map((e) { + var characteristic = characteristicFromGattCharacteristic(service, e); + return characteristic; + }); + // ignore: invalid_use_of_protected_member + return service..characteristics = characteristics.toList(); + } + + BleBluetoothCharacteristic characteristicFromGattCharacteristic( + BleBluetoothService service, + BluetoothGattCharacteristic gattCharacteristic) { + var characteristic = BleBluetoothCharacteristicImpl( + service: service, + uuid: gattCharacteristic.uuid, + properties: gattCharacteristic.properties); + return characteristic; + } + + @override + Future> getServices() async { + var services = manager.peripheral!.services!; + return services.map(serviceFromPeripheralService).toList(); + } + + @override + Stream onCharacteristicValueChanged( + BleBluetoothCharacteristic characteristic) { + return _bleNotificationWrapper.stream.where((event) => + event.service.uuid == characteristic.service.uuid && + event.uuid == characteristic.uuid); + } + + @override + // TODO: implement onConnectionState + Stream get onConnectionState => + _bleConnectionStateWrapper.stream; + + BleBluetoothService getService(Uuid128 uuid) { + var gattServices = manager.peripheral!.services!; + var service = BleBluetoothServiceImpl(uuid: uuid); + for (var peripheralService in gattServices) { + if (peripheralService.uuid == uuid) { + return serviceFromPeripheralService(peripheralService); + } + } + return service; + } + + BleBluetoothCharacteristic getCharacteristic( + Uuid128 serviceUuid, Uuid128 uuid) { + var service = getService(serviceUuid); + + for (var characteristic in service.characteristics) { + if (characteristic.uuid == uuid) { + return characteristic; + } + } + throw ArgumentError('characteristic not found $uuid'); + } + + @override + Future readCharacteristic( + BleBluetoothCharacteristic characteristic) async { + var service = getService(characteristic.service.uuid); + var bleCharacteristic = + getCharacteristic(characteristic.service.uuid, characteristic.uuid); + var value = await peripheral.getCharacteristicValue( + serviceUuid: characteristic.service.uuid, + characteristicUuid: characteristic.uuid); + return BleBluetoothCharacteristicValue( + service: service, value: value, bc: bleCharacteristic); + } + + @override + Future registerCharacteristic( + BleBluetoothCharacteristic characteristic, bool on) async { + peripheral.bleNotification.stream.listen((event) { + if (event.serviceUuid == characteristic.service.uuid && + event.characteristicUuid == characteristic.uuid) { + _bleNotificationWrapper.sink.add(BleBluetoothCharacteristicValue( + service: BleBluetoothService(uuid: event.serviceUuid), + value: event.value!, + uuid: event.characteristicUuid)); + } + }); + } + + @override + Future writeCharacteristic( + BleBluetoothCharacteristicValue characteristicValue) async { + await peripheral.writeCharacteristicValue( + serviceUuid: characteristicValue.service.uuid, + characteristicUuid: characteristicValue.uuid, + value: characteristicValue.value); + } +} diff --git a/bluetooth/lib/src/mock/manager_mock.dart b/bluetooth/lib/src/mock/manager_mock.dart new file mode 100644 index 0000000..2569a50 --- /dev/null +++ b/bluetooth/lib/src/mock/manager_mock.dart @@ -0,0 +1,81 @@ +import 'package:tekartik_bluetooth/bluetooth_device.dart'; +import 'package:tekartik_bluetooth/src/bluetooth_device.dart'; +import 'package:tekartik_bluetooth/src/common/platform_mixin.dart'; +import 'package:tekartik_bluetooth/src/device_id.dart'; +import 'package:tekartik_bluetooth/src/import.dart'; +import 'package:tekartik_bluetooth/src/mixin.dart'; +import 'package:tekartik_bluetooth/src/mock/device_connection_mock.dart'; +import 'package:tekartik_bluetooth/src/mock/peripheral_mock.dart'; +import 'package:tekartik_bluetooth/src/uuid.dart'; + +import 'scan_result_mock.dart'; + +class BluetoothManagerMock + with BluetoothManagerMixin, BluetoothManagerPlatformCompatMixin + implements BluetoothManager { + final BluetoothPeripheralMock? peripheral; + List connectedDevices = []; + bool isScanning = false; + + BluetoothManagerMock({required this.peripheral}); + @override + Future close() async {} + + @override + Future disable() async {} + + @override + Future enable({int? requestCode, int? androidRequestCode}) async {} + + @override + Future> getConnectedDevices() async => connectedDevices; + + @override + Future getInfo() async { + return BluetoothInfoImpl( + hasBluetooth: true, + hasBluetoothBle: true, + isBluetoothEnabled: true, + isScanning: isScanning); + } + + @override + Future init() async {} + + @override + Future newConnection( + BluetoothDeviceId deviceId) async { + return BluetoothDeviceConnectionMock(manager: this); + } + + @override + Stream scan( + {ScanMode scanMode = ScanMode.lowLatency, + List? withServices}) async* { + try { + isScanning = true; + var peripheral = this.peripheral; + if (peripheral != null) { + var device = BluetoothDeviceImpl(); + device.id = BluetoothDeviceIdImpl('mock'); + device.name = 'Mock'; + device.address = 'mock_address'; + yield ScanResultMock(device: device, rssi: 50); + } + } catch (_) { + isScanning = false; + } + } + + @override + Future stop() async { + isScanning = false; + } + + @override + bool? get supportsEnable => false; + + @override + Future invokeMethod(String method, [Object? arguments]) => + throw UnsupportedError('invokeMethod($method)'); +} diff --git a/bluetooth/lib/src/mock/peripheral_mock.dart b/bluetooth/lib/src/mock/peripheral_mock.dart index 4a0c8e9..200bf64 100644 --- a/bluetooth/lib/src/mock/peripheral_mock.dart +++ b/bluetooth/lib/src/mock/peripheral_mock.dart @@ -1,15 +1,68 @@ +import 'dart:typed_data'; + import 'package:tekartik_bluetooth/bluetooth_peripheral.dart'; +import 'package:tekartik_bluetooth/src/rx_utils.dart'; +import 'package:tekartik_bluetooth/uuid.dart'; import 'package:tekartik_common_utils/common_utils_import.dart'; +class BluetoothPeripheralCharacteristicMock { + final Uint8List value; + + BluetoothPeripheralCharacteristicMock({required this.value}); +} + +class BluetoothPeripheralServiceMock { + final characteristicsMap = {}; +} + +class BluetoothPeripheralNotificationMock { + final Uuid128 serviceUuid; + final Uuid128 characteristicUuid; + final Uint8List? value; + + BluetoothPeripheralNotificationMock( + {required this.serviceUuid, + required this.characteristicUuid, + required this.value}); +} + class BluetoothPeripheralMock extends BluetoothPeripheral { + // publish here we don't replay like behavior + final _writeCharacteristicEvent = + PublishSubjectWrapper(); + + SubjectInterface + get writeCharacteristicEvent => _writeCharacteristicEvent; + + /// Where to post notification to be sent + final _bleNotificationWrapper = + PublishSubjectWrapper(); + + SubjectInterface get bleNotification => + _bleNotificationWrapper; BluetoothPeripheralMock( - {BluetoothPeripheralPlugin? plugin, // Needed? + { // Needed? String? deviceName, List? services}) - : super(plugin: plugin, deviceName: deviceName, services: services); + : super(plugin: null, deviceName: deviceName, services: services); /// Current advertiseData AdvertiseData? advertiseData; + final servicesMap = {}; + + BluetoothPeripheralServiceMock getBluetoothPeripheralServiceMock( + Uuid128 serviceUuid, + {bool createIfMissing = false}) { + var service = servicesMap[serviceUuid]; + if (service == null) { + if (createIfMissing) { + servicesMap[serviceUuid] = service = BluetoothPeripheralServiceMock(); + } else { + throw 'service not found $serviceUuid'; + } + } + return service; + } @override Future startAdvertising({AdvertiseData? advertiseData}) async { @@ -20,4 +73,54 @@ class BluetoothPeripheralMock extends BluetoothPeripheral { Future stopAdvertising() async { advertiseData = null; } + + @override + Future setCharacteristicValue( + {required Uuid128 serviceUuid, + required Uuid128 characteristicUuid, + required Uint8List? value}) async { + var service = servicesMap[serviceUuid] ??= BluetoothPeripheralServiceMock(); + service.characteristicsMap[characteristicUuid] = + BluetoothPeripheralCharacteristicMock(value: value!); + } + + @override + Future notifyCharacteristicValue( + {required Uuid128 serviceUuid, + required Uuid128 characteristicUuid, + Uint8List? value}) async { + // TODO: implement notifyCharacteristicValue + _bleNotificationWrapper.sink.add(BluetoothPeripheralNotificationMock( + serviceUuid: serviceUuid, + characteristicUuid: characteristicUuid, + value: value)); + } + + @override + Future getCharacteristicValue( + {required Uuid128 serviceUuid, + required Uuid128 characteristicUuid}) async { + var service = servicesMap[serviceUuid]; + if (service == null) { + throw 'service not found $serviceUuid'; + } + return service.characteristicsMap[characteristicUuid]?.value ?? + Uint8List(0); + } + + /// From client connection + Future writeCharacteristicValue( + {required Uuid128 serviceUuid, + required Uuid128 characteristicUuid, + required Uint8List value}) async { + await setCharacteristicValue( + serviceUuid: serviceUuid, + characteristicUuid: characteristicUuid, + value: value); + _writeCharacteristicEvent.sink + .add(BluetoothPeripheralWriteCharacteristicEvent() + ..serviceUuid = serviceUuid + ..characteristicUuid = characteristicUuid + ..value = value); + } } diff --git a/bluetooth/lib/src/mock/scan_result_mock.dart b/bluetooth/lib/src/mock/scan_result_mock.dart new file mode 100644 index 0000000..74ba833 --- /dev/null +++ b/bluetooth/lib/src/mock/scan_result_mock.dart @@ -0,0 +1,11 @@ +import 'package:tekartik_bluetooth/bluetooth_device.dart'; + +class ScanResultMock implements ScanResult { + @override + final BluetoothDevice device; + + @override + final int rssi; + + ScanResultMock({required this.device, this.rssi = 50}); +} diff --git a/bluetooth/lib/src/ping/mixin.dart b/bluetooth/lib/src/ping/mixin.dart index ed3974f..775e7f1 100644 --- a/bluetooth/lib/src/ping/mixin.dart +++ b/bluetooth/lib/src/ping/mixin.dart @@ -1,6 +1,6 @@ import 'package:tekartik_bluetooth/uuid.dart'; -const int lumiServicePingNumber = 0x0101; +const int demoServicePingNumber = 0x0101; -final Uuid16 lumiServicePingCharacteristicUuid16 = - Uuid16.fromValue(lumiServicePingNumber); +final Uuid16 demoServicePingCharacteristicUuid16 = + Uuid16.fromValue(demoServicePingNumber); diff --git a/bluetooth/lib/src/platform_channel.dart b/bluetooth/lib/src/platform_channel.dart new file mode 100644 index 0000000..2b6f269 --- /dev/null +++ b/bluetooth/lib/src/platform_channel.dart @@ -0,0 +1,91 @@ +import 'package:rxdart/rxdart.dart'; +import 'package:tekartik_common_utils/common_utils_import.dart'; + +/// An command object representing the invocation of a named method. +@immutable +class MethodCall { + /// Creates a [MethodCall] representing the invocation of [method] with the + /// specified [arguments]. + const MethodCall(this.method, [this.arguments]); + + /// The name of the method to be called. + final String method; + + /// The arguments for the method. + /// + /// Must be a valid value for the [MethodCodec] used. + final dynamic arguments; + + @override + String toString() => 'MethodCall($method $arguments)'; +} + +/// Default implementation +mixin MethodCallMixin implements MethodCall { + @override + dynamic get arguments => throw UnimplementedError(); + + @override + String get method => throw UnimplementedError(); +} + +class EventChannel { + final _stream = BehaviorSubject(); + final String name; + + EventChannel(this.name); + + Stream receiveBroadcastStream() => _stream; +} + +mixin EventChannelMixin implements EventChannel { + @override + String get name => throw UnimplementedError(); + + @override + Stream receiveBroadcastStream() { + throw UnimplementedError(); + } +} + +class MethodChannel { + final String name; + + const MethodChannel(this.name); + + Future invokeMethod(String method, [dynamic arguments]) async { + throw UnsupportedError('invokeMethod'); + } + + /// Sets a callback for receiving method calls on this channel. + /// + /// The given callback will replace the currently registered callback for this + /// channel, if any. To remove the handler, pass null as the + /// `handler` argument. + /// + /// If the future returned by the handler completes with a result, that value + /// is sent back to the platform plugin caller wrapped in a success envelope + /// as defined by the [codec] of this channel. If the future completes with + /// a [PlatformException], the fields of that exception will be used to + /// populate an error envelope which is sent back instead. If the future + /// completes with a [MissingPluginException], an empty reply is sent + /// similarly to what happens if no method call handler has been set. + /// Any other exception results in an error envelope being sent. + void setMethodCallHandler( + Future Function(MethodCall call)? handler) { + throw UnsupportedError('Unsupported'); + } +} + +mixin MethodChannelMixin implements MethodChannel { + @override + Future invokeMethod(String method, [dynamic arguments]) { + throw UnimplementedError(); + } + + @override + String get name => throw UnimplementedError(); + + @override + void setMethodCallHandler(Future Function(MethodCall call)? handler) {} +} diff --git a/bluetooth/lib/src/scan_result.dart b/bluetooth/lib/src/scan_result.dart index fc77416..a3bab1c 100644 --- a/bluetooth/lib/src/scan_result.dart +++ b/bluetooth/lib/src/scan_result.dart @@ -1,5 +1,5 @@ +import 'package:cv/cv.dart'; import 'package:tekartik_common_utils/int_utils.dart'; -import 'package:tekartik_common_utils/model/model_v2.dart'; import 'bluetooth_device.dart'; diff --git a/bluetooth/pubspec.yaml b/bluetooth/pubspec.yaml index 8fc169d..4cfdb26 100644 --- a/bluetooth/pubspec.yaml +++ b/bluetooth/pubspec.yaml @@ -1,22 +1,23 @@ name: tekartik_bluetooth description: Bluetooth abstraction -version: 0.5.0 +version: 0.6.1 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: meta: collection: uuid: + cv: tekartik_common_utils: git: url: https://github.com/tekartik/common_utils.dart - ref: dart2_3 + ref: dart3a version: '>=0.10.7+1' rxdart: dev_dependencies: test: - process_run: \ No newline at end of file + process_run: diff --git a/bluetooth/test/bluetooth_manager_test_mock.dart b/bluetooth/test/bluetooth_manager_test_mock.dart index f424295..3f6b2c3 100644 --- a/bluetooth/test/bluetooth_manager_test_mock.dart +++ b/bluetooth/test/bluetooth_manager_test_mock.dart @@ -1,20 +1,13 @@ import 'package:tekartik_bluetooth/bluetooth_device.dart'; +import 'package:tekartik_bluetooth/src/common/platform_mixin.dart'; import 'package:tekartik_bluetooth/src/mixin.dart'; class BluetoothManagerTestMock - with BluetoothManagerMixin + with BluetoothManagerMixin, BluetoothManagerPlatformCompatMixin implements BluetoothManager { @override Future invokeMethod(String method, [Object? arguments]) { // TODO: implement invokeMethod throw UnimplementedError(); } - - @override - // TODO: implement isAndroid - bool? get isAndroid => throw UnimplementedError(); - - @override - // TODO: implement isIOS - bool? get isIOS => throw UnimplementedError(); } diff --git a/bluetooth/test/bluetooth_mock_test.dart b/bluetooth/test/bluetooth_mock_test.dart index 648d1bc..7e21ca9 100644 --- a/bluetooth/test/bluetooth_mock_test.dart +++ b/bluetooth/test/bluetooth_mock_test.dart @@ -1,4 +1,13 @@ +import 'dart:typed_data'; + +import 'package:tekartik_bluetooth/ble.dart'; +import 'package:tekartik_bluetooth/bluetooth.dart'; +import 'package:tekartik_bluetooth/bluetooth_peripheral.dart'; +import 'package:tekartik_bluetooth/src/mock/admin_manager_mock.dart'; import 'package:tekartik_bluetooth/src/mock/battery_device_mock.dart'; +import 'package:tekartik_bluetooth/src/mock/manager_mock.dart'; +import 'package:tekartik_bluetooth/src/mock/peripheral_mock.dart'; +import 'package:tekartik_bluetooth/uuid.dart'; import 'package:test/test.dart'; void main() { @@ -12,5 +21,213 @@ void main() { await remoteMock.start(); await remoteMock.stop(); }); + + test('check permission', () async { + var mock = BluetoothAdminManagerMock(); + expect(await mock.checkBluetoothPermissions(), true); + }); + test('check coarse', () async { + var mock = BluetoothAdminManagerMock(); + expect(await mock.checkCoarseLocationPermission(), true); + }); + test('admin manager', () async { + var mock = BluetoothAdminManagerMock(); + expect( + await mock.getAdminInfo(), + BluetoothAdminInfoImpl( + hasBluetoothBle: true, + hasBluetooth: true, + isBluetoothEnabled: true)); + }); + /* + test('manager no peripheral', () async { + var mock = BluetoothManagerMock(peripheral: null); + try { + await mock.scan().first.timeout(Duration(milliseconds: 100)); + } on TimeoutException catch (_) { + // expected + } + + await mock.close(); + });*/ + test('manager with peripheral', () async { + var peripheral = BluetoothPeripheralMock(deviceName: 'mock'); + var mock = BluetoothManagerMock(peripheral: peripheral); + var scanResult = await mock.scan().first; + expect(scanResult.device.name, 'Mock'); + expect(scanResult.device.id.id, 'mock'); + expect(scanResult.device.address, 'mock_address'); + await mock.close(); + }); + test('manager with advertising', () async { + var peripheral = BluetoothPeripheralMock(deviceName: 'mock'); + await peripheral.startAdvertising(); + var mock = BluetoothManagerMock(peripheral: peripheral); + var scanResult = await mock.scan().first; + expect(scanResult.device.name, 'Mock'); + await mock.close(); + }); + test('manager with peripheral services', () async { + var peripheral = + BluetoothPeripheralMock(deviceName: 'mock', services: []); + var mock = BluetoothManagerMock(peripheral: peripheral); + var scanResult = await mock.scan().first; + expect(scanResult.device.name, 'Mock'); + var device = await mock.newConnection(scanResult.device.id); + expect(await device.onConnectionState.first, + BluetoothDeviceConnectionState.disconnected); + await device.connect(); + expect(await device.onConnectionState.first, + BluetoothDeviceConnectionState.connected); + await device.disconnect(); + expect(await device.onConnectionState.first, + BluetoothDeviceConnectionState.disconnected); + }); + test('manager with peripheral services', () async { + var serviceUuid = Uuid128('0000180f-0000-1000-8000-00805f9b34fb'); + var characteristicUuid = Uuid128('00002a19-0000-1000-8000-00805f9b34fb'); + var peripheral = BluetoothPeripheralMock(deviceName: 'mock', services: [ + BluetoothGattService(uuid: serviceUuid, characteristics: [ + BluetoothGattCharacteristic( + uuid: characteristicUuid, + properties: BluetoothGattCharacteristic.propertyRead | + BluetoothGattCharacteristic.propertyWrite, + permissions: BluetoothGattCharacteristic.permissionRead | + BluetoothGattCharacteristic.permissionWrite) + ]) + ]); + var mock = BluetoothManagerMock(peripheral: peripheral); + var scanResult = await mock.scan().first; + expect(scanResult.device.name, 'Mock'); + var device = await mock.newConnection(scanResult.device.id); + var services = await device.getServices(); + var service = services.first; + expect( + services.first.uuid, Uuid128('0000180f-0000-1000-8000-00805f9b34fb')); + + await peripheral.setCharacteristicValue( + serviceUuid: serviceUuid, + characteristicUuid: characteristicUuid, + value: Uint8List.fromList([1, 2, 3])); + expect( + (await device.readCharacteristic(BleBluetoothCharacteristic( + service: service, uuid: characteristicUuid))) + .value, + Uint8List.fromList([1, 2, 3])); + await device.writeCharacteristic(BleBluetoothCharacteristicValue( + service: BleBluetoothService(uuid: serviceUuid), + value: Uint8List.fromList([1, 2, 3, 4]), + uuid: characteristicUuid)); + expect( + await peripheral.getCharacteristicValue( + serviceUuid: serviceUuid, + characteristicUuid: characteristicUuid, + ), + Uint8List.fromList([1, 2, 3, 4])); + await mock.close(); + }); + test('manager with notify', () async { + var serviceUuid = Uuid128('0000180f-0000-1000-8000-00805f9b34fb'); + var characteristicUuid = Uuid128('00002a19-0000-1000-8000-00805f9b34fb'); + var peripheral = BluetoothPeripheralMock(deviceName: 'mock', services: [ + BluetoothGattService(uuid: serviceUuid, characteristics: [ + BluetoothGattCharacteristic( + uuid: characteristicUuid, + properties: BluetoothGattCharacteristic.propertyRead | + BluetoothGattCharacteristic.propertyWrite | + BluetoothGattCharacteristic.propertyNotify, + permissions: BluetoothGattCharacteristic.permissionRead | + BluetoothGattCharacteristic.permissionWrite) + ]) + ]); + var mock = BluetoothManagerMock(peripheral: peripheral); + var scanResult = await mock.scan().first; + expect(scanResult.device.name, 'Mock'); + var device = await mock.newConnection(scanResult.device.id); + var services = await device.getServices(); + var service = services.first; + expect( + services.first.uuid, Uuid128('0000180f-0000-1000-8000-00805f9b34fb')); + + var characteristic = BleBluetoothCharacteristic( + service: service, uuid: characteristicUuid); + await device.registerCharacteristic(characteristic, true); + var first = device.onCharacteristicValueChanged(characteristic).first; + await peripheral.setAndNotifyCharacteristicValue( + serviceUuid: serviceUuid, + characteristicUuid: characteristicUuid, + value: Uint8List.fromList([1, 2, 3])); + expect((await first).value, [1, 2, 3]); + await mock.close(); + }); + test('manager with indicate', () async { + var serviceUuid = Uuid128('0000180f-0000-1000-8000-00805f9b34fb'); + var characteristicUuid = Uuid128('00002a19-0000-1000-8000-00805f9b34fb'); + var peripheral = BluetoothPeripheralMock(deviceName: 'mock', services: [ + BluetoothGattService(uuid: serviceUuid, characteristics: [ + BluetoothGattCharacteristic( + uuid: characteristicUuid, + properties: BluetoothGattCharacteristic.propertyRead | + BluetoothGattCharacteristic.propertyWrite | + BluetoothGattCharacteristic.propertyIndicate, + permissions: BluetoothGattCharacteristic.permissionRead | + BluetoothGattCharacteristic.permissionWrite) + ]) + ]); + var mock = BluetoothManagerMock(peripheral: peripheral); + var scanResult = await mock.scan().first; + expect(scanResult.device.name, 'Mock'); + var device = await mock.newConnection(scanResult.device.id); + var services = await device.getServices(); + var service = services.first; + expect( + services.first.uuid, Uuid128('0000180f-0000-1000-8000-00805f9b34fb')); + + var characteristic = BleBluetoothCharacteristic( + service: service, uuid: characteristicUuid); + + await device.registerCharacteristic(characteristic, false); + var first = device.onCharacteristicValueChanged(characteristic).first; + await peripheral.setAndNotifyCharacteristicValue( + serviceUuid: serviceUuid, + characteristicUuid: characteristicUuid, + value: Uint8List.fromList([1, 2, 3])); + expect((await first).value, [1, 2, 3]); + await mock.close(); + }); + test('peripheral on write', () async { + var serviceUuid = Uuid128('0000180f-0000-1000-8000-00805f9b34fb'); + var characteristicUuid = Uuid128('00002a19-0000-1000-8000-00805f9b34fb'); + var peripheral = BluetoothPeripheralMock(deviceName: 'mock', services: [ + BluetoothGattService(uuid: serviceUuid, characteristics: [ + BluetoothGattCharacteristic( + uuid: characteristicUuid, + properties: BluetoothGattCharacteristic.propertyRead | + BluetoothGattCharacteristic.propertyWrite, + permissions: BluetoothGattCharacteristic.permissionRead | + BluetoothGattCharacteristic.permissionWrite) + ]) + ]); + var first = peripheral.writeCharacteristicEvent.stream.first; + var mock = BluetoothManagerMock(peripheral: peripheral); + var scanResult = await mock.scan().first; + expect(scanResult.device.name, 'Mock'); + var device = await mock.newConnection(scanResult.device.id); + + var service = BleBluetoothService(uuid: serviceUuid); + var characteristic = BleBluetoothCharacteristic( + service: service, uuid: characteristicUuid); + await device.writeCharacteristic(BleBluetoothCharacteristicValue( + bc: characteristic, + value: Uint8List.fromList([1, 2, 3, 4, 5]), + )); + expect( + await first, + BluetoothPeripheralWriteCharacteristicEvent( + serviceUuid: serviceUuid, + characteristicUuid: characteristicUuid, + value: Uint8List.fromList([1, 2, 3, 4, 5]))); + await mock.close(); + }); }); } diff --git a/bluetooth/test/bluetooth_peripheral_test.dart b/bluetooth/test/bluetooth_peripheral_test.dart index e9d4aa3..73f2295 100644 --- a/bluetooth/test/bluetooth_peripheral_test.dart +++ b/bluetooth/test/bluetooth_peripheral_test.dart @@ -10,7 +10,7 @@ var _characteristic2Uuid = Uuid128('56adb822-7a90-4d45-90ec-b8710207282a'); void main() { group('BluetoothGattService', () { - test('findGettServices', () { + test('findGattServices', () { var gattServices = [ BluetoothGattService( uuid: _service1Uuid, diff --git a/bluetooth_bluez/README.md b/bluetooth_bluez/README.md index 62b4253..2405238 100644 --- a/bluetooth_bluez/README.md +++ b/bluetooth_bluez/README.md @@ -10,6 +10,6 @@ dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_bluez - ref: dart2_3 + ref: dart3a version: '>=0.1.0' ``` \ No newline at end of file diff --git a/bluetooth_bluez/pubspec.yaml b/bluetooth_bluez/pubspec.yaml index 16cb18b..76abeca 100644 --- a/bluetooth_bluez/pubspec.yaml +++ b/bluetooth_bluez/pubspec.yaml @@ -4,14 +4,14 @@ version: 0.5.0 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: tekartik_bluetooth: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth - ref: dart2_3 + ref: dart3a version: '>=0.2.1' bluez: dbus: @@ -21,4 +21,4 @@ dev_dependencies: dependency_overrides: tekartik_bluetooth: - path: ../bluetooth \ No newline at end of file + path: ../bluetooth diff --git a/bluetooth_flutter/README.md b/bluetooth_flutter/README.md index 7f311a7..096322f 100644 --- a/bluetooth_flutter/README.md +++ b/bluetooth_flutter/README.md @@ -11,6 +11,6 @@ dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_flutter - ref: dart2_3 + ref: dart3a version: '>=0.2.1' ``` \ No newline at end of file diff --git a/bluetooth_flutter/analysis_options.yaml b/bluetooth_flutter/analysis_options.yaml index 206b1a5..637d31d 100644 --- a/bluetooth_flutter/analysis_options.yaml +++ b/bluetooth_flutter/analysis_options.yaml @@ -27,8 +27,7 @@ linter: - empty_statements - hash_and_equals # - invariant_booleans # https://github.com/flutter/flutter/issues/5790 - - iterable_contains_unrelated_type - - list_remove_unrelated_type + - collection_methods_unrelated_type # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791 - no_adjacent_strings_in_list - no_duplicate_case_values @@ -84,7 +83,6 @@ linter: # - prefer_const_constructors # remove for app a pain for widgets list # - prefer_constructors_over_static_methods # not yet tested - prefer_contains - - prefer_equal_for_default_values # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods # - prefer_final_fields # https://github.com/dart-lang/linter/issues/506 # removed alex diff --git a/bluetooth_flutter/example/analysis_options.yaml b/bluetooth_flutter/example/analysis_options.yaml index babb142..13a2bb9 100644 --- a/bluetooth_flutter/example/analysis_options.yaml +++ b/bluetooth_flutter/example/analysis_options.yaml @@ -36,8 +36,7 @@ linter: - empty_statements - hash_and_equals # - invariant_booleans # https://github.com/flutter/flutter/issues/5790 - - iterable_contains_unrelated_type - - list_remove_unrelated_type + - collection_methods_unrelated_type # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791 - no_adjacent_strings_in_list - no_duplicate_case_values @@ -93,7 +92,6 @@ linter: # - prefer_const_constructors # remove for app a pain for widgets list # - prefer_constructors_over_static_methods # not yet tested - prefer_contains - - prefer_equal_for_default_values # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods # - prefer_final_fields # https://github.com/dart-lang/linter/issues/506 # removed alex diff --git a/bluetooth_flutter/example/pubspec.yaml b/bluetooth_flutter/example/pubspec.yaml index 999b6bb..56080e6 100644 --- a/bluetooth_flutter/example/pubspec.yaml +++ b/bluetooth_flutter/example/pubspec.yaml @@ -3,7 +3,7 @@ description: Demonstrates how to use the bluetooth_flutter plugin. publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: flutter: @@ -25,7 +25,7 @@ dev_dependencies: tekartik_test_menu_flutter: git: url: https://github.com/tekartik/test_menu_flutter - ref: dart2_3 + ref: dart3a path: test_menu_flutter version: '>=0.2.5' @@ -77,4 +77,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.io/custom-fonts/#from-packages \ No newline at end of file + # see https://flutter.io/custom-fonts/#from-packages diff --git a/bluetooth_flutter/lib/src/client/connection.dart b/bluetooth_flutter/lib/src/client/connection.dart index eccadbb..fa90aeb 100644 --- a/bluetooth_flutter/lib/src/client/connection.dart +++ b/bluetooth_flutter/lib/src/client/connection.dart @@ -8,7 +8,6 @@ import 'package:tekartik_bluetooth_flutter/src/import.dart'; import 'package:tekartik_bluetooth_flutter/src/import_bluetooth.dart'; import 'package:tekartik_bluetooth_flutter/src/mixin.dart'; import 'package:tekartik_bluetooth_flutter/utils/model_utils.dart'; - // abstract class BluetoothDeviceConnection {} export 'package:tekartik_bluetooth/src/device_connection.dart'; diff --git a/bluetooth_flutter/lib/src/import.dart b/bluetooth_flutter/lib/src/import.dart index 11a2dfc..bd1af46 100644 --- a/bluetooth_flutter/lib/src/import.dart +++ b/bluetooth_flutter/lib/src/import.dart @@ -1,6 +1,6 @@ // ignore_for_file: implementation_imports, depend_on_referenced_packages +export 'package:cv/cv.dart'; export 'package:tekartik_bluetooth/src/import.dart'; export 'package:tekartik_common_utils/common_utils_import.dart'; export 'package:tekartik_common_utils/env_utils.dart'; export 'package:tekartik_common_utils/map_utils.dart'; -export 'package:tekartik_common_utils/model/model_v2.dart'; diff --git a/bluetooth_flutter/pubspec.yaml b/bluetooth_flutter/pubspec.yaml index 47c5c7e..6284913 100644 --- a/bluetooth_flutter/pubspec.yaml +++ b/bluetooth_flutter/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.5.0 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: flutter: @@ -15,7 +15,7 @@ dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth - ref: dart2_3 + ref: dart3a version: '>=0.2.1' dependency_overrides: @@ -34,7 +34,7 @@ dev_dependencies: tekartik_lints_flutter: git: url: https://github.com/tekartik/common_flutter.dart - ref: dart2_3 + ref: dart3a path: packages/lints_flutter version: '>=0.1.0' # For information on the generic Dart part of this file, see the @@ -81,4 +81,4 @@ flutter: # weight: 700 # # For details regarding fonts in packages, see - # https://flutter.io/custom-fonts/#from-packages \ No newline at end of file + # https://flutter.io/custom-fonts/#from-packages diff --git a/bluetooth_flutter_blue/README.md b/bluetooth_flutter_blue/README.md index 7f311a7..096322f 100644 --- a/bluetooth_flutter_blue/README.md +++ b/bluetooth_flutter_blue/README.md @@ -11,6 +11,6 @@ dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_flutter - ref: dart2_3 + ref: dart3a version: '>=0.2.1' ``` \ No newline at end of file diff --git a/bluetooth_flutter_blue/analysis_options.yaml b/bluetooth_flutter_blue/analysis_options.yaml index b87de40..54f7057 100644 --- a/bluetooth_flutter_blue/analysis_options.yaml +++ b/bluetooth_flutter_blue/analysis_options.yaml @@ -34,8 +34,7 @@ linter: - empty_statements - hash_and_equals # - invariant_booleans # https://github.com/flutter/flutter/issues/5790 - - iterable_contains_unrelated_type - - list_remove_unrelated_type + - collection_methods_unrelated_type # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791 - no_adjacent_strings_in_list - no_duplicate_case_values @@ -91,7 +90,6 @@ linter: # - prefer_const_constructors # remove for app a pain for widgets list # - prefer_constructors_over_static_methods # not yet tested - prefer_contains - - prefer_equal_for_default_values # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods # - prefer_final_fields # https://github.com/dart-lang/linter/issues/506 # removed alex diff --git a/bluetooth_flutter_blue/example/analysis_options.yaml b/bluetooth_flutter_blue/example/analysis_options.yaml index babb142..13a2bb9 100644 --- a/bluetooth_flutter_blue/example/analysis_options.yaml +++ b/bluetooth_flutter_blue/example/analysis_options.yaml @@ -36,8 +36,7 @@ linter: - empty_statements - hash_and_equals # - invariant_booleans # https://github.com/flutter/flutter/issues/5790 - - iterable_contains_unrelated_type - - list_remove_unrelated_type + - collection_methods_unrelated_type # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791 - no_adjacent_strings_in_list - no_duplicate_case_values @@ -93,7 +92,6 @@ linter: # - prefer_const_constructors # remove for app a pain for widgets list # - prefer_constructors_over_static_methods # not yet tested - prefer_contains - - prefer_equal_for_default_values # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods # - prefer_final_fields # https://github.com/dart-lang/linter/issues/506 # removed alex diff --git a/bluetooth_flutter_blue/example/lib/main.dart b/bluetooth_flutter_blue/example/lib/main.dart index 6ca9aa9..33e875a 100644 --- a/bluetooth_flutter_blue/example/lib/main.dart +++ b/bluetooth_flutter_blue/example/lib/main.dart @@ -16,7 +16,7 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - BluetoothState? _bluetoothState; + BluetoothAdapterState? _bluetoothState; DateTime? _statusDate; // final _bluetoothManager = bluetoothManager; diff --git a/bluetooth_flutter_blue/example/lib/menu_flutter_blue.dart b/bluetooth_flutter_blue/example/lib/menu_flutter_blue.dart index fbdee6e..822b0fc 100644 --- a/bluetooth_flutter_blue/example/lib/menu_flutter_blue.dart +++ b/bluetooth_flutter_blue/example/lib/menu_flutter_blue.dart @@ -1,5 +1,6 @@ //import 'package:tekartik_bluetooth_flutter_blue/bluetooth_flutter.dart'; -import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +// ignore: implementation_imports +import 'package:tekartik_bluetooth_flutter_blue/src/flutter_blue_import.dart'; import 'package:tekartik_common_utils/common_utils_import.dart'; import 'package:tekartik_test_menu_flutter/test.dart'; @@ -18,13 +19,13 @@ void menuFlutterBlue() { item('connect_$name', () async { scanSubscription?.cancel(); - scanSubscription = FlutterBluePlus.instance - .scan(timeout: Duration(seconds: 30)) + scanSubscription = FlutterBluePlusPrvExt.scanAndStreamResults( + timeout: Duration(seconds: 30)) .listen((result) { - var id = result.device.id.id; + var id = result.device.remoteId.str; if (!deviceIds.contains(id)) { write( - '[${_devices.length}] scan_$name: ${result.device.id} ${result.device.name} ${result.rssi}'); + '[${_devices.length}] scan_$name: ${result.device.remoteId} ${result.device.platformName} ${result.rssi}'); deviceIds.add(id); _devices[id] = result.device; } @@ -37,7 +38,7 @@ void menuFlutterBlue() { for (int i = 0; i < deviceIds.length; i++) { var device = _devices[deviceIds[i]]!; - write('[$i]: ${device.id} ${device.name}'); + write('[$i]: ${device.remoteId} ${device.platformName}'); } int? index = parseInt(await prompt('Enter connect_$name index')); if (index != null) { @@ -45,15 +46,15 @@ void menuFlutterBlue() { var device = _devices[deviceId]!; _cancelScanSubscription(); stateChangeSubscription?.cancel(); - stateChangeSubscription = device.state.listen((state) { + stateChangeSubscription = device.connectionState.listen((state) { write('onStateChanged_$name $state'); }, onDone: () { write('onStateChanged_$name done'); }); - write('get_state_$name ${await device.state.first}'); + write('get_state_$name ${await device.connectionState.first}'); - write('connecting ${device.id}'); - connectSubscription = device.state.listen((state) { + write('connecting ${device.remoteId}'); + connectSubscription = device.connectionState.listen((state) { write('state_$name: $state'); }, onDone: () { write('scan_$name: connect done'); @@ -80,10 +81,10 @@ void menuFlutterBlue() { menu('flutter_blue_scan', () { StreamSubscription? stateSubscription; item('get_bt_state', () async { - write('get_state: ${await FlutterBluePlus.instance.state.first}'); + write('get_state: ${await FlutterBluePlus.adapterState.first}'); }); item('register_bt_state', () { - FlutterBluePlus.instance.state.listen((state) { + FlutterBluePlus.adapterState.listen((state) { write('state: $state'); }, onDone: () { write('register_bt_state done'); @@ -106,11 +107,11 @@ void menuFlutterBlue() { item('scan_$name', () { scanSubscription?.cancel(); - scanSubscription = FlutterBluePlus.instance - .scan(timeout: Duration(seconds: 30)) + scanSubscription = FlutterBluePlusPrvExt.scanAndStreamResults( + timeout: Duration(seconds: 30)) .listen((result) { write( - 'scan_$name: ${result.device.id} ${result.device.name} ${result.rssi}'); + 'scan_$name: ${result.device.remoteId} ${result.device.platformName} ${result.rssi}'); }, onDone: () { write('scan_$name: done'); }, onError: (e, st) { diff --git a/bluetooth_flutter_blue/example/pubspec.yaml b/bluetooth_flutter_blue/example/pubspec.yaml index b2ecde4..5765e89 100644 --- a/bluetooth_flutter_blue/example/pubspec.yaml +++ b/bluetooth_flutter_blue/example/pubspec.yaml @@ -3,7 +3,7 @@ description: Demonstrates how to use the bluetooth_flutter plugin. publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: flutter: @@ -25,7 +25,7 @@ dev_dependencies: tekartik_test_menu_flutter: git: url: https://github.com/tekartik/test_menu_flutter - ref: dart2_3 + ref: dart3a path: test_menu_flutter version: '>=0.2.5' @@ -79,4 +79,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.io/custom-fonts/#from-packages \ No newline at end of file + # see https://flutter.io/custom-fonts/#from-packages diff --git a/bluetooth_flutter_blue/lib/bluetooth_flutter.dart b/bluetooth_flutter_blue/lib/bluetooth_flutter.dart index 0f4c157..0a63fad 100644 --- a/bluetooth_flutter_blue/lib/bluetooth_flutter.dart +++ b/bluetooth_flutter_blue/lib/bluetooth_flutter.dart @@ -19,15 +19,15 @@ class BluetoothFlutterBlue { static bool? _isSupported; static Future get _isSupportedReady async { - return _isSupported ??= await FlutterBluePlus.instance.isAvailable; + return _isSupported ??= await FlutterBluePlus.isAvailable; } - static Future get bluetoothState async { + static Future get bluetoothState async { _isSupported ??= await _isSupportedReady; if (_isSupported!) { - return await FlutterBluePlus.instance.state.first; + return await FlutterBluePlus.adapterState.first; } else { - return BluetoothState.unavailable; + return BluetoothAdapterState.unavailable; } } } diff --git a/bluetooth_flutter_blue/lib/src/ble_flutter_blue.dart b/bluetooth_flutter_blue/lib/src/ble_flutter_blue.dart index 9e11801..24da10f 100644 --- a/bluetooth_flutter_blue/lib/src/ble_flutter_blue.dart +++ b/bluetooth_flutter_blue/lib/src/ble_flutter_blue.dart @@ -24,7 +24,7 @@ class BluetoothCharacteristicFlutterBlue { /// Value changed Stream> get value { - return nativeImpl.onValueChangedStream; + return nativeImpl.onValueReceived; } } diff --git a/bluetooth_flutter_blue/lib/src/bluetooth_admin_manager_flutter_blue.dart b/bluetooth_flutter_blue/lib/src/bluetooth_admin_manager_flutter_blue.dart index 867708a..745098b 100644 --- a/bluetooth_flutter_blue/lib/src/bluetooth_admin_manager_flutter_blue.dart +++ b/bluetooth_flutter_blue/lib/src/bluetooth_admin_manager_flutter_blue.dart @@ -25,12 +25,13 @@ class BluetoothAdminManagerFlutterBlue implements BluetoothAdminManager { @override Future disable() async { - await FlutterBluePlus.instance.turnOff(); + // No longer supported... + // await FlutterBluePlus.turnOff(); } @override Future enable({int? requestCode, int? androidRequestCode}) async { - await FlutterBluePlus.instance.turnOn(); + await FlutterBluePlus.turnOn(); if (!((await getAdminInfo()).isBluetoothEnabled ?? false)) { throw StateError('Manual setup needed'); } @@ -43,17 +44,17 @@ class BluetoothAdminManagerFlutterBlue implements BluetoothAdminManager { bool? available; bool? on; try { - var instance = FlutterBluePlus.instance; + BluetoothAdapterState? state; try { - await instance.state.first; - available = await instance.isAvailable; + state = await FlutterBluePlus.adapterState.first; + available = await FlutterBluePlus.isAvailable; // devPrint('blue available $available'); } catch (e) { // ignore: avoid_print print('error $e getting flutter blue available'); } try { - on = await instance.isOn; + on = state == BluetoothAdapterState.on; } catch (e) { // ignore: avoid_print print('error $e getting flutter blue on'); diff --git a/bluetooth_flutter_blue/lib/src/bluetooth_device_connection_flutter_blue.dart b/bluetooth_flutter_blue/lib/src/bluetooth_device_connection_flutter_blue.dart index aa1ead2..f1c1482 100644 --- a/bluetooth_flutter_blue/lib/src/bluetooth_device_connection_flutter_blue.dart +++ b/bluetooth_flutter_blue/lib/src/bluetooth_device_connection_flutter_blue.dart @@ -9,17 +9,22 @@ import 'package:tekartik_bluetooth_flutter_blue/utils/guid_utils.dart'; import 'flutter_blue_import.dart' as native; import 'import.dart'; -BluetoothDeviceConnectionState connectionStateFromBluetoothDeviceState( - native.BluetoothDeviceState state) { +BluetoothDeviceConnectionState connectionStateFromBluetoothConnectionState( + native.BluetoothConnectionState state) { switch (state) { - case native.BluetoothDeviceState.connecting: - return BluetoothDeviceConnectionState.connecting; - case native.BluetoothDeviceState.connected: + case native.BluetoothConnectionState.connected: return BluetoothDeviceConnectionState.connected; - case native.BluetoothDeviceState.disconnected: + case native.BluetoothConnectionState.disconnected: return BluetoothDeviceConnectionState.disconnected; - case native.BluetoothDeviceState.disconnecting: + /* + No longer supported in iOS and Android + case native.BluetoothConnectionState.connecting: + return BluetoothDeviceConnectionState.connecting; + case native.BluetoothConnectionState.disconnecting: return BluetoothDeviceConnectionState.disconnecting; + */ + default: + return BluetoothDeviceConnectionState.disconnected; } } @@ -129,8 +134,8 @@ class BluetoothDeviceConnectionFlutterBlue @override Stream get onConnectionState { var nativeImpl = device.nativeImpl; - return nativeImpl.state - .map((native) => connectionStateFromBluetoothDeviceState(native)); + return nativeImpl.connectionState + .map((native) => connectionStateFromBluetoothConnectionState(native)); } BluetoothCharacteristicFlutterBlue? findCharacteristic( diff --git a/bluetooth_flutter_blue/lib/src/bluetooth_device_flutter_blue.dart b/bluetooth_flutter_blue/lib/src/bluetooth_device_flutter_blue.dart index 6e9ca05..e2b45d2 100644 --- a/bluetooth_flutter_blue/lib/src/bluetooth_device_flutter_blue.dart +++ b/bluetooth_flutter_blue/lib/src/bluetooth_device_flutter_blue.dart @@ -9,7 +9,7 @@ class BluetoothDeviceIdFlutterBlue BluetoothDeviceIdFlutterBlue(this.nativeId); @override - String get id => nativeId.id; + String get id => nativeId.str; } class BluetoothDeviceFlutterBlue @@ -20,22 +20,14 @@ class BluetoothDeviceFlutterBlue BluetoothDeviceFlutterBlue(this.nativeImpl); @override - String get address => nativeImpl.id.id; + String get address => nativeImpl.remoteId.str; @override - BluetoothDeviceId get id => BluetoothDeviceIdFlutterBlue(nativeImpl.id); + BluetoothDeviceId get id => BluetoothDeviceIdFlutterBlue(nativeImpl.remoteId); @override - String get name => nativeImpl.name; + String get name => nativeImpl.platformName; @override - BluetoothDeviceType get type => - _deviceTypeMap[nativeImpl.type] ?? BluetoothDeviceType.unknown; + BluetoothDeviceType get type => BluetoothDeviceType.le; } - -var _deviceTypeMap = { - native.BluetoothDeviceType.le: BluetoothDeviceType.le, - native.BluetoothDeviceType.unknown: BluetoothDeviceType.unknown, - native.BluetoothDeviceType.classic: BluetoothDeviceType.classic, - native.BluetoothDeviceType.dual: BluetoothDeviceType.dual -}; diff --git a/bluetooth_flutter_blue/lib/src/bluetooth_manager_flutter_blue.dart b/bluetooth_flutter_blue/lib/src/bluetooth_manager_flutter_blue.dart index 53128e4..4b83744 100644 --- a/bluetooth_flutter_blue/lib/src/bluetooth_manager_flutter_blue.dart +++ b/bluetooth_flutter_blue/lib/src/bluetooth_manager_flutter_blue.dart @@ -76,7 +76,7 @@ class BluetoothManagerFlutterBlue implements BluetoothManager { @override Future> getConnectedDevices() async { - var devices = await native.FlutterBluePlus.instance.connectedDevices; + var devices = await native.FlutterBluePlus.connectedSystemDevices; var blueDevices = devices.map((native) => BluetoothDeviceFlutterBlue(native)).toList(); // cache @@ -123,8 +123,8 @@ class BluetoothManagerFlutterBlue implements BluetoothManager { var nativeServices = withServices?.map((e) => guidFromUuid(e)).toList() ?? []; ctlr = StreamController(onListen: () { - scannerSubscription ??= native.FlutterBlue.instance - .scan(withServices: nativeServices) + scannerSubscription ??= native.FlutterBluePlusPrvExt.scanAndStreamResults( + withServices: nativeServices) .listen((nativeResult) { var scanResult = ScanResultFlutter(nativeResult); @@ -134,7 +134,7 @@ class BluetoothManagerFlutterBlue implements BluetoothManager { ctlr!.add(scanResult); }); }, onCancel: () { - native.FlutterBlue.instance.stopScan(); + native.FlutterBlue.stopScan(); scannerSubscription?.cancel(); ctlr?.close(); }); @@ -143,7 +143,7 @@ class BluetoothManagerFlutterBlue implements BluetoothManager { @override Future stop() async { - await native.FlutterBlue.instance.stopScan(); + await native.FlutterBlue.stopScan(); /* unawaited(scannerSubscription?.cancel()); scannerSubscription = null; diff --git a/bluetooth_flutter_blue/lib/src/flutter_blue_import.dart b/bluetooth_flutter_blue/lib/src/flutter_blue_import.dart index eb6ae50..3741eb9 100644 --- a/bluetooth_flutter_blue/lib/src/flutter_blue_import.dart +++ b/bluetooth_flutter_blue/lib/src/flutter_blue_import.dart @@ -1,5 +1,45 @@ +import 'dart:async'; + import 'package:flutter_blue_plus/flutter_blue_plus.dart'; export 'package:flutter_blue_plus/flutter_blue_plus.dart'; typedef FlutterBlue = FlutterBluePlus; + +extension FlutterBluePlusPrvExt on FlutterBluePlus { + static Stream scanAndStreamResults({ + List withServices = const [], + Duration? timeout, + bool androidUsesFineLocation = false, + }) { + if (FlutterBluePlus.isScanningNow) { + throw Exception('Another scan is already in progress'); + } + + final controller = StreamController(); + + var subscription = FlutterBluePlus.scanResults.listen( + (r) => controller.add(r.first), + onError: (Object e, StackTrace? stackTrace) => + controller.addError(e, stackTrace), + ); + + FlutterBluePlus.startScan( + withServices: withServices, + timeout: timeout, + removeIfGone: null, + oneByOne: true, + androidUsesFineLocation: androidUsesFineLocation, + ); + + Future scanComplete = + FlutterBluePlus.isScanning.where((e) => e == false).first; + + scanComplete.whenComplete(() { + subscription.cancel(); + controller.close(); + }); + + return controller.stream; + } +} diff --git a/bluetooth_flutter_blue/pubspec.yaml b/bluetooth_flutter_blue/pubspec.yaml index e135dcc..8eee362 100644 --- a/bluetooth_flutter_blue/pubspec.yaml +++ b/bluetooth_flutter_blue/pubspec.yaml @@ -4,12 +4,12 @@ version: 0.5.0 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: flutter: sdk: flutter - flutter_blue_plus: + flutter_blue_plus: '>=1.16.2' # flutter_blue: '>=0.8.0' # flutter_blue: # git: @@ -21,18 +21,18 @@ dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth - ref: dart2_3 + ref: dart3a version: '>=0.2.1' tekartik_bluetooth_flutter: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_flutter - ref: dart2_3 + ref: dart3a version: '>=0.2.1' tekartik_app_platform: git: url: https://github.com/tekartik/app_flutter_utils.dart - ref: dart2_3 + ref: dart3a path: app_platform version: '>=0.1.0' dependency_overrides: @@ -55,7 +55,7 @@ dev_dependencies: tekartik_lints_flutter: git: url: https://github.com/tekartik/common_flutter.dart - ref: dart2_3 + ref: dart3a path: packages/lints_flutter version: '>=0.1.0' @@ -99,4 +99,4 @@ flutter: # weight: 700 # # For details regarding fonts in packages, see - # https://flutter.io/custom-fonts/#from-packages \ No newline at end of file + # https://flutter.io/custom-fonts/#from-packages diff --git a/bluetooth_server/README.md b/bluetooth_server/README.md index ffc48b5..dbafb18 100644 --- a/bluetooth_server/README.md +++ b/bluetooth_server/README.md @@ -11,6 +11,6 @@ dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_server - ref: dart2_3 + ref: dart3a version: '>=0.2.1' ``` \ No newline at end of file diff --git a/bluetooth_server/pubspec.yaml b/bluetooth_server/pubspec.yaml index 4a431f4..b20938f 100644 --- a/bluetooth_server/pubspec.yaml +++ b/bluetooth_server/pubspec.yaml @@ -4,26 +4,26 @@ version: 0.2.1 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: tekartik_web_socket_io: git: url: https://github.com/tekartik/web_socket.dart - ref: dart2_3 + ref: dart3a path: web_socket_io version: '>=0.2.0' json_rpc_2: tekartik_common_utils: git: url: https://github.com/tekartik/common_utils.dart - ref: dart2_3 + ref: dart3a version: '>=0.9.7+1' tekartik_bluetooth: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth - ref: dart2_3 + ref: dart3a version: '>=0.2.1' dev_dependencies: @@ -69,4 +69,4 @@ flutter: # weight: 700 # # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file + # https://flutter.dev/custom-fonts/#from-packages diff --git a/bluetooth_server_app/pubspec.yaml b/bluetooth_server_app/pubspec.yaml index 45083da..003d511 100644 --- a/bluetooth_server_app/pubspec.yaml +++ b/bluetooth_server_app/pubspec.yaml @@ -15,7 +15,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: flutter: @@ -29,7 +29,7 @@ dependencies: tekartik_test_menu_flutter: git: url: https://github.com/tekartik/test_menu_flutter - ref: dart2_3 + ref: dart3a path: test_menu_flutter version: '>=0.2.4' @@ -40,7 +40,7 @@ dev_dependencies: tekartik_lints_flutter: git: url: https://github.com/tekartik/common_flutter.dart - ref: dart2_3 + ref: dart3a path: packages/lints_flutter version: '>=0.1.0' @@ -99,4 +99,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/bluetooth_server_flutter/README.md b/bluetooth_server_flutter/README.md index 717ae34..599da01 100644 --- a/bluetooth_server_flutter/README.md +++ b/bluetooth_server_flutter/README.md @@ -11,6 +11,6 @@ dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_server_flutter - ref: dart2_3 + ref: dart3a version: '>=0.2.1' ``` \ No newline at end of file diff --git a/bluetooth_server_flutter/analysis_options.yaml b/bluetooth_server_flutter/analysis_options.yaml index 206b1a5..637d31d 100644 --- a/bluetooth_server_flutter/analysis_options.yaml +++ b/bluetooth_server_flutter/analysis_options.yaml @@ -27,8 +27,7 @@ linter: - empty_statements - hash_and_equals # - invariant_booleans # https://github.com/flutter/flutter/issues/5790 - - iterable_contains_unrelated_type - - list_remove_unrelated_type + - collection_methods_unrelated_type # - literal_only_boolean_expressions # https://github.com/flutter/flutter/issues/5791 - no_adjacent_strings_in_list - no_duplicate_case_values @@ -84,7 +83,6 @@ linter: # - prefer_const_constructors # remove for app a pain for widgets list # - prefer_constructors_over_static_methods # not yet tested - prefer_contains - - prefer_equal_for_default_values # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods # - prefer_final_fields # https://github.com/dart-lang/linter/issues/506 # removed alex diff --git a/bluetooth_server_flutter/pubspec.yaml b/bluetooth_server_flutter/pubspec.yaml index 1ff3fc3..b33525d 100644 --- a/bluetooth_server_flutter/pubspec.yaml +++ b/bluetooth_server_flutter/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.2.1 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' flutter: ">=1.17.0" dependencies: @@ -14,13 +14,13 @@ dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_flutter_blue - ref: dart2_3 + ref: dart3a version: '>=0.2.1' tekartik_bluetooth_server: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_server - ref: dart2_3 + ref: dart3a version: '>=0.2.1' dev_dependencies: @@ -30,7 +30,7 @@ dev_dependencies: tekartik_lints_flutter: git: url: https://github.com/tekartik/common_flutter.dart - ref: dart2_3 + ref: dart3a path: packages/lints_flutter version: '>=0.1.0' @@ -78,4 +78,4 @@ flutter: # weight: 700 # # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file + # https://flutter.dev/custom-fonts/#from-packages diff --git a/bluetooth_test/pubspec.yaml b/bluetooth_test/pubspec.yaml index 3e5cf4c..fd39751 100644 --- a/bluetooth_test/pubspec.yaml +++ b/bluetooth_test/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dev_dependencies: test: @@ -13,7 +13,7 @@ dev_dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth - ref: dart2_3 + ref: dart3a version: '>=0.2.1' dependency_overrides: @@ -55,4 +55,4 @@ flutter: # weight: 700 # # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file + # https://flutter.dev/custom-fonts/#from-packages diff --git a/bluetooth_test_app/lib/page/ble_characteristic_page.dart b/bluetooth_test_app/lib/page/ble_characteristic_page.dart index 7dfd1db..5ca4ed9 100644 --- a/bluetooth_test_app/lib/page/ble_characteristic_page.dart +++ b/bluetooth_test_app/lib/page/ble_characteristic_page.dart @@ -246,10 +246,14 @@ class _BleCharacteristicPageState extends State // ignore: use_build_context_synchronously snackInfo(context, 'Write $bcv success'); } catch (e) { - snackError(context, 'Write $bcv error $e'); + if (context.mounted) { + snackError(context, 'Write $bcv error $e'); + } } } catch (e) { - snackError(context, 'Cannot parse data'); + if (context.mounted) { + snackError(context, 'Cannot parse data'); + } } }, text: 'Write') diff --git a/bluetooth_test_app/lib/test_main.dart b/bluetooth_test_app/lib/test_main.dart index d47c8a7..415b4c7 100644 --- a/bluetooth_test_app/lib/test_main.dart +++ b/bluetooth_test_app/lib/test_main.dart @@ -101,7 +101,7 @@ void main() { menu('Flutter blue', () { item('Current state', () async { - var state = await fbl.FlutterBluePlus.instance.state.first; + var state = await fbl.FlutterBluePlus.adapterState.first; write('state: $state'); }); }); diff --git a/bluetooth_test_app/pubspec.yaml b/bluetooth_test_app/pubspec.yaml index 93f5b4a..fe6b1a3 100644 --- a/bluetooth_test_app/pubspec.yaml +++ b/bluetooth_test_app/pubspec.yaml @@ -15,7 +15,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: flutter: @@ -23,7 +23,7 @@ dependencies: tekartik_app_platform: git: url: https://github.com/tekartik/app_flutter_utils.dart - ref: dart2_3 + ref: dart3a path: app_platform version: '>=0.1.0' # The following adds the Cupertino Icons font to your application. @@ -33,13 +33,13 @@ dependencies: tekartik_platform_io: git: url: https://github.com/tekartik/platform.dart - ref: dart2_3 + ref: dart3a path: platform_io tekartik_bluetooth_web: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_web - ref: dart2_3 + ref: dart3a flutter_blue_plus: dev_dependencies: flutter_test: @@ -50,30 +50,30 @@ dev_dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_flutter_blue - ref: dart2_3 + ref: dart3a version: '>=0.2.1' tekartik_bluetooth_bluez: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_bluez - ref: dart2_3 + ref: dart3a version: '>=0.2.1' tekartik_test_menu_flutter: git: url: https://github.com/tekartik/test_menu_flutter - ref: dart2_3 + ref: dart3a path: test_menu_flutter version: '>=0.2.5' tekartik_lints_flutter: git: url: https://github.com/tekartik/common_flutter.dart - ref: dart2_3 + ref: dart3a path: packages/lints_flutter version: '>=0.1.0' tekartik_build_menu_flutter: git: url: https://github.com/tekartik/build_flutter.dart - ref: dart2_3 + ref: dart3a path: packages/build_menu_flutter version: '>=0.1.0' dependency_overrides: @@ -132,4 +132,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/bluetooth_web/README.md b/bluetooth_web/README.md index d4140a1..8ec4705 100644 --- a/bluetooth_web/README.md +++ b/bluetooth_web/README.md @@ -10,6 +10,6 @@ dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth_web - ref: dart2_3 + ref: dart3a version: '>=0.2.1' ``` \ No newline at end of file diff --git a/bluetooth_web/pubspec.yaml b/bluetooth_web/pubspec.yaml index ecd3ba9..f554939 100644 --- a/bluetooth_web/pubspec.yaml +++ b/bluetooth_web/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: @@ -15,7 +15,7 @@ dependencies: git: url: https://github.com/tekartik/bluetooth.dart path: bluetooth - ref: dart2_3 + ref: dart3a version: '>=0.2.1' dependency_overrides: @@ -31,5 +31,5 @@ dev_dependencies: git: url: https://github.com/tekartik/test_menu.dart path: test_menu_browser - ref: dart2_3 - version: '>=0.4.7' \ No newline at end of file + ref: dart3a + version: '>=0.4.7' diff --git a/example/java_objc_app/pubspec.yaml b/example/java_objc_app/pubspec.yaml index 79a3877..68e1d91 100644 --- a/example/java_objc_app/pubspec.yaml +++ b/example/java_objc_app/pubspec.yaml @@ -15,7 +15,7 @@ version: 1.0.0+1 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dependencies: flutter: @@ -30,7 +30,7 @@ dependencies: tekartik_test_menu_flutter: git: url: https://github.com/tekartik/test_menu_flutter - ref: dart2_3 + ref: dart3a path: test_menu_flutter version: '>=0.2.5' @@ -44,7 +44,7 @@ dev_dependencies: tekartik_lints_flutter: git: url: https://github.com/tekartik/common_flutter.dart - ref: dart2_3 + ref: dart3a path: packages/lints_flutter version: '>=0.1.0' @@ -88,4 +88,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/repo_support/pubspec.yaml b/repo_support/pubspec.yaml index 29478e6..b35e417 100644 --- a/repo_support/pubspec.yaml +++ b/repo_support/pubspec.yaml @@ -4,21 +4,16 @@ version: 0.1.0 publish_to: none environment: - sdk: '>=2.18.0 <3.0.0' + sdk: '>=3.0.0 <4.0.0' dev_dependencies: path: - tekartik_travis_ci_flutter: - git: - url: https://github.com/tekartik/travis_ci_flutter.dart - ref: dart2_3 - version: '>=0.1.0' process_run: '>=0.11.0+2' dev_test: '>=0.13.4-dev.2' test: tekartik_lints: git: url: https://github.com/tekartik/common.dart - ref: dart2_3 + ref: dart3a path: packages/lints - version: '>=0.1.0' \ No newline at end of file + version: '>=0.1.0'