From fef4e8526b197824a456d9c0504b799aa468ddbd Mon Sep 17 00:00:00 2001 From: Foti Dim Date: Thu, 9 May 2024 11:48:32 +0200 Subject: [PATCH] Improve Apple, Android and Windows (#40) * Cleanup connection * Reverse search list * Bump version * Fix Android serviceUuids in scanResults * Fix typo * Improve cleanup on apple * Improve Apple reconnection and service discovery * Persist long scan result name on Windows Co-authored-by: navidecklabs * Update changelog --------- Co-authored-by: Rohit Sangwan Co-authored-by: navidecklabs --- CHANGELOG.md | 7 +++ .../universal_ble/UniversalBleHelper.kt | 2 +- .../universal_ble/UniversalBlePlugin.kt | 14 ++++-- darwin/Classes/UniversalBlePlugin.swift | 47 ++++++++++++++++--- example/lib/home/home.dart | 3 +- example/macos/Podfile.lock | 2 +- .../macos/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- pubspec.yaml | 2 +- windows/src/universal_ble_plugin.cpp | 33 ++++++++----- 10 files changed, 88 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c37aac..b501fb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.9.9 +* Improve service discovery on Apple +* Improve reconnection on Apple +* Persist long scan result name on Windows +* Fix Android serviceUuids in scanResults +* Example app improvements + ## 0.9.8 * Improve manufacturer data discovery on Windows * Example app improvements diff --git a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt index 8458962..b224a07 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBleHelper.kt @@ -238,7 +238,7 @@ fun BluetoothDevice.removeBond() { try { javaClass.getMethod("removeBond").invoke(this) } catch (e: Exception) { - Log.e(TAG, "Removing bond has been failed. ${e.message}") + Log.e(TAG, "Removing bond failed. ${e.message}") } } diff --git a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt index f25c0ad..78580fa 100644 --- a/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt +++ b/android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt @@ -665,6 +665,15 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), override fun onScanResult(callbackType: Int, result: ScanResult) { // Log.v(TAG, "onScanResult: $result") + var serviceUuids: Array = arrayOf() + result.device.uuids?.forEach { + serviceUuids += it.uuid.toString() + } + result.scanRecord?.serviceUuids?.forEach { + if (!serviceUuids.contains(it.uuid.toString())) { + serviceUuids += it.uuid.toString() + } + } mainThreadHandler?.post { callbackChannel?.onScanResult( UniversalBleScanResult( @@ -673,13 +682,12 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), isPaired = result.device.bondState == BOND_BONDED, manufacturerDataHead = result.manufacturerDataHead, rssi = result.rssi.toLong(), - services = result.device.uuids?.map { it.uuid?.toString() ?: "" } - ?: emptyList() + services = serviceUuids.toList() ) ) {} } } - + override fun onBatchScanResults(results: MutableList?) { Log.v(TAG, "onBatchScanResults: $results") } diff --git a/darwin/Classes/UniversalBlePlugin.swift b/darwin/Classes/UniversalBlePlugin.swift index 958dd23..a034378 100644 --- a/darwin/Classes/UniversalBlePlugin.swift +++ b/darwin/Classes/UniversalBlePlugin.swift @@ -95,24 +95,51 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral func cleanUpConnection(deviceId: String) { characteristicReadFutures.removeAll { future in - future.deviceId == deviceId + if future.deviceId == deviceId { + future.result( + Result.failure(FlutterError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil)) + ) + return true + } + return false } discoverServicesFutures.removeAll { future in - future.deviceId == deviceId + if future.deviceId == deviceId { + future.result( + Result.failure(FlutterError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil)) + ) + return true + } + return false } discoveredServicesProgressMap[deviceId] = nil } func discoverServices(deviceId: String, completion: @escaping (Result<[UniversalBleService], Error>) -> Void) { guard let peripheral = discoveredPeripherals[deviceId] else { - completion(Result.failure( - FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil))) + completion( + Result.failure(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)) + ) return } + if discoveredServicesProgressMap[deviceId] != nil { - completion(Result.failure(FlutterError(code: "AlreadyInProgress", message: "Services discovery already in progress for :\(deviceId)", details: nil))) + print("Services discovery already in progress for :\(deviceId), waiting for completion.") + discoverServicesFutures.append(DiscoverServicesFuture(deviceId: deviceId, result: completion)) return } + + if let cachedServices = peripheral.services { + // If services already discovered no need to discover again + if !cachedServices.isEmpty { + // print("Services already cached for this peripheral") + discoverServicesFutures.append(DiscoverServicesFuture(deviceId: deviceId, result: completion)) + self.peripheral(peripheral, didDiscoverServices: nil) + return + } + } + + peripheral.discoverServices(nil) discoverServicesFutures.append(DiscoverServicesFuture(deviceId: deviceId, result: completion)) } @@ -270,6 +297,8 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral public func centralManager(_: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error _: Error?) { callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, state: BlueConnectionState.disconnected.rawValue) { _ in } + // Cleanup on disconnect + cleanUpConnection(deviceId: peripheral.uuid.uuidString) } public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices _: Error?) { @@ -280,6 +309,12 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral } discoveredServicesProgressMap[deviceId] = services.map { UniversalBleService(uuid: $0.uuid.uuidString, characteristics: nil) } for service in services { + if let cachedChar = service.characteristics { + if !cachedChar.isEmpty { + self.peripheral(peripheral, didDiscoverCharacteristicsFor: service, error: nil) + continue + } + } peripheral.discoverCharacteristics(nil, for: service) } } @@ -303,7 +338,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral } public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { - print("peripheral:didWriteValueForCharacteristic \(characteristic.uuid.uuidStr) error: \(String(describing: error))") + // print("peripheral:didWriteValueForCharacteristic \(characteristic.uuid.uuidStr) error: \(String(describing: error))") // Update futures for writeValue characteristicWriteFutures.removeAll { future in if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr { diff --git a/example/lib/home/home.dart b/example/lib/home/home.dart index 65c6f05..19ab76e 100644 --- a/example/lib/home/home.dart +++ b/example/lib/home/home.dart @@ -225,7 +225,8 @@ class _MyAppState extends State { itemCount: _scanResults.length, separatorBuilder: (context, index) => const Divider(), itemBuilder: (context, index) { - BleScanResult scanResult = _scanResults[index]; + BleScanResult scanResult = + _scanResults[_scanResults.length - index - 1]; return ScannedItemWidget( scanResult: scanResult, onTap: () { diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 7cabaf3..c285c03 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -26,4 +26,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index 57008be..ea4127b 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -259,7 +259,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C80D4294CF70F00263BE5 = { diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c60a4dd..793b0cf 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ second; + UniversalBleScanResult ¤tScanResult = it->second; bool shouldUpdate = false; - if ((scanResult.name() == nullptr || scanResult.name()->empty()) && (existingScanResult.name() != nullptr && !existingScanResult.name()->empty())) + + // Check if current scanResult name is longer than the received scanResult name + if (scanResult.name() != nullptr && !scanResult.name()->empty() && currentScanResult.name() != nullptr && !currentScanResult.name()->empty()) { - scanResult.set_name(*existingScanResult.name()); + if (currentScanResult.name()->size() > scanResult.name()->size()) + { + scanResult.set_name(*currentScanResult.name()); + } + } + + if ((scanResult.name() == nullptr || scanResult.name()->empty()) && (currentScanResult.name() != nullptr && !currentScanResult.name()->empty())) + { + scanResult.set_name(*currentScanResult.name()); shouldUpdate = true; } - if (scanResult.is_paired() == nullptr && existingScanResult.is_paired() != nullptr) + + if (scanResult.is_paired() == nullptr && currentScanResult.is_paired() != nullptr) { - scanResult.set_is_paired(existingScanResult.is_paired()); + scanResult.set_is_paired(currentScanResult.is_paired()); shouldUpdate = true; } - if (scanResult.manufacturer_data_head() == nullptr && existingScanResult.manufacturer_data_head() != nullptr) + if (scanResult.manufacturer_data_head() == nullptr && currentScanResult.manufacturer_data_head() != nullptr) { - scanResult.set_manufacturer_data_head(existingScanResult.manufacturer_data_head()); + scanResult.set_manufacturer_data_head(currentScanResult.manufacturer_data_head()); shouldUpdate = true; } - if (scanResult.services() == nullptr && existingScanResult.services() != nullptr) + if (scanResult.services() == nullptr && currentScanResult.services() != nullptr) { - scanResult.set_services(existingScanResult.services()); + scanResult.set_services(currentScanResult.services()); shouldUpdate = true; } @@ -581,8 +592,8 @@ namespace universal_ble if (!shouldUpdate) return; - // update the existing scan result - existingScanResult = scanResult; + // update the current scan result + currentScanResult = scanResult; } else {