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 <navidecklabs@users.noreply.github.com>

* Update changelog

---------

Co-authored-by: Rohit Sangwan <rohitsangwan647@gmail.com>
Co-authored-by: navidecklabs <navidecklabs@users.noreply.github.com>
This commit is contained in:
Foti Dim
2024-05-09 11:48:32 +02:00
committed by GitHub
parent 3be945c3d5
commit fef4e8526b
10 changed files with 88 additions and 26 deletions
+7
View File
@@ -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 ## 0.9.8
* Improve manufacturer data discovery on Windows * Improve manufacturer data discovery on Windows
* Example app improvements * Example app improvements
@@ -238,7 +238,7 @@ fun BluetoothDevice.removeBond() {
try { try {
javaClass.getMethod("removeBond").invoke(this) javaClass.getMethod("removeBond").invoke(this)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Removing bond has been failed. ${e.message}") Log.e(TAG, "Removing bond failed. ${e.message}")
} }
} }
@@ -665,6 +665,15 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
override fun onScanResult(callbackType: Int, result: ScanResult) { override fun onScanResult(callbackType: Int, result: ScanResult) {
// Log.v(TAG, "onScanResult: $result") // Log.v(TAG, "onScanResult: $result")
var serviceUuids: Array<String> = arrayOf<String>()
result.device.uuids?.forEach {
serviceUuids += it.uuid.toString()
}
result.scanRecord?.serviceUuids?.forEach {
if (!serviceUuids.contains(it.uuid.toString())) {
serviceUuids += it.uuid.toString()
}
}
mainThreadHandler?.post { mainThreadHandler?.post {
callbackChannel?.onScanResult( callbackChannel?.onScanResult(
UniversalBleScanResult( UniversalBleScanResult(
@@ -673,13 +682,12 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
isPaired = result.device.bondState == BOND_BONDED, isPaired = result.device.bondState == BOND_BONDED,
manufacturerDataHead = result.manufacturerDataHead, manufacturerDataHead = result.manufacturerDataHead,
rssi = result.rssi.toLong(), rssi = result.rssi.toLong(),
services = result.device.uuids?.map { it.uuid?.toString() ?: "" } services = serviceUuids.toList()
?: emptyList()
) )
) {} ) {}
} }
} }
override fun onBatchScanResults(results: MutableList<ScanResult>?) { override fun onBatchScanResults(results: MutableList<ScanResult>?) {
Log.v(TAG, "onBatchScanResults: $results") Log.v(TAG, "onBatchScanResults: $results")
} }
+41 -6
View File
@@ -95,24 +95,51 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
func cleanUpConnection(deviceId: String) { func cleanUpConnection(deviceId: String) {
characteristicReadFutures.removeAll { future in 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 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 discoveredServicesProgressMap[deviceId] = nil
} }
func discoverServices(deviceId: String, completion: @escaping (Result<[UniversalBleService], Error>) -> Void) { func discoverServices(deviceId: String, completion: @escaping (Result<[UniversalBleService], Error>) -> Void) {
guard let peripheral = discoveredPeripherals[deviceId] else { guard let peripheral = discoveredPeripherals[deviceId] else {
completion(Result.failure( completion(
FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil))) Result.failure(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil))
)
return return
} }
if discoveredServicesProgressMap[deviceId] != nil { 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 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) peripheral.discoverServices(nil)
discoverServicesFutures.append(DiscoverServicesFuture(deviceId: deviceId, result: completion)) 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?) { public func centralManager(_: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error _: Error?) {
callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, state: BlueConnectionState.disconnected.rawValue) { _ in } 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?) { 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) } discoveredServicesProgressMap[deviceId] = services.map { UniversalBleService(uuid: $0.uuid.uuidString, characteristics: nil) }
for service in services { 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) 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?) { 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 // Update futures for writeValue
characteristicWriteFutures.removeAll { future in characteristicWriteFutures.removeAll { future in
if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr { if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr {
+2 -1
View File
@@ -225,7 +225,8 @@ class _MyAppState extends State<MyApp> {
itemCount: _scanResults.length, itemCount: _scanResults.length,
separatorBuilder: (context, index) => const Divider(), separatorBuilder: (context, index) => const Divider(),
itemBuilder: (context, index) { itemBuilder: (context, index) {
BleScanResult scanResult = _scanResults[index]; BleScanResult scanResult =
_scanResults[_scanResults.length - index - 1];
return ScannedItemWidget( return ScannedItemWidget(
scanResult: scanResult, scanResult: scanResult,
onTap: () { onTap: () {
+1 -1
View File
@@ -26,4 +26,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
COCOAPODS: 1.14.3 COCOAPODS: 1.15.2
@@ -259,7 +259,7 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0920; LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1430; LastUpgradeCheck = 1510;
ORGANIZATIONNAME = ""; ORGANIZATIONNAME = "";
TargetAttributes = { TargetAttributes = {
331C80D4294CF70F00263BE5 = { 331C80D4294CF70F00263BE5 = {
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1430" LastUpgradeVersion = "1510"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
+1 -1
View File
@@ -1,6 +1,6 @@
name: universal_ble name: universal_ble
description: A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE) plugin for Flutter description: A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE) plugin for Flutter
version: 0.9.8 version: 0.9.9
homepage: https://navideck.com homepage: https://navideck.com
repository: https://github.com/Navideck/universal_ble repository: https://github.com/Navideck/universal_ble
issue_tracker: https://github.com/Navideck/universal_ble/issues issue_tracker: https://github.com/Navideck/universal_ble/issues
+22 -11
View File
@@ -554,26 +554,37 @@ namespace universal_ble
auto it = scanResults.find(scanResult.device_id()); auto it = scanResults.find(scanResult.device_id());
if (it != scanResults.end()) if (it != scanResults.end())
{ {
UniversalBleScanResult &existingScanResult = it->second; UniversalBleScanResult &currentScanResult = it->second;
bool shouldUpdate = false; 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; 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; 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; 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; shouldUpdate = true;
} }
@@ -581,8 +592,8 @@ namespace universal_ble
if (!shouldUpdate) if (!shouldUpdate)
return; return;
// update the existing scan result // update the current scan result
existingScanResult = scanResult; currentScanResult = scanResult;
} }
else else
{ {