diff --git a/packages/quick_blue/android/src/main/AndroidManifest.xml b/packages/quick_blue/android/src/main/AndroidManifest.xml index 919bc9c..6dbadaa 100644 --- a/packages/quick_blue/android/src/main/AndroidManifest.xml +++ b/packages/quick_blue/android/src/main/AndroidManifest.xml @@ -5,10 +5,8 @@ - - + + diff --git a/packages/quick_blue/android/src/main/kotlin/com/example/quick_blue/QuickBluePlugin.kt b/packages/quick_blue/android/src/main/kotlin/com/example/quick_blue/QuickBluePlugin.kt index 7193b9b..b0a1761 100644 --- a/packages/quick_blue/android/src/main/kotlin/com/example/quick_blue/QuickBluePlugin.kt +++ b/packages/quick_blue/android/src/main/kotlin/com/example/quick_blue/QuickBluePlugin.kt @@ -80,120 +80,128 @@ class QuickBluePlugin: FlutterPlugin, MethodCallHandler, EventChannel.StreamHand fun trace() = Arrays.toString(Throwable().stackTrace) override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { - when (call.method) { - "isBluetoothAvailable" -> { - result.success(bluetoothManager.adapter.isEnabled) - } - "startScan" -> { - val serviceUUIDs = call.argument>("serviceUUIDs")!! - if (serviceUUIDs != null && serviceUUIDs.size > 0) { - val filters: ArrayList = ArrayList() - for (serviceUUID in serviceUUIDs) { - val filter = ScanFilter.Builder() - .setServiceUuid(parseToParcelUuid(serviceUUID)) + try { + when (call.method) { + "isBluetoothAvailable" -> { + result.success(bluetoothManager.adapter.isEnabled) + } + "startScan" -> { + val serviceUUIDs = call.argument>("serviceUUIDs") + if (serviceUUIDs != null && serviceUUIDs.size > 0) { + val filters: ArrayList = ArrayList() + for (serviceUUID in serviceUUIDs) { + val filter = ScanFilter.Builder() + .setServiceUuid(parseToParcelUuid(serviceUUID)) + .build() + filters.add(filter) + } + + val settings = ScanSettings.Builder() + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .build() - filters.add(filter) - } - - val settings = ScanSettings.Builder() - .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) - .build() - bluetoothManager.adapter.bluetoothLeScanner?.startScan(filters, settings, scanCallback) - } else { - bluetoothManager.adapter.bluetoothLeScanner?.startScan(scanCallback) + bluetoothManager.adapter.bluetoothLeScanner?.startScan(filters, settings, scanCallback) + } else { + bluetoothManager.adapter.bluetoothLeScanner?.startScan(scanCallback) + } + result.success(null) } - result.success(null) - } - "stopScan" -> { - bluetoothManager.adapter.bluetoothLeScanner?.stopScan(scanCallback) - result.success(null) - } - "connect" -> { - val deviceId = call.argument("deviceId")!! - if (knownGatts.find { it.device.address == deviceId } != null) { - return result.success(null) + "stopScan" -> { + bluetoothManager.adapter.bluetoothLeScanner?.stopScan(scanCallback) + result.success(null) } - val remoteDevice = bluetoothManager.adapter.getRemoteDevice(deviceId) - val gatt = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - remoteDevice.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE) - } else { - remoteDevice.connectGatt(context, false, gattCallback) + "connect" -> { + val deviceId = call.argument("deviceId")!! + if (knownGatts.find { it.device.address == deviceId } != null) { + return result.success(null) + } + val remoteDevice = bluetoothManager.adapter.getRemoteDevice(deviceId) + val gatt = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + remoteDevice.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE) + } else { + remoteDevice.connectGatt(context, false, gattCallback) + } + knownGatts.add(gatt) + result.success(null) + // TODO connecting } - knownGatts.add(gatt) - result.success(null) - // TODO connecting - } - "disconnect" -> { - val deviceId = call.argument("deviceId")!! - val gatt = knownGatts.find { it.device.address == deviceId } - ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) - cleanConnection(gatt) - result.success(null) - //FIXME If `disconnect` is called before BluetoothGatt.STATE_CONNECTED - // there will be no `disconnected` message any more - } - "discoverServices" -> { - val deviceId = call.argument("deviceId")!! - val gatt = knownGatts.find { it.device.address == deviceId } - ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) - gatt.discoverServices() - result.success(null) - } - "setNotifiable" -> { - val deviceId = call.argument("deviceId")!! - val service = call.argument("service")!! - val characteristic = call.argument("characteristic")!! - val bleInputProperty = call.argument("bleInputProperty")!! - val gatt = knownGatts.find { it.device.address == deviceId } - ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) - val c = gatt.getCharacteristic(service, characteristic) - ?: return result.error("IllegalArgument", "Unknown characteristic: $characteristic", trace()) - gatt.setNotifiable(c, bleInputProperty) - result.success(null) - } - "readValue" -> { - val deviceId = call.argument("deviceId")!! - val service = call.argument("service")!! - val characteristic = call.argument("characteristic")!! - val gatt = knownGatts.find { it.device.address == deviceId } - ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) - val c = gatt.getCharacteristic(service, characteristic) - ?: return result.error("IllegalArgument", "Unknown characteristic: $characteristic", trace()) - if (gatt.readCharacteristic(c)) + "disconnect" -> { + val deviceId = call.argument("deviceId")!! + val gatt = knownGatts.find { it.device.address == deviceId } + ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) + cleanConnection(gatt) result.success(null) - else - result.error("Characteristic unavailable", null, trace()) - } - "writeValue" -> { - val deviceId = call.argument("deviceId")!! - val service = call.argument("service")!! - val characteristic = call.argument("characteristic")!! - val value = call.argument("value")!! - val gatt = knownGatts.find { it.device.address == deviceId } - ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) - val c = gatt.getCharacteristic(service, characteristic) - ?: return result.error("IllegalArgument", "Unknown characteristic: $characteristic", trace()) - c.value = value - if (gatt.writeCharacteristic(c)) + //FIXME If `disconnect` is called before BluetoothGatt.STATE_CONNECTED + // there will be no `disconnected` message any more + } + "discoverServices" -> { + val deviceId = call.argument("deviceId")!! + val gatt = knownGatts.find { it.device.address == deviceId } + ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) + gatt.discoverServices() result.success(null) - else - result.error("Characteristic unavailable", null, trace()) - } - "requestMtu" -> { - val deviceId = call.argument("deviceId")!! - val expectedMtu = call.argument("expectedMtu")!! - val gatt = knownGatts.find { it.device.address == deviceId } - ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) - val success = gatt.requestMtu(expectedMtu) - if (success) + } + "setNotifiable" -> { + val deviceId = call.argument("deviceId")!! + val service = call.argument("service")!! + val characteristic = call.argument("characteristic")!! + val bleInputProperty = call.argument("bleInputProperty")!! + val gatt = knownGatts.find { it.device.address == deviceId } + ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) + val s = gatt.getService(UUID.fromString(service)) + ?: return result.error("IllegalArgument", "Unknown service: $service", trace()) + val c = s.getCharacteristic(UUID.fromString(characteristic)) + ?: return result.error("IllegalArgument", "Unknown characteristic: $characteristic", trace()) + gatt.setNotifiable(c, bleInputProperty) result.success(null) - else - result.error("Unable to set MTU", null, trace()) - } - else -> { - result.notImplemented() + } + "readValue" -> { + val deviceId = call.argument("deviceId")!! + val service = call.argument("service")!! + val characteristic = call.argument("characteristic")!! + val gatt = knownGatts.find { it.device.address == deviceId } + ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) + val c = gatt.getCharacteristic(service, characteristic) + ?: return result.error("IllegalArgument", "Unknown characteristic: $characteristic", trace()) + if (gatt.readCharacteristic(c)) + result.success(null) + else + result.error("Characteristic unavailable", null, trace()) + } + "writeValue" -> { + val deviceId = call.argument("deviceId")!! + val service = call.argument("service")!! + val characteristic = call.argument("characteristic")!! + val value = call.argument("value")!! + val gatt = knownGatts.find { it.device.address == deviceId } + ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) + val c = gatt.getCharacteristic(service, characteristic) + ?: return result.error("IllegalArgument", "Unknown characteristic: $characteristic", trace()) + c.value = value + if (gatt.writeCharacteristic(c)) { + result.success(null) + } else { + result.error("Characteristic unavailable", null, trace()) + } + } + "requestMtu" -> { + val deviceId = call.argument("deviceId")!! + val expectedMtu = call.argument("expectedMtu")!! + val gatt = knownGatts.find { it.device.address == deviceId } + ?: return result.error("IllegalArgument", "Unknown deviceId: $deviceId", trace()) + val success = gatt.requestMtu(expectedMtu) + if (success) + result.success(null) + else + result.error("Unable to set MTU", null, trace()) + } + else -> { + result.notImplemented() + } } + } catch (e: Throwable) { + e.printStackTrace() + result.error("Error", "Error", trace()) } } @@ -363,7 +371,7 @@ fun Short.toByteArray(byteOrder: ByteOrder = ByteOrder.LITTLE_ENDIAN): ByteArray ByteBuffer.allocate(2 /*Short.SIZE_BYTES*/).order(byteOrder).putShort(this).array() fun BluetoothGatt.getCharacteristic(service: String, characteristic: String): BluetoothGattCharacteristic? = - getService(UUID.fromString(service)).getCharacteristic(UUID.fromString(characteristic)) + getService(UUID.fromString(service))?.getCharacteristic(UUID.fromString(characteristic)) private val DESC__CLIENT_CHAR_CONFIGURATION = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb") @@ -374,8 +382,10 @@ fun BluetoothGatt.setNotifiable(gattCharacteristic: BluetoothGattCharacteristic, "indication" -> BluetoothGattDescriptor.ENABLE_INDICATION_VALUE to true else -> BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE to false } - descriptor.value = value - setCharacteristicNotification(descriptor.characteristic, enable) && writeDescriptor(descriptor) + if (setCharacteristicNotification(gattCharacteristic, enable) && descriptor != null) { + descriptor.value = value + writeDescriptor(descriptor) + } } const val baseBluetoothUuidPostfix = "0000-1000-8000-00805F9B34FB" diff --git a/packages/quick_blue/lib/src/method_channel_quick_blue.dart b/packages/quick_blue/lib/src/method_channel_quick_blue.dart index ab5d886..8c57a77 100644 --- a/packages/quick_blue/lib/src/method_channel_quick_blue.dart +++ b/packages/quick_blue/lib/src/method_channel_quick_blue.dart @@ -92,8 +92,7 @@ class MethodChannelQuickBlue extends QuickBluePlatform { if (message['ServiceState'] == 'discovered') { String deviceId = message['deviceId']; String service = message['service']; - List characteristics = - (message['characteristics'] as List).cast(); + List characteristics = ((message['characteristics'] ?? []) as List).cast(); onServiceDiscovered?.call(deviceId, service, characteristics); } } else if (message['characteristicValue'] != null) { diff --git a/packages/quick_blue/windows/quick_blue_plugin.cpp b/packages/quick_blue/windows/quick_blue_plugin.cpp index 31d184d..e4331bd 100644 --- a/packages/quick_blue/windows/quick_blue_plugin.cpp +++ b/packages/quick_blue/windows/quick_blue_plugin.cpp @@ -24,6 +24,10 @@ #include #include +#include +#include +#include + #define GUID_FORMAT "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" #define GUID_ARG(guid) guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7] @@ -620,6 +624,10 @@ namespace winrt::fire_and_forget QuickBluePlugin::ConnectAsync(uint64_t bluetoothAddress) { auto device = co_await BluetoothLEDevice::FromBluetoothAddressAsync(bluetoothAddress); + if (!device) { + OutputDebugString((L"ConnectAsync null device! "+ std::to_wstring(bluetoothAddress) +L"\n").c_str()); + co_return; + } auto servicesResult = co_await device.GetGattServicesAsync(); if (servicesResult.Status() != GattCommunicationStatus::Success) {