From ce4491fa40d681fe4173f663b7e822f15cf6763a Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Sat, 16 Jul 2022 21:56:37 +0200 Subject: [PATCH 1/7] Add `FlutterBlue.name` to get the human readable device name This commit implements a new `FlutterBlue.name` property that will get the friendly bluetooth name of the local bluetooth adapter. --- .../boskokg/flutter_blue_plus/FlutterBluePlusPlugin.java | 9 +++++++++ ios/Classes/FlutterBluePlusPlugin.m | 2 ++ lib/src/flutter_blue_plus.dart | 4 ++++ 3 files changed, 15 insertions(+) diff --git a/android/src/main/java/com/boskokg/flutter_blue_plus/FlutterBluePlusPlugin.java b/android/src/main/java/com/boskokg/flutter_blue_plus/FlutterBluePlusPlugin.java index 86512000..dd4b7917 100644 --- a/android/src/main/java/com/boskokg/flutter_blue_plus/FlutterBluePlusPlugin.java +++ b/android/src/main/java/com/boskokg/flutter_blue_plus/FlutterBluePlusPlugin.java @@ -227,6 +227,15 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { break; } + case "name": + { + String name = mBluetoothAdapter.getName(); + if (name == null) + name = ""; + result.success(name); + break; + } + case "turnOn": { if (!mBluetoothAdapter.isEnabled()) { diff --git a/ios/Classes/FlutterBluePlusPlugin.m b/ios/Classes/FlutterBluePlusPlugin.m index 3d431b5f..92d8fc8e 100644 --- a/ios/Classes/FlutterBluePlusPlugin.m +++ b/ios/Classes/FlutterBluePlusPlugin.m @@ -86,6 +86,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else { result(@(NO)); } + } else if([@"name" isEqualToString:call.method]) { + result([[UIDevice currentDevice] name]); } else if([@"startScan" isEqualToString:call.method]) { // Clear any existing scan results [self.scannedPeripherals removeAllObjects]; diff --git a/lib/src/flutter_blue_plus.dart b/lib/src/flutter_blue_plus.dart index a063e25d..57b92960 100644 --- a/lib/src/flutter_blue_plus.dart +++ b/lib/src/flutter_blue_plus.dart @@ -34,6 +34,10 @@ class FlutterBluePlus { Future get isAvailable => _channel.invokeMethod('isAvailable').then((d) => d); + /// Return the friendly Bluetooth name of the local Bluetooth adapter + Future get name => + _channel.invokeMethod('name').then((d) => d); + /// Checks if Bluetooth functionality is turned on Future get isOn => _channel.invokeMethod('isOn').then((d) => d); From d405bcb4fe1aa59c093adca9cd8c5af0e6093a7d Mon Sep 17 00:00:00 2001 From: Bosko Popovic Date: Sat, 23 Jul 2022 20:49:43 +0200 Subject: [PATCH 2/7] connect timeout fixed (thanks to crazy-rodney, sophisticode, SkuggaEdward, MousyBusiness and cthurston) --- CHANGELOG.md | 3 +++ example/pubspec.lock | 16 ++++++++-------- lib/src/bluetooth_device.dart | 30 +++++++++++++----------------- pubspec.yaml | 2 +- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9471eed..b52a7aef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 1.2.0 +* connect timeout fixed (thanks to crazy-rodney, sophisticode, SkuggaEdward, MousyBusiness and cthurston) + ## 1.1.3 * Read RSSI from a connected BLE device #1 (thanks to sophisticode) * Fixed a crash on Android OS 12 (added check for BLUETOOTH_CONNECT permission) (fixed by dspells) diff --git a/example/pubspec.lock b/example/pubspec.lock index 80661d3a..8d704694 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -42,7 +42,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" + version: "1.16.0" convert: dependency: transitive description: @@ -63,7 +63,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" fixnum: dependency: transitive description: @@ -115,7 +115,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "0.1.4" meta: dependency: transitive description: @@ -129,7 +129,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" protobuf: dependency: transitive description: @@ -155,7 +155,7 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" + version: "1.8.2" stack_trace: dependency: transitive description: @@ -190,7 +190,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" + version: "0.4.9" typed_data: dependency: transitive description: @@ -204,7 +204,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.2" sdks: - dart: ">=2.15.1 <3.0.0" + dart: ">=2.17.0-0 <3.0.0" flutter: ">=2.5.0" diff --git a/lib/src/bluetooth_device.dart b/lib/src/bluetooth_device.dart index f276ec7f..993d0208 100644 --- a/lib/src/bluetooth_device.dart +++ b/lib/src/bluetooth_device.dart @@ -32,30 +32,26 @@ class BluetoothDevice { Duration? timeout, bool autoConnect = true, }) async { - final completer = Completer(); var request = protos.ConnectRequest.create() ..remoteId = id.toString() ..androidAutoConnect = autoConnect; - Timer? timer; - if (timeout != null) { - timer = Timer(timeout, () { - disconnect(); - completer.completeError( - TimeoutException('Failed to connect in time.', timeout)); - }); - } - await FlutterBluePlus.instance._channel .invokeMethod('connect', request.writeToBuffer()); - await state.firstWhere((s) => s == BluetoothDeviceState.connected); - - timer?.cancel(); - - completer.complete(); - - return completer.future; + if (timeout == null) { + await state.firstWhere((s) => s == BluetoothDeviceState.connected); + } else { + await state + .firstWhere((s) => s == BluetoothDeviceState.connected) + .timeout( + timeout, + onTimeout: () { + disconnect(); + throw TimeoutException('Failed to connect in time.', timeout); + }, + ); + } } /// Cancels connection to the Bluetooth Device diff --git a/pubspec.yaml b/pubspec.yaml index 6c229e84..7461ee4f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_blue_plus description: Flutter plugin for connecting and communicationg with Bluetooth Low Energy devices, on Android and iOS -version: 1.1.3 +version: 1.2.0 homepage: https://github.com/boskokg/flutter_blue_plus environment: From c2488a2da29c8b3a40cca09c8a1199e68916f3f2 Mon Sep 17 00:00:00 2001 From: Bosko Popovic Date: Sat, 23 Jul 2022 21:02:50 +0200 Subject: [PATCH 3/7] Add timestamp field to ScanResult class --- CHANGELOG.md | 1 + lib/src/flutter_blue_plus.dart | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b52a7aef..481a83e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## 1.2.0 * connect timeout fixed (thanks to crazy-rodney, sophisticode, SkuggaEdward, MousyBusiness and cthurston) +* Add timestamp field to ScanResult class #59 (thanks to simon-iversen) ## 1.1.3 * Read RSSI from a connected BLE device #1 (thanks to sophisticode) diff --git a/lib/src/flutter_blue_plus.dart b/lib/src/flutter_blue_plus.dart index a063e25d..62cd1354 100644 --- a/lib/src/flutter_blue_plus.dart +++ b/lib/src/flutter_blue_plus.dart @@ -280,11 +280,13 @@ class ScanResult { ScanResult.fromProto(protos.ScanResult p) : device = BluetoothDevice.fromProto(p.device), advertisementData = AdvertisementData.fromProto(p.advertisementData), - rssi = p.rssi; + rssi = p.rssi, + timeStamp = DateTime.now(); final BluetoothDevice device; final AdvertisementData advertisementData; final int rssi; + final DateTime timeStamp; @override bool operator ==(Object other) => @@ -298,7 +300,7 @@ class ScanResult { @override String toString() { - return 'ScanResult{device: $device, advertisementData: $advertisementData, rssi: $rssi}'; + return 'ScanResult{device: $device, advertisementData: $advertisementData, rssi: $rssi, timeStamp: $timeStamp}'; } } From 04ae5aa9116c5954e63da73f7fd45ef75d3c9516 Mon Sep 17 00:00:00 2001 From: Bosko Popovic Date: Sat, 23 Jul 2022 21:25:32 +0200 Subject: [PATCH 4/7] Changelog updated for: Add FlutterBlue.name to get the human readable device name #93 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 481a83e2..3c767924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 1.2.0 * connect timeout fixed (thanks to crazy-rodney, sophisticode, SkuggaEdward, MousyBusiness and cthurston) * Add timestamp field to ScanResult class #59 (thanks to simon-iversen) +* Add FlutterBlue.name to get the human readable device name #93 (thanks to mvo5) ## 1.1.3 * Read RSSI from a connected BLE device #1 (thanks to sophisticode) From f45844848ab2042cb5f4c4b7fe6421e6feca36c7 Mon Sep 17 00:00:00 2001 From: Bosko Popovic Date: Sat, 23 Jul 2022 21:36:32 +0200 Subject: [PATCH 5/7] Add android pairing solution #95 --- .../boskokg/flutter_blue_plus/FlutterBluePlusPlugin.java | 9 +++++++++ lib/flutter_blue_plus.dart | 1 + lib/src/bluetooth_device.dart | 9 +++++++++ 3 files changed, 19 insertions(+) diff --git a/android/src/main/java/com/boskokg/flutter_blue_plus/FlutterBluePlusPlugin.java b/android/src/main/java/com/boskokg/flutter_blue_plus/FlutterBluePlusPlugin.java index dd4b7917..2602e231 100644 --- a/android/src/main/java/com/boskokg/flutter_blue_plus/FlutterBluePlusPlugin.java +++ b/android/src/main/java/com/boskokg/flutter_blue_plus/FlutterBluePlusPlugin.java @@ -359,6 +359,15 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { break; } + case "pair": + { + String deviceId = (String)call.arguments; + BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(deviceId); + device.createBond(); + result.success(null); + break; + } + case "disconnect": { String deviceId = (String)call.arguments; diff --git a/lib/flutter_blue_plus.dart b/lib/flutter_blue_plus.dart index 251519aa..0692fe56 100644 --- a/lib/flutter_blue_plus.dart +++ b/lib/flutter_blue_plus.dart @@ -5,6 +5,7 @@ library flutter_blue_plus; import 'dart:async'; +import 'dart:io'; import 'package:collection/collection.dart'; import 'package:convert/convert.dart'; diff --git a/lib/src/bluetooth_device.dart b/lib/src/bluetooth_device.dart index 993d0208..a4a739a7 100644 --- a/lib/src/bluetooth_device.dart +++ b/lib/src/bluetooth_device.dart @@ -54,6 +54,15 @@ class BluetoothDevice { } } + /// Send a pairing request to the device. + /// Currently only implemented on Android. + Future pair() async { + if (Platform.isAndroid) { + return FlutterBluePlus.instance._channel + .invokeMethod('pair', id.toString()); + } + } + /// Cancels connection to the Bluetooth Device Future disconnect() => FlutterBluePlus.instance._channel .invokeMethod('disconnect', id.toString()); From 362fb166430cfecb92134b553a1ff48a583abaf8 Mon Sep 17 00:00:00 2001 From: Bosko Popovic Date: Sat, 23 Jul 2022 21:36:55 +0200 Subject: [PATCH 6/7] example pubspec.lock updated --- example/pubspec.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 8d704694..163f5f5b 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -82,7 +82,7 @@ packages: path: ".." relative: true source: path - version: "1.1.3" + version: "1.2.0" flutter_lints: dependency: "direct dev" description: From 9abbd7a8a633179800590c774405a12a09d24f15 Mon Sep 17 00:00:00 2001 From: Bosko Popovic Date: Sat, 23 Jul 2022 22:38:11 +0200 Subject: [PATCH 7/7] Fix FlutterBlue.state stream cancel --- CHANGELOG.md | 1 + lib/src/flutter_blue_plus.dart | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c767924..4a80d873 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * connect timeout fixed (thanks to crazy-rodney, sophisticode, SkuggaEdward, MousyBusiness and cthurston) * Add timestamp field to ScanResult class #59 (thanks to simon-iversen) * Add FlutterBlue.name to get the human readable device name #93 (thanks to mvo5) +* Fix bug where if there were multiple subscribers to FlutterBlue.state and one cancelled it would accidentally cancel all subscribers (thank to MacMalainey and MrCsabaToth) ## 1.1.3 * Read RSSI from a connected BLE device #1 (thanks to sophisticode) diff --git a/lib/src/flutter_blue_plus.dart b/lib/src/flutter_blue_plus.dart index 055e24c5..47614d32 100644 --- a/lib/src/flutter_blue_plus.dart +++ b/lib/src/flutter_blue_plus.dart @@ -14,6 +14,12 @@ class FlutterBluePlus { Stream get _methodStream => _methodStreamController .stream; // Used internally to dispatch methods from platform. + /// Cached broadcast stream for FlutterBlue.state events + /// Caching this stream allows for more than one listener to subscribe + /// and unsubscribe apart from each other, + /// while allowing events to still be sent to others that are subscribed + Stream? _stateStream; + /// Singleton boilerplate FlutterBluePlus._() { _channel.setMethodCallHandler((MethodCall call) async { @@ -87,10 +93,13 @@ class FlutterBluePlus { .then((buffer) => protos.BluetoothState.fromBuffer(buffer)) .then((s) => BluetoothState.values[s.state.value]); - yield* _stateChannel + _stateStream ??= _stateChannel .receiveBroadcastStream() .map((buffer) => protos.BluetoothState.fromBuffer(buffer)) - .map((s) => BluetoothState.values[s.state.value]); + .map((s) => BluetoothState.values[s.state.value]) + .doOnCancel(() => _stateStream = null); + + yield* _stateStream!; } /// Retrieve a list of connected devices