Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions packages/quick_blue/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<!-- legacy for Android 11 or lower -->
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- legacy for Android 9 or lower -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ArrayList<String>>("serviceUUIDs")!!
if (serviceUUIDs != null && serviceUUIDs.size > 0) {
val filters: ArrayList<ScanFilter> = 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<ArrayList<String>>("serviceUUIDs")
if (serviceUUIDs != null && serviceUUIDs.size > 0) {
val filters: ArrayList<ScanFilter> = 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<String>("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<String>("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<String>("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<String>("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<String>("deviceId")!!
val service = call.argument<String>("service")!!
val characteristic = call.argument<String>("characteristic")!!
val bleInputProperty = call.argument<String>("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<String>("deviceId")!!
val service = call.argument<String>("service")!!
val characteristic = call.argument<String>("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<String>("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<String>("deviceId")!!
val service = call.argument<String>("service")!!
val characteristic = call.argument<String>("characteristic")!!
val value = call.argument<ByteArray>("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<String>("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<String>("deviceId")!!
val expectedMtu = call.argument<Int>("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<String>("deviceId")!!
val service = call.argument<String>("service")!!
val characteristic = call.argument<String>("characteristic")!!
val bleInputProperty = call.argument<String>("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<String>("deviceId")!!
val service = call.argument<String>("service")!!
val characteristic = call.argument<String>("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<String>("deviceId")!!
val service = call.argument<String>("service")!!
val characteristic = call.argument<String>("characteristic")!!
val value = call.argument<ByteArray>("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<String>("deviceId")!!
val expectedMtu = call.argument<Int>("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())
}
}

Expand Down Expand Up @@ -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")

Expand All @@ -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"

Expand Down
3 changes: 1 addition & 2 deletions packages/quick_blue/lib/src/method_channel_quick_blue.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ class MethodChannelQuickBlue extends QuickBluePlatform {
if (message['ServiceState'] == 'discovered') {
String deviceId = message['deviceId'];
String service = message['service'];
List<String> characteristics =
(message['characteristics'] as List).cast();
List<String> characteristics = ((message['characteristics'] ?? []) as List).cast();
onServiceDiscovered?.call(deviceId, service, characteristics);
}
} else if (message['characteristicValue'] != null) {
Expand Down
8 changes: 8 additions & 0 deletions packages/quick_blue/windows/quick_blue_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
#include <algorithm>
#include <iomanip>

#include <codecvt>
#include <locale>
#include <iostream>

#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]

Expand Down Expand Up @@ -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)
{
Expand Down