Update Universal BLE Plugin for Peripheral Support and Code Generation

- Updated autogenerated files to reflect changes from Pigeon v26.3.4.
- Added new classes for UniversalBlePeripheralConfig, UniversalBlePeripheralService, UniversalBlePeripheralCharacteristic, UniversalBlePeripheralDescriptor, and UniversalBlePeripheralWriteEvent to handle peripheral configurations and events.
- Modified UniversalBlePlugin to implement methods for peripheral support, including StartPeripheral, StopPeripheral, UpdatePeripheralCharacteristicValue, and NotifyPeripheralCharacteristic, returning appropriate error messages for unsupported features on Windows.
- Updated HasPermissions method to include an additional parameter for Bluetooth advertising permissions.
- Adjusted method signatures and implementations across the plugin to accommodate new peripheral functionality.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-08 03:36:12 +08:00
parent dde6c084b7
commit 1d7108c9d0
26 changed files with 5522 additions and 1183 deletions
+1
View File
@@ -1,4 +1,5 @@
## 1.3.0
* Add Linux BLE peripheral/GATT-server support through BlueZ GATT application and LE advertisement registration
* Add `requestConnectionPriority` to allow tuning BLE connection intervals on Android
* Add SPM support on Apple
+45
View File
@@ -24,6 +24,7 @@ A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE
- [Connecting](#connecting)
- [Discovering Services](#discovering-services)
- [Reading & Writing data](#reading--writing-data)
- [Peripheral Mode](#peripheral-mode)
- [Pairing](#pairing)
- [Bluetooth Availability](#bluetooth-availability)
- [Requesting MTU](#requesting-mtu)
@@ -57,6 +58,7 @@ A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE
| requestConnectionPriority | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ |
| readRssi | ✔️ | ✔️ | ✔️ | ❌ | 🚧 | ❌ |
| requestPermissions | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| peripheral/GATT server | ✔️ | 🚧 | 🚧 | 🚧 | ✔️ | ❌ |
## Getting Started
@@ -338,6 +340,49 @@ Unsubscribe from notifications and indications of this characteristic.
await characteristic.unsubscribe();
```
## Peripheral Mode
`UniversalBlePeripheral` exposes a local GATT-server API for apps that need to advertise services and accept central/client writes. Android and Linux are implemented; Apple and Windows currently report `notSupported` until their platform server implementations are added. Web browsers do not expose a standard GATT-server API.
On Linux, peripheral mode uses BlueZ `GattManager1` and `LEAdvertisingManager1`. The Bluetooth adapter must be powered, support the peripheral role, and the app process must be allowed to register GATT applications and LE advertisements on the system bus.
```dart
await UniversalBle.requestPermissions(withAndroidBluetoothAdvertise: true);
await UniversalBlePeripheral.start(
BlePeripheralConfig(
advertisedName: 'DALI Relay',
services: [
BlePeripheralService(
uuid: 'fff0',
characteristics: [
BlePeripheralCharacteristic(
uuid: 'fff1',
properties: const [
CharacteristicProperty.read,
CharacteristicProperty.write,
CharacteristicProperty.writeWithoutResponse,
CharacteristicProperty.notify,
],
permissions: const [
BlePeripheralCharacteristicPermission.read,
BlePeripheralCharacteristicPermission.write,
],
),
],
),
],
),
);
UniversalBlePeripheral.writeStream.listen((event) {
debugPrint('Central ${event.deviceId} wrote ${event.value}');
});
await UniversalBlePeripheral.notify('fff0', 'fff1', Uint8List.fromList([1, 2]));
await UniversalBlePeripheral.stop();
```
### Pairing
#### Trigger pairing
+1
View File
@@ -7,4 +7,5 @@
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
</manifest>
@@ -35,12 +35,12 @@ class PermissionHandler(
/**
* Check if we have required permissions
*/
fun hasPermissions(withFineLocation: Boolean): Boolean {
val validationError = validateRequiredPermissions(withFineLocation)
fun hasPermissions(withFineLocation: Boolean, withAdvertise: Boolean): Boolean {
val validationError = validateRequiredPermissions(withFineLocation, withAdvertise)
if (validationError != null) {
throw validationError
}
val permissionsToRequest = getRequiredPermissions(withFineLocation)
val permissionsToRequest = getRequiredPermissions(withFineLocation, withAdvertise)
return permissionsToRequest.isEmpty()
}
@@ -55,17 +55,18 @@ class PermissionHandler(
*/
fun requestPermissions(
withFineLocation: Boolean,
withAdvertise: Boolean,
callback: (Result<Unit>) -> Unit,
) {
// Validate required permissions are declared in manifest
val validationError = validateRequiredPermissions(withFineLocation)
val validationError = validateRequiredPermissions(withFineLocation, withAdvertise)
if (validationError != null) {
callback(Result.failure(validationError))
return
}
// Check which permissions need to be requested
val permissionsToRequest = getRequiredPermissions(withFineLocation)
val permissionsToRequest = getRequiredPermissions(withFineLocation, withAdvertise)
if (permissionsToRequest.isEmpty()) {
// All required permissions are already granted
@@ -163,7 +164,7 @@ class PermissionHandler(
*
* Returns a list of permissions that need to be requested (excluding already granted ones)
*/
private fun getRequiredPermissions(withFineLocation: Boolean): List<String> {
private fun getRequiredPermissions(withFineLocation: Boolean, withAdvertise: Boolean): List<String> {
val permissionsToRequest = mutableListOf<String>()
// Android 12+ (API 31+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -175,6 +176,9 @@ class PermissionHandler(
if (!hasPermissionGranted(Manifest.permission.BLUETOOTH_CONNECT)) {
permissionsToRequest.add(Manifest.permission.BLUETOOTH_CONNECT)
}
if (withAdvertise && !hasPermissionGranted(Manifest.permission.BLUETOOTH_ADVERTISE)) {
permissionsToRequest.add(Manifest.permission.BLUETOOTH_ADVERTISE)
}
// Location permission is optional - only request if user wants it
if (withFineLocation) {
// Prefer ACCESS_FINE_LOCATION over ACCESS_COARSE_LOCATION
@@ -236,7 +240,7 @@ class PermissionHandler(
* @param withFineLocation Whether location permission should be requested
* @return FlutterError if validation fails, null if all required permissions are declared
*/
private fun validateRequiredPermissions(withFineLocation: Boolean): FlutterError? {
private fun validateRequiredPermissions(withFineLocation: Boolean, withAdvertise: Boolean): FlutterError? {
val sdkInt = Build.VERSION.SDK_INT
val missingPermissions = mutableListOf<String>()
@@ -259,6 +263,10 @@ class PermissionHandler(
missingPermissions.add(Manifest.permission.BLUETOOTH_CONNECT)
}
if (withAdvertise && !hasPermissionInManifest(Manifest.permission.BLUETOOTH_ADVERTISE)) {
missingPermissions.add(Manifest.permission.BLUETOOTH_ADVERTISE)
}
// Location permission is optional on Android 12+ (depends on neverForLocation and withFineLocation)
// Only validate if it's actually needed
if (withFineLocation && !hasDeclaredLocationPermission) {
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.1.4), do not edit directly.
// Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -37,36 +37,150 @@ private object UniversalBlePigeonUtils {
)
}
}
fun doubleEquals(a: Double, b: Double): Boolean {
// Normalize -0.0 to 0.0 and handle NaN equality.
return (if (a == 0.0) 0.0 else a) == (if (b == 0.0) 0.0 else b) || (a.isNaN() && b.isNaN())
}
fun floatEquals(a: Float, b: Float): Boolean {
// Normalize -0.0 to 0.0 and handle NaN equality.
return (if (a == 0.0f) 0.0f else a) == (if (b == 0.0f) 0.0f else b) || (a.isNaN() && b.isNaN())
}
fun doubleHash(d: Double): Int {
// Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
val normalized = if (d == 0.0) 0.0 else d
val bits = java.lang.Double.doubleToLongBits(normalized)
return (bits xor (bits ushr 32)).toInt()
}
fun floatHash(f: Float): Int {
// Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
val normalized = if (f == 0.0f) 0.0f else f
return java.lang.Float.floatToIntBits(normalized)
}
fun deepEquals(a: Any?, b: Any?): Boolean {
if (a === b) {
return true
}
if (a == null || b == null) {
return false
}
if (a is ByteArray && b is ByteArray) {
return a.contentEquals(b)
return a.contentEquals(b)
}
if (a is IntArray && b is IntArray) {
return a.contentEquals(b)
return a.contentEquals(b)
}
if (a is LongArray && b is LongArray) {
return a.contentEquals(b)
return a.contentEquals(b)
}
if (a is DoubleArray && b is DoubleArray) {
return a.contentEquals(b)
if (a.size != b.size) return false
for (i in a.indices) {
if (!doubleEquals(a[i], b[i])) return false
}
return true
}
if (a is FloatArray && b is FloatArray) {
if (a.size != b.size) return false
for (i in a.indices) {
if (!floatEquals(a[i], b[i])) return false
}
return true
}
if (a is Array<*> && b is Array<*>) {
return a.size == b.size &&
a.indices.all{ deepEquals(a[it], b[it]) }
if (a.size != b.size) return false
for (i in a.indices) {
if (!deepEquals(a[i], b[i])) return false
}
return true
}
if (a is List<*> && b is List<*>) {
return a.size == b.size &&
a.indices.all{ deepEquals(a[it], b[it]) }
if (a.size != b.size) return false
val iterA = a.iterator()
val iterB = b.iterator()
while (iterA.hasNext() && iterB.hasNext()) {
if (!deepEquals(iterA.next(), iterB.next())) return false
}
return true
}
if (a is Map<*, *> && b is Map<*, *>) {
return a.size == b.size && a.all {
(b as Map<Any?, Any?>).contains(it.key) &&
deepEquals(it.value, b[it.key])
if (a.size != b.size) return false
for (entry in a) {
val key = entry.key
var found = false
for (bEntry in b) {
if (deepEquals(key, bEntry.key)) {
if (deepEquals(entry.value, bEntry.value)) {
found = true
break
} else {
return false
}
}
}
if (!found) return false
}
return true
}
if (a is Double && b is Double) {
return doubleEquals(a, b)
}
if (a is Float && b is Float) {
return floatEquals(a, b)
}
return a == b
}
fun deepHash(value: Any?): Int {
return when (value) {
null -> 0
is ByteArray -> value.contentHashCode()
is IntArray -> value.contentHashCode()
is LongArray -> value.contentHashCode()
is DoubleArray -> {
var result = 1
for (item in value) {
result = 31 * result + doubleHash(item)
}
result
}
is FloatArray -> {
var result = 1
for (item in value) {
result = 31 * result + floatHash(item)
}
result
}
is Array<*> -> {
var result = 1
for (item in value) {
result = 31 * result + deepHash(item)
}
result
}
is List<*> -> {
var result = 1
for (item in value) {
result = 31 * result + deepHash(item)
}
result
}
is Map<*, *> -> {
var result = 0
for (entry in value) {
result += ((deepHash(entry.key) * 31) xor deepHash(entry.value))
}
result
}
is Double -> doubleHash(value)
is Float -> floatHash(value)
else -> value.hashCode()
}
}
}
/**
@@ -79,7 +193,7 @@ class FlutterError (
val code: String,
override val message: String? = null,
val details: Any? = null
) : Throwable()
) : RuntimeException()
enum class UniversalBleLogLevel(val raw: Int) {
NONE(0),
@@ -219,15 +333,28 @@ data class UniversalBleScanResult (
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalBleScanResult) {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
val other = other as UniversalBleScanResult
return UniversalBlePigeonUtils.deepEquals(this.deviceId, other.deviceId) && UniversalBlePigeonUtils.deepEquals(this.name, other.name) && UniversalBlePigeonUtils.deepEquals(this.isPaired, other.isPaired) && UniversalBlePigeonUtils.deepEquals(this.rssi, other.rssi) && UniversalBlePigeonUtils.deepEquals(this.manufacturerDataList, other.manufacturerDataList) && UniversalBlePigeonUtils.deepEquals(this.serviceData, other.serviceData) && UniversalBlePigeonUtils.deepEquals(this.services, other.services) && UniversalBlePigeonUtils.deepEquals(this.timestamp, other.timestamp)
}
override fun hashCode(): Int = toList().hashCode()
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.deviceId)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.name)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.isPaired)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.rssi)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.manufacturerDataList)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.serviceData)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.services)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.timestamp)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -250,15 +377,22 @@ data class UniversalBleService (
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalBleService) {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
val other = other as UniversalBleService
return UniversalBlePigeonUtils.deepEquals(this.uuid, other.uuid) && UniversalBlePigeonUtils.deepEquals(this.characteristics, other.characteristics)
}
override fun hashCode(): Int = toList().hashCode()
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.uuid)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.characteristics)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -284,15 +418,23 @@ data class UniversalBleCharacteristic (
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalBleCharacteristic) {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
val other = other as UniversalBleCharacteristic
return UniversalBlePigeonUtils.deepEquals(this.uuid, other.uuid) && UniversalBlePigeonUtils.deepEquals(this.properties, other.properties) && UniversalBlePigeonUtils.deepEquals(this.descriptors, other.descriptors)
}
override fun hashCode(): Int = toList().hashCode()
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.uuid)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.properties)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.descriptors)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -312,15 +454,235 @@ data class UniversalBleDescriptor (
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalBleDescriptor) {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
val other = other as UniversalBleDescriptor
return UniversalBlePigeonUtils.deepEquals(this.uuid, other.uuid)
}
override fun hashCode(): Int = toList().hashCode()
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.uuid)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class UniversalBlePeripheralConfig (
val advertisedName: String,
val services: List<UniversalBlePeripheralService>
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): UniversalBlePeripheralConfig {
val advertisedName = pigeonVar_list[0] as String
val services = pigeonVar_list[1] as List<UniversalBlePeripheralService>
return UniversalBlePeripheralConfig(advertisedName, services)
}
}
fun toList(): List<Any?> {
return listOf(
advertisedName,
services,
)
}
override fun equals(other: Any?): Boolean {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
val other = other as UniversalBlePeripheralConfig
return UniversalBlePigeonUtils.deepEquals(this.advertisedName, other.advertisedName) && UniversalBlePigeonUtils.deepEquals(this.services, other.services)
}
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.advertisedName)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.services)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class UniversalBlePeripheralService (
val uuid: String,
val characteristics: List<UniversalBlePeripheralCharacteristic>
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): UniversalBlePeripheralService {
val uuid = pigeonVar_list[0] as String
val characteristics = pigeonVar_list[1] as List<UniversalBlePeripheralCharacteristic>
return UniversalBlePeripheralService(uuid, characteristics)
}
}
fun toList(): List<Any?> {
return listOf(
uuid,
characteristics,
)
}
override fun equals(other: Any?): Boolean {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
val other = other as UniversalBlePeripheralService
return UniversalBlePigeonUtils.deepEquals(this.uuid, other.uuid) && UniversalBlePigeonUtils.deepEquals(this.characteristics, other.characteristics)
}
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.uuid)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.characteristics)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class UniversalBlePeripheralCharacteristic (
val uuid: String,
val properties: List<Long>,
val permissions: List<Long>,
val descriptors: List<UniversalBlePeripheralDescriptor>,
val initialValue: ByteArray? = null
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): UniversalBlePeripheralCharacteristic {
val uuid = pigeonVar_list[0] as String
val properties = pigeonVar_list[1] as List<Long>
val permissions = pigeonVar_list[2] as List<Long>
val descriptors = pigeonVar_list[3] as List<UniversalBlePeripheralDescriptor>
val initialValue = pigeonVar_list[4] as ByteArray?
return UniversalBlePeripheralCharacteristic(uuid, properties, permissions, descriptors, initialValue)
}
}
fun toList(): List<Any?> {
return listOf(
uuid,
properties,
permissions,
descriptors,
initialValue,
)
}
override fun equals(other: Any?): Boolean {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
val other = other as UniversalBlePeripheralCharacteristic
return UniversalBlePigeonUtils.deepEquals(this.uuid, other.uuid) && UniversalBlePigeonUtils.deepEquals(this.properties, other.properties) && UniversalBlePigeonUtils.deepEquals(this.permissions, other.permissions) && UniversalBlePigeonUtils.deepEquals(this.descriptors, other.descriptors) && UniversalBlePigeonUtils.deepEquals(this.initialValue, other.initialValue)
}
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.uuid)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.properties)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.permissions)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.descriptors)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.initialValue)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class UniversalBlePeripheralDescriptor (
val uuid: String,
val permissions: List<Long>,
val initialValue: ByteArray? = null
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): UniversalBlePeripheralDescriptor {
val uuid = pigeonVar_list[0] as String
val permissions = pigeonVar_list[1] as List<Long>
val initialValue = pigeonVar_list[2] as ByteArray?
return UniversalBlePeripheralDescriptor(uuid, permissions, initialValue)
}
}
fun toList(): List<Any?> {
return listOf(
uuid,
permissions,
initialValue,
)
}
override fun equals(other: Any?): Boolean {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
val other = other as UniversalBlePeripheralDescriptor
return UniversalBlePigeonUtils.deepEquals(this.uuid, other.uuid) && UniversalBlePigeonUtils.deepEquals(this.permissions, other.permissions) && UniversalBlePigeonUtils.deepEquals(this.initialValue, other.initialValue)
}
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.uuid)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.permissions)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.initialValue)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class UniversalBlePeripheralWriteEvent (
val deviceId: String,
val service: String,
val characteristic: String,
val value: ByteArray
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): UniversalBlePeripheralWriteEvent {
val deviceId = pigeonVar_list[0] as String
val service = pigeonVar_list[1] as String
val characteristic = pigeonVar_list[2] as String
val value = pigeonVar_list[3] as ByteArray
return UniversalBlePeripheralWriteEvent(deviceId, service, characteristic, value)
}
}
fun toList(): List<Any?> {
return listOf(
deviceId,
service,
characteristic,
value,
)
}
override fun equals(other: Any?): Boolean {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
val other = other as UniversalBlePeripheralWriteEvent
return UniversalBlePigeonUtils.deepEquals(this.deviceId, other.deviceId) && UniversalBlePigeonUtils.deepEquals(this.service, other.service) && UniversalBlePigeonUtils.deepEquals(this.characteristic, other.characteristic) && UniversalBlePigeonUtils.deepEquals(this.value, other.value)
}
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.deviceId)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.service)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.characteristic)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.value)
return result
}
}
/**
@@ -355,15 +717,23 @@ data class AndroidOptions (
)
}
override fun equals(other: Any?): Boolean {
if (other !is AndroidOptions) {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
val other = other as AndroidOptions
return UniversalBlePigeonUtils.deepEquals(this.requestLocationPermission, other.requestLocationPermission) && UniversalBlePigeonUtils.deepEquals(this.scanMode, other.scanMode) && UniversalBlePigeonUtils.deepEquals(this.reportDelayMillis, other.reportDelayMillis)
}
override fun hashCode(): Int = toList().hashCode()
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.requestLocationPermission)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.scanMode)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.reportDelayMillis)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -383,15 +753,21 @@ data class UniversalScanConfig (
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalScanConfig) {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
val other = other as UniversalScanConfig
return UniversalBlePigeonUtils.deepEquals(this.android, other.android)
}
override fun hashCode(): Int = toList().hashCode()
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.android)
return result
}
}
/**
@@ -421,15 +797,23 @@ data class UniversalScanFilter (
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalScanFilter) {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
val other = other as UniversalScanFilter
return UniversalBlePigeonUtils.deepEquals(this.withServices, other.withServices) && UniversalBlePigeonUtils.deepEquals(this.withNamePrefix, other.withNamePrefix) && UniversalBlePigeonUtils.deepEquals(this.withManufacturerData, other.withManufacturerData)
}
override fun hashCode(): Int = toList().hashCode()
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.withServices)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.withNamePrefix)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.withManufacturerData)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -455,15 +839,23 @@ data class UniversalManufacturerDataFilter (
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalManufacturerDataFilter) {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
val other = other as UniversalManufacturerDataFilter
return UniversalBlePigeonUtils.deepEquals(this.companyIdentifier, other.companyIdentifier) && UniversalBlePigeonUtils.deepEquals(this.data, other.data) && UniversalBlePigeonUtils.deepEquals(this.mask, other.mask)
}
override fun hashCode(): Int = toList().hashCode()
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.companyIdentifier)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.data)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.mask)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -486,15 +878,22 @@ data class UniversalManufacturerData (
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalManufacturerData) {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
val other = other as UniversalManufacturerData
return UniversalBlePigeonUtils.deepEquals(this.companyIdentifier, other.companyIdentifier) && UniversalBlePigeonUtils.deepEquals(this.data, other.data)
}
override fun hashCode(): Int = toList().hashCode()
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.companyIdentifier)
result = 31 * result + UniversalBlePigeonUtils.deepHash(this.data)
return result
}
}
private open class UniversalBlePigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
@@ -536,25 +935,50 @@ private open class UniversalBlePigeonCodec : StandardMessageCodec() {
}
136.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
AndroidOptions.fromList(it)
UniversalBlePeripheralConfig.fromList(it)
}
}
137.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalScanConfig.fromList(it)
UniversalBlePeripheralService.fromList(it)
}
}
138.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalScanFilter.fromList(it)
UniversalBlePeripheralCharacteristic.fromList(it)
}
}
139.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalManufacturerDataFilter.fromList(it)
UniversalBlePeripheralDescriptor.fromList(it)
}
}
140.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalBlePeripheralWriteEvent.fromList(it)
}
}
141.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
AndroidOptions.fromList(it)
}
}
142.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalScanConfig.fromList(it)
}
}
143.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalScanFilter.fromList(it)
}
}
144.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalManufacturerDataFilter.fromList(it)
}
}
145.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalManufacturerData.fromList(it)
}
@@ -592,26 +1016,46 @@ private open class UniversalBlePigeonCodec : StandardMessageCodec() {
stream.write(135)
writeValue(stream, value.toList())
}
is AndroidOptions -> {
is UniversalBlePeripheralConfig -> {
stream.write(136)
writeValue(stream, value.toList())
}
is UniversalScanConfig -> {
is UniversalBlePeripheralService -> {
stream.write(137)
writeValue(stream, value.toList())
}
is UniversalScanFilter -> {
is UniversalBlePeripheralCharacteristic -> {
stream.write(138)
writeValue(stream, value.toList())
}
is UniversalManufacturerDataFilter -> {
is UniversalBlePeripheralDescriptor -> {
stream.write(139)
writeValue(stream, value.toList())
}
is UniversalManufacturerData -> {
is UniversalBlePeripheralWriteEvent -> {
stream.write(140)
writeValue(stream, value.toList())
}
is AndroidOptions -> {
stream.write(141)
writeValue(stream, value.toList())
}
is UniversalScanConfig -> {
stream.write(142)
writeValue(stream, value.toList())
}
is UniversalScanFilter -> {
stream.write(143)
writeValue(stream, value.toList())
}
is UniversalManufacturerDataFilter -> {
stream.write(144)
writeValue(stream, value.toList())
}
is UniversalManufacturerData -> {
stream.write(145)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@@ -625,8 +1069,8 @@ private open class UniversalBlePigeonCodec : StandardMessageCodec() {
*/
interface UniversalBlePlatformChannel {
fun getBluetoothAvailabilityState(callback: (Result<Long>) -> Unit)
fun hasPermissions(withAndroidFineLocation: Boolean): Boolean
fun requestPermissions(withAndroidFineLocation: Boolean, callback: (Result<Unit>) -> Unit)
fun hasPermissions(withAndroidFineLocation: Boolean, withAndroidBluetoothAdvertise: Boolean): Boolean
fun requestPermissions(withAndroidFineLocation: Boolean, withAndroidBluetoothAdvertise: Boolean, callback: (Result<Unit>) -> Unit)
fun enableBluetooth(callback: (Result<Boolean>) -> Unit)
fun disableBluetooth(callback: (Result<Boolean>) -> Unit)
fun startScan(filter: UniversalScanFilter?, config: UniversalScanConfig?)
@@ -646,6 +1090,11 @@ interface UniversalBlePlatformChannel {
fun getConnectionState(deviceId: String): Long
fun readRssi(deviceId: String, callback: (Result<Long>) -> Unit)
fun requestConnectionPriority(deviceId: String, priority: Long, callback: (Result<Unit>) -> Unit)
fun isPeripheralSupported(): Boolean
fun startPeripheral(config: UniversalBlePeripheralConfig, callback: (Result<Unit>) -> Unit)
fun stopPeripheral(callback: (Result<Unit>) -> Unit)
fun updatePeripheralCharacteristicValue(service: String, characteristic: String, value: ByteArray, callback: (Result<Unit>) -> Unit)
fun notifyPeripheralCharacteristic(service: String, characteristic: String, value: ByteArray, indicate: Boolean, callback: (Result<Unit>) -> Unit)
fun setLogLevel(logLevel: UniversalBleLogLevel)
companion object {
@@ -681,8 +1130,9 @@ interface UniversalBlePlatformChannel {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val withAndroidFineLocationArg = args[0] as Boolean
val withAndroidBluetoothAdvertiseArg = args[1] as Boolean
val wrapped: List<Any?> = try {
listOf(api.hasPermissions(withAndroidFineLocationArg))
listOf(api.hasPermissions(withAndroidFineLocationArg, withAndroidBluetoothAdvertiseArg))
} catch (exception: Throwable) {
UniversalBlePigeonUtils.wrapError(exception)
}
@@ -698,7 +1148,8 @@ interface UniversalBlePlatformChannel {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val withAndroidFineLocationArg = args[0] as Boolean
api.requestPermissions(withAndroidFineLocationArg) { result: Result<Unit> ->
val withAndroidBluetoothAdvertiseArg = args[1] as Boolean
api.requestPermissions(withAndroidFineLocationArg, withAndroidBluetoothAdvertiseArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(UniversalBlePigeonUtils.wrapError(error))
@@ -1078,6 +1529,100 @@ interface UniversalBlePlatformChannel {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPeripheralSupported$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> = try {
listOf(api.isPeripheralSupported())
} catch (exception: Throwable) {
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startPeripheral$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val configArg = args[0] as UniversalBlePeripheralConfig
api.startPeripheral(configArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
reply.reply(UniversalBlePigeonUtils.wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopPeripheral$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
api.stopPeripheral{ result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
reply.reply(UniversalBlePigeonUtils.wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.updatePeripheralCharacteristicValue$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val serviceArg = args[0] as String
val characteristicArg = args[1] as String
val valueArg = args[2] as ByteArray
api.updatePeripheralCharacteristicValue(serviceArg, characteristicArg, valueArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
reply.reply(UniversalBlePigeonUtils.wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.notifyPeripheralCharacteristic$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val serviceArg = args[0] as String
val characteristicArg = args[1] as String
val valueArg = args[2] as ByteArray
val indicateArg = args[3] as Boolean
api.notifyPeripheralCharacteristic(serviceArg, characteristicArg, valueArg, indicateArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
reply.reply(UniversalBlePigeonUtils.wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel$separatedMessageChannelSuffix", codec)
if (api != null) {
@@ -1125,7 +1670,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
}
} else {
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
}
fun onPairStateChange(deviceIdArg: String, isPairedArg: Boolean, errorArg: String?, callback: (Result<Unit>) -> Unit)
@@ -1142,7 +1687,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
}
} else {
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
}
fun onScanResult(resultArg: UniversalBleScanResult, callback: (Result<Unit>) -> Unit)
@@ -1159,7 +1704,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
}
} else {
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
}
fun onValueChanged(deviceIdArg: String, characteristicIdArg: String, valueArg: ByteArray, timestampArg: Long?, callback: (Result<Unit>) -> Unit)
@@ -1176,7 +1721,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
}
} else {
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
}
fun onConnectionChanged(deviceIdArg: String, connectedArg: Boolean, errorArg: String?, callback: (Result<Unit>) -> Unit)
@@ -1193,7 +1738,58 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
}
} else {
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
}
fun onPeripheralConnectionChanged(deviceIdArg: String, connectedArg: Boolean, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPeripheralConnectionChanged$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(deviceIdArg, connectedArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
fun onPeripheralWrite(eventArg: UniversalBlePeripheralWriteEvent, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPeripheralWrite$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(eventArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
fun onPeripheralSubscriptionChanged(deviceIdArg: String, serviceArg: String, characteristicArg: String, subscribedArg: Boolean, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPeripheralSubscriptionChanged$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(deviceIdArg, serviceArg, characteristicArg, subscribedArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
}
@@ -9,9 +9,16 @@ import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattServer
import android.bluetooth.BluetoothGattServerCallback
import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.bluetooth.BluetoothStatusCodes
import android.bluetooth.le.AdvertiseCallback
import android.bluetooth.le.AdvertiseData
import android.bluetooth.le.AdvertiseSettings
import android.bluetooth.le.BluetoothLeAdvertiser
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
@@ -24,6 +31,7 @@ import android.content.IntentFilter
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.ParcelUuid
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
@@ -63,6 +71,12 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
private val rssiResultFutureList = mutableListOf<RssiResultFuture>()
private val autoConnectDevices = mutableSetOf<String>()
private var peripheralGattServer: BluetoothGattServer? = null
private var peripheralAdvertiser: BluetoothLeAdvertiser? = null
private var peripheralPendingStartCallback: ((Result<Unit>) -> Unit)? = null
private val peripheralConnectedDevices = mutableSetOf<BluetoothDevice>()
private val peripheralCharacteristics = mutableMapOf<String, BluetoothGattCharacteristic>()
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
UniversalBlePlatformChannel.setUp(flutterPluginBinding.binaryMessenger, this)
callbackChannel = UniversalBleCallbackChannel(flutterPluginBinding.binaryMessenger)
@@ -84,6 +98,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
bluetoothManager.adapter.bluetoothLeScanner?.stopScan(scanCallback)
stopPeripheralInternal()
context.unregisterReceiver(broadcastReceiver)
callbackChannel = null
mainThreadHandler = null
@@ -99,12 +114,19 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
)
}
override fun hasPermissions(withAndroidFineLocation: Boolean): Boolean {
return permissionHandler?.hasPermissions(withAndroidFineLocation) ?: false
override fun hasPermissions(
withAndroidFineLocation: Boolean,
withAndroidBluetoothAdvertise: Boolean,
): Boolean {
return permissionHandler?.hasPermissions(
withAndroidFineLocation,
withAndroidBluetoothAdvertise,
) ?: false
}
override fun requestPermissions(
withAndroidFineLocation: Boolean,
withAndroidBluetoothAdvertise: Boolean,
callback: (Result<Unit>) -> Unit,
) {
if (permissionHandler == null) {
@@ -117,7 +139,11 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
)
)
}
permissionHandler?.requestPermissions(withAndroidFineLocation, callback)
permissionHandler?.requestPermissions(
withAndroidFineLocation,
withAndroidBluetoothAdvertise,
callback,
)
}
override fun enableBluetooth(callback: (Result<Boolean>) -> Unit) {
@@ -801,6 +827,319 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
}
override fun isPeripheralSupported(): Boolean {
val adapter = bluetoothManager.adapter ?: return false
return adapter.isEnabled && adapter.isMultipleAdvertisementSupported
}
override fun startPeripheral(
config: UniversalBlePeripheralConfig,
callback: (Result<Unit>) -> Unit,
) {
val adapter = bluetoothManager.adapter
if (adapter == null || !adapter.isEnabled) {
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.BLUETOOTH_NOT_ENABLED,
"Bluetooth not enabled"
)
)
)
return
}
if (!adapter.isMultipleAdvertisementSupported) {
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.NOT_SUPPORTED,
"BLE peripheral advertising is not supported on this device"
)
)
)
return
}
if (peripheralPendingStartCallback != null) {
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.OPERATION_IN_PROGRESS,
"Peripheral start already in progress"
)
)
)
return
}
stopPeripheralInternal()
val server = bluetoothManager.openGattServer(context, peripheralGattServerCallback)
if (server == null) {
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Unable to open GATT server"
)
)
)
return
}
try {
peripheralCharacteristics.clear()
config.services.forEach { serviceConfig ->
val service = BluetoothGattService(
UUID.fromString(serviceConfig.uuid),
BluetoothGattService.SERVICE_TYPE_PRIMARY,
)
serviceConfig.characteristics.forEach { characteristicConfig ->
val characteristic = BluetoothGattCharacteristic(
UUID.fromString(characteristicConfig.uuid),
characteristicConfig.properties.toPeripheralPropertyFlags(),
characteristicConfig.permissions.toPeripheralPermissionFlags(),
)
@Suppress("DEPRECATION")
characteristic.value = characteristicConfig.initialValue ?: ByteArray(0)
characteristicConfig.descriptors.forEach { descriptorConfig ->
val descriptor = BluetoothGattDescriptor(
UUID.fromString(descriptorConfig.uuid),
descriptorConfig.permissions.toPeripheralPermissionFlags(),
)
@Suppress("DEPRECATION")
descriptor.value = descriptorConfig.initialValue ?: ByteArray(0)
characteristic.addDescriptor(descriptor)
}
if (characteristicConfig.properties.any {
it == CharacteristicProperty.Notify.value ||
it == CharacteristicProperty.Indicate.value
} && characteristic.getDescriptor(ccdCharacteristic) == null
) {
characteristic.addDescriptor(
BluetoothGattDescriptor(
ccdCharacteristic,
BluetoothGattDescriptor.PERMISSION_READ or
BluetoothGattDescriptor.PERMISSION_WRITE,
)
)
}
service.addCharacteristic(characteristic)
peripheralCharacteristics[peripheralCharacteristicKey(serviceConfig.uuid, characteristicConfig.uuid)] = characteristic
}
if (!server.addService(service)) {
throw createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to add GATT service ${serviceConfig.uuid}"
)
}
}
} catch (e: FlutterError) {
server.close()
peripheralCharacteristics.clear()
callback(Result.failure(e))
return
} catch (e: Exception) {
server.close()
peripheralCharacteristics.clear()
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to create GATT services",
e.toString()
)
)
)
return
}
peripheralGattServer = server
try {
adapter.name = config.advertisedName
} catch (_: SecurityException) {
}
val advertiser = adapter.bluetoothLeAdvertiser
if (advertiser == null) {
stopPeripheralInternal()
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.NOT_SUPPORTED,
"BLE advertiser not available"
)
)
)
return
}
val settings = AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
.setConnectable(true)
.setTimeout(0)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
.build()
val dataBuilder = AdvertiseData.Builder().setIncludeDeviceName(true)
config.services.forEach { service ->
dataBuilder.addServiceUuid(ParcelUuid(UUID.fromString(service.uuid)))
}
peripheralAdvertiser = advertiser
peripheralPendingStartCallback = callback
advertiser.startAdvertising(settings, dataBuilder.build(), peripheralAdvertiseCallback)
}
override fun stopPeripheral(callback: (Result<Unit>) -> Unit) {
peripheralPendingStartCallback?.invoke(
Result.failure(
createFlutterError(
UniversalBleErrorCode.OPERATION_CANCELLED,
"Peripheral start cancelled"
)
)
)
peripheralPendingStartCallback = null
stopPeripheralInternal()
callback(Result.success(Unit))
}
override fun updatePeripheralCharacteristicValue(
service: String,
characteristic: String,
value: ByteArray,
callback: (Result<Unit>) -> Unit,
) {
val gattCharacteristic = peripheralCharacteristics[peripheralCharacteristicKey(service, characteristic)]
if (gattCharacteristic == null) {
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_NOT_FOUND,
"Unknown peripheral characteristic $characteristic"
)
)
)
return
}
@Suppress("DEPRECATION")
gattCharacteristic.value = value
callback(Result.success(Unit))
}
override fun notifyPeripheralCharacteristic(
service: String,
characteristic: String,
value: ByteArray,
indicate: Boolean,
callback: (Result<Unit>) -> Unit,
) {
val server = peripheralGattServer
val gattCharacteristic = peripheralCharacteristics[peripheralCharacteristicKey(service, characteristic)]
if (server == null || gattCharacteristic == null) {
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_NOT_FOUND,
"Unknown peripheral characteristic $characteristic"
)
)
)
return
}
@Suppress("DEPRECATION")
gattCharacteristic.value = value
val devices = synchronized(peripheralConnectedDevices) {
peripheralConnectedDevices.toList()
}
devices.forEach { device ->
server.notifyCharacteristicChanged(device, gattCharacteristic, indicate)
}
callback(Result.success(Unit))
}
private fun stopPeripheralInternal() {
peripheralAdvertiser?.stopAdvertising(peripheralAdvertiseCallback)
peripheralAdvertiser = null
peripheralGattServer?.close()
peripheralGattServer = null
synchronized(peripheralConnectedDevices) {
peripheralConnectedDevices.clear()
}
peripheralCharacteristics.clear()
}
private fun peripheralCharacteristicKey(service: String, characteristic: String): String {
return "${service.lowercase()}|${characteristic.lowercase()}"
}
private fun List<Long>.toPeripheralPropertyFlags(): Int {
var flags = 0
forEach { property ->
when (CharacteristicProperty.entries.firstOrNull { it.value == property }) {
CharacteristicProperty.Broadcast -> flags = flags or BluetoothGattCharacteristic.PROPERTY_BROADCAST
CharacteristicProperty.Read -> flags = flags or BluetoothGattCharacteristic.PROPERTY_READ
CharacteristicProperty.WriteWithoutResponse -> flags = flags or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
CharacteristicProperty.Write -> flags = flags or BluetoothGattCharacteristic.PROPERTY_WRITE
CharacteristicProperty.Notify -> flags = flags or BluetoothGattCharacteristic.PROPERTY_NOTIFY
CharacteristicProperty.Indicate -> flags = flags or BluetoothGattCharacteristic.PROPERTY_INDICATE
CharacteristicProperty.AuthenticatedSignedWrites -> flags = flags or BluetoothGattCharacteristic.PROPERTY_SIGNED_WRITE
CharacteristicProperty.ExtendedProperties -> flags = flags or BluetoothGattCharacteristic.PROPERTY_EXTENDED_PROPS
null -> {}
}
}
return flags
}
private fun List<Long>.toPeripheralPermissionFlags(): Int {
var flags = 0
forEach { permission ->
when (permission) {
0L -> flags = flags or BluetoothGattCharacteristic.PERMISSION_READ
1L -> flags = flags or BluetoothGattCharacteristic.PERMISSION_WRITE
}
}
return flags
}
private fun callbackPeripheralConnection(deviceId: String, connected: Boolean) {
mainThreadHandler?.post {
callbackChannel?.onPeripheralConnectionChanged(deviceId, connected) {}
}
}
private fun callbackPeripheralSubscription(
deviceId: String,
service: String,
characteristic: String,
subscribed: Boolean,
) {
mainThreadHandler?.post {
callbackChannel?.onPeripheralSubscriptionChanged(
deviceId,
service,
characteristic,
subscribed,
) {}
}
}
private fun callbackPeripheralWrite(
deviceId: String,
service: String,
characteristic: String,
value: ByteArray,
) {
mainThreadHandler?.post {
callbackChannel?.onPeripheralWrite(
UniversalBlePeripheralWriteEvent(
deviceId,
service,
characteristic,
value,
)
) {}
}
}
override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
val deviceId = gatt?.device?.address ?: return
mtuResultFutureList.removeAll {
@@ -1191,6 +1530,157 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
}
private val peripheralAdvertiseCallback = object : AdvertiseCallback() {
override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
peripheralPendingStartCallback?.invoke(Result.success(Unit))
peripheralPendingStartCallback = null
}
override fun onStartFailure(errorCode: Int) {
peripheralPendingStartCallback?.invoke(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"BLE advertising failed with code $errorCode",
errorCode.toString()
)
)
)
peripheralPendingStartCallback = null
stopPeripheralInternal()
}
}
private val peripheralGattServerCallback = object : BluetoothGattServerCallback() {
override fun onConnectionStateChange(device: BluetoothDevice, status: Int, newState: Int) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
synchronized(peripheralConnectedDevices) {
peripheralConnectedDevices.add(device)
}
callbackPeripheralConnection(device.address, true)
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
synchronized(peripheralConnectedDevices) {
peripheralConnectedDevices.remove(device)
}
callbackPeripheralConnection(device.address, false)
}
}
override fun onCharacteristicReadRequest(
device: BluetoothDevice,
requestId: Int,
offset: Int,
characteristic: BluetoothGattCharacteristic,
) {
@Suppress("DEPRECATION")
val value = characteristic.value ?: ByteArray(0)
if (offset > value.size) {
peripheralGattServer?.sendResponse(
device,
requestId,
BluetoothGatt.GATT_INVALID_OFFSET,
offset,
null,
)
return
}
peripheralGattServer?.sendResponse(
device,
requestId,
BluetoothGatt.GATT_SUCCESS,
offset,
value.copyOfRange(offset, value.size),
)
}
override fun onCharacteristicWriteRequest(
device: BluetoothDevice,
requestId: Int,
characteristic: BluetoothGattCharacteristic,
preparedWrite: Boolean,
responseNeeded: Boolean,
offset: Int,
value: ByteArray,
) {
@Suppress("DEPRECATION")
characteristic.value = value
if (responseNeeded) {
peripheralGattServer?.sendResponse(
device,
requestId,
BluetoothGatt.GATT_SUCCESS,
offset,
null,
)
}
callbackPeripheralWrite(
device.address,
characteristic.service.uuid.toString(),
characteristic.uuid.toString(),
value,
)
}
override fun onDescriptorReadRequest(
device: BluetoothDevice,
requestId: Int,
offset: Int,
descriptor: BluetoothGattDescriptor,
) {
@Suppress("DEPRECATION")
val value = descriptor.value ?: ByteArray(0)
if (offset > value.size) {
peripheralGattServer?.sendResponse(
device,
requestId,
BluetoothGatt.GATT_INVALID_OFFSET,
offset,
null,
)
return
}
peripheralGattServer?.sendResponse(
device,
requestId,
BluetoothGatt.GATT_SUCCESS,
offset,
value.copyOfRange(offset, value.size),
)
}
override fun onDescriptorWriteRequest(
device: BluetoothDevice,
requestId: Int,
descriptor: BluetoothGattDescriptor,
preparedWrite: Boolean,
responseNeeded: Boolean,
offset: Int,
value: ByteArray,
) {
@Suppress("DEPRECATION")
descriptor.value = value
if (responseNeeded) {
peripheralGattServer?.sendResponse(
device,
requestId,
BluetoothGatt.GATT_SUCCESS,
offset,
null,
)
}
if (descriptor.uuid == ccdCharacteristic) {
val subscribed = value.contentEquals(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) ||
value.contentEquals(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)
callbackPeripheralSubscription(
device.address,
descriptor.characteristic.service.uuid.toString(),
descriptor.characteristic.uuid.toString(),
subscribed,
)
}
}
}
override fun onConnectionStateChange(
gatt: BluetoothGatt,
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.1.4), do not edit directly.
// Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
import Foundation
@@ -50,7 +50,7 @@ private func wrapError(_ error: Any) -> [Any?] {
}
return [
"\(error)",
"\(type(of: error))",
"\(Swift.type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)",
]
}
@@ -68,6 +68,19 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
return value as! T?
}
private func doubleEqualsUniversalBle(_ lhs: Double, _ rhs: Double) -> Bool {
return (lhs.isNaN && rhs.isNaN) || lhs == rhs
}
private func doubleHashUniversalBle(_ value: Double, _ hasher: inout Hasher) {
if value.isNaN {
hasher.combine(0x7FF8000000000000)
} else {
// Normalize -0.0 to 0.0
hasher.combine(value == 0 ? 0 : value)
}
}
func deepEqualsUniversalBle(_ lhs: Any?, _ rhs: Any?) -> Bool {
let cleanLhs = nilOrValue(lhs) as Any?
let cleanRhs = nilOrValue(rhs) as Any?
@@ -78,59 +91,92 @@ func deepEqualsUniversalBle(_ lhs: Any?, _ rhs: Any?) -> Bool {
case (nil, _), (_, nil):
return false
case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs:
return true
case is (Void, Void):
return true
case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable):
return cleanLhsHashable == cleanRhsHashable
case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]):
guard cleanLhsArray.count == cleanRhsArray.count else { return false }
for (index, element) in cleanLhsArray.enumerated() {
if !deepEqualsUniversalBle(element, cleanRhsArray[index]) {
case (let lhsArray, let rhsArray) as ([Any?], [Any?]):
guard lhsArray.count == rhsArray.count else { return false }
for (index, element) in lhsArray.enumerated() {
if !deepEqualsUniversalBle(element, rhsArray[index]) {
return false
}
}
return true
case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false }
for (key, cleanLhsValue) in cleanLhsDictionary {
guard cleanRhsDictionary.index(forKey: key) != nil else { return false }
if !deepEqualsUniversalBle(cleanLhsValue, cleanRhsDictionary[key]!) {
case (let lhsArray, let rhsArray) as ([Double], [Double]):
guard lhsArray.count == rhsArray.count else { return false }
for (index, element) in lhsArray.enumerated() {
if !doubleEqualsUniversalBle(element, rhsArray[index]) {
return false
}
}
return true
case (let lhsDictionary, let rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
guard lhsDictionary.count == rhsDictionary.count else { return false }
for (lhsKey, lhsValue) in lhsDictionary {
var found = false
for (rhsKey, rhsValue) in rhsDictionary {
if deepEqualsUniversalBle(lhsKey, rhsKey) {
if deepEqualsUniversalBle(lhsValue, rhsValue) {
found = true
break
} else {
return false
}
}
}
if !found { return false }
}
return true
case (let lhs as Double, let rhs as Double):
return doubleEqualsUniversalBle(lhs, rhs)
case (let lhsHashable, let rhsHashable) as (AnyHashable, AnyHashable):
return lhsHashable == rhsHashable
default:
// Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue.
return false
}
}
func deepHashUniversalBle(value: Any?, hasher: inout Hasher) {
if let valueList = value as? [AnyHashable] {
for item in valueList { deepHashUniversalBle(value: item, hasher: &hasher) }
return
}
if let valueDict = value as? [AnyHashable: AnyHashable] {
for key in valueDict.keys {
hasher.combine(key)
deepHashUniversalBle(value: valueDict[key]!, hasher: &hasher)
let cleanValue = nilOrValue(value) as Any?
if let cleanValue = cleanValue {
if let doubleValue = cleanValue as? Double {
doubleHashUniversalBle(doubleValue, &hasher)
} else if let valueList = cleanValue as? [Any?] {
for item in valueList {
deepHashUniversalBle(value: item, hasher: &hasher)
}
} else if let valueList = cleanValue as? [Double] {
for item in valueList {
doubleHashUniversalBle(item, &hasher)
}
} else if let valueDict = cleanValue as? [AnyHashable: Any?] {
var result = 0
for (key, value) in valueDict {
var entryKeyHasher = Hasher()
deepHashUniversalBle(value: key, hasher: &entryKeyHasher)
var entryValueHasher = Hasher()
deepHashUniversalBle(value: value, hasher: &entryValueHasher)
result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize())
}
hasher.combine(result)
} else if let hashableValue = cleanValue as? AnyHashable {
hasher.combine(hashableValue)
} else {
hasher.combine(String(describing: cleanValue))
}
return
} else {
hasher.combine(0)
}
if let hashableValue = value as? AnyHashable {
hasher.combine(hashableValue.hashValue)
}
return hasher.combine(String(describing: value))
}
enum UniversalBleLogLevel: Int {
case none = 0
@@ -261,9 +307,22 @@ struct UniversalBleScanResult: Hashable {
]
}
static func == (lhs: UniversalBleScanResult, rhs: UniversalBleScanResult) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.deviceId, rhs.deviceId) && deepEqualsUniversalBle(lhs.name, rhs.name) && deepEqualsUniversalBle(lhs.isPaired, rhs.isPaired) && deepEqualsUniversalBle(lhs.rssi, rhs.rssi) && deepEqualsUniversalBle(lhs.manufacturerDataList, rhs.manufacturerDataList) && deepEqualsUniversalBle(lhs.serviceData, rhs.serviceData) && deepEqualsUniversalBle(lhs.services, rhs.services) && deepEqualsUniversalBle(lhs.timestamp, rhs.timestamp)
}
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
hasher.combine("UniversalBleScanResult")
deepHashUniversalBle(value: deviceId, hasher: &hasher)
deepHashUniversalBle(value: name, hasher: &hasher)
deepHashUniversalBle(value: isPaired, hasher: &hasher)
deepHashUniversalBle(value: rssi, hasher: &hasher)
deepHashUniversalBle(value: manufacturerDataList, hasher: &hasher)
deepHashUniversalBle(value: serviceData, hasher: &hasher)
deepHashUniversalBle(value: services, hasher: &hasher)
deepHashUniversalBle(value: timestamp, hasher: &hasher)
}
}
@@ -290,9 +349,16 @@ struct UniversalBleService: Hashable {
]
}
static func == (lhs: UniversalBleService, rhs: UniversalBleService) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.uuid, rhs.uuid) && deepEqualsUniversalBle(lhs.characteristics, rhs.characteristics)
}
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
hasher.combine("UniversalBleService")
deepHashUniversalBle(value: uuid, hasher: &hasher)
deepHashUniversalBle(value: characteristics, hasher: &hasher)
}
}
@@ -323,9 +389,17 @@ struct UniversalBleCharacteristic: Hashable {
]
}
static func == (lhs: UniversalBleCharacteristic, rhs: UniversalBleCharacteristic) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.uuid, rhs.uuid) && deepEqualsUniversalBle(lhs.properties, rhs.properties) && deepEqualsUniversalBle(lhs.descriptors, rhs.descriptors)
}
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
hasher.combine("UniversalBleCharacteristic")
deepHashUniversalBle(value: uuid, hasher: &hasher)
deepHashUniversalBle(value: properties, hasher: &hasher)
deepHashUniversalBle(value: descriptors, hasher: &hasher)
}
}
@@ -348,9 +422,225 @@ struct UniversalBleDescriptor: Hashable {
]
}
static func == (lhs: UniversalBleDescriptor, rhs: UniversalBleDescriptor) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.uuid, rhs.uuid)
}
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
hasher.combine("UniversalBleDescriptor")
deepHashUniversalBle(value: uuid, hasher: &hasher)
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalBlePeripheralConfig: Hashable {
var advertisedName: String
var services: [UniversalBlePeripheralService]
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalBlePeripheralConfig? {
let advertisedName = pigeonVar_list[0] as! String
let services = pigeonVar_list[1] as! [UniversalBlePeripheralService]
return UniversalBlePeripheralConfig(
advertisedName: advertisedName,
services: services
)
}
func toList() -> [Any?] {
return [
advertisedName,
services,
]
}
static func == (lhs: UniversalBlePeripheralConfig, rhs: UniversalBlePeripheralConfig) -> Bool {
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.advertisedName, rhs.advertisedName) && deepEqualsUniversalBle(lhs.services, rhs.services)
}
func hash(into hasher: inout Hasher) {
hasher.combine("UniversalBlePeripheralConfig")
deepHashUniversalBle(value: advertisedName, hasher: &hasher)
deepHashUniversalBle(value: services, hasher: &hasher)
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalBlePeripheralService: Hashable {
var uuid: String
var characteristics: [UniversalBlePeripheralCharacteristic]
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalBlePeripheralService? {
let uuid = pigeonVar_list[0] as! String
let characteristics = pigeonVar_list[1] as! [UniversalBlePeripheralCharacteristic]
return UniversalBlePeripheralService(
uuid: uuid,
characteristics: characteristics
)
}
func toList() -> [Any?] {
return [
uuid,
characteristics,
]
}
static func == (lhs: UniversalBlePeripheralService, rhs: UniversalBlePeripheralService) -> Bool {
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.uuid, rhs.uuid) && deepEqualsUniversalBle(lhs.characteristics, rhs.characteristics)
}
func hash(into hasher: inout Hasher) {
hasher.combine("UniversalBlePeripheralService")
deepHashUniversalBle(value: uuid, hasher: &hasher)
deepHashUniversalBle(value: characteristics, hasher: &hasher)
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalBlePeripheralCharacteristic: Hashable {
var uuid: String
var properties: [Int64]
var permissions: [Int64]
var descriptors: [UniversalBlePeripheralDescriptor]
var initialValue: FlutterStandardTypedData? = nil
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalBlePeripheralCharacteristic? {
let uuid = pigeonVar_list[0] as! String
let properties = pigeonVar_list[1] as! [Int64]
let permissions = pigeonVar_list[2] as! [Int64]
let descriptors = pigeonVar_list[3] as! [UniversalBlePeripheralDescriptor]
let initialValue: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[4])
return UniversalBlePeripheralCharacteristic(
uuid: uuid,
properties: properties,
permissions: permissions,
descriptors: descriptors,
initialValue: initialValue
)
}
func toList() -> [Any?] {
return [
uuid,
properties,
permissions,
descriptors,
initialValue,
]
}
static func == (lhs: UniversalBlePeripheralCharacteristic, rhs: UniversalBlePeripheralCharacteristic) -> Bool {
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.uuid, rhs.uuid) && deepEqualsUniversalBle(lhs.properties, rhs.properties) && deepEqualsUniversalBle(lhs.permissions, rhs.permissions) && deepEqualsUniversalBle(lhs.descriptors, rhs.descriptors) && deepEqualsUniversalBle(lhs.initialValue, rhs.initialValue)
}
func hash(into hasher: inout Hasher) {
hasher.combine("UniversalBlePeripheralCharacteristic")
deepHashUniversalBle(value: uuid, hasher: &hasher)
deepHashUniversalBle(value: properties, hasher: &hasher)
deepHashUniversalBle(value: permissions, hasher: &hasher)
deepHashUniversalBle(value: descriptors, hasher: &hasher)
deepHashUniversalBle(value: initialValue, hasher: &hasher)
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalBlePeripheralDescriptor: Hashable {
var uuid: String
var permissions: [Int64]
var initialValue: FlutterStandardTypedData? = nil
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalBlePeripheralDescriptor? {
let uuid = pigeonVar_list[0] as! String
let permissions = pigeonVar_list[1] as! [Int64]
let initialValue: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[2])
return UniversalBlePeripheralDescriptor(
uuid: uuid,
permissions: permissions,
initialValue: initialValue
)
}
func toList() -> [Any?] {
return [
uuid,
permissions,
initialValue,
]
}
static func == (lhs: UniversalBlePeripheralDescriptor, rhs: UniversalBlePeripheralDescriptor) -> Bool {
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.uuid, rhs.uuid) && deepEqualsUniversalBle(lhs.permissions, rhs.permissions) && deepEqualsUniversalBle(lhs.initialValue, rhs.initialValue)
}
func hash(into hasher: inout Hasher) {
hasher.combine("UniversalBlePeripheralDescriptor")
deepHashUniversalBle(value: uuid, hasher: &hasher)
deepHashUniversalBle(value: permissions, hasher: &hasher)
deepHashUniversalBle(value: initialValue, hasher: &hasher)
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalBlePeripheralWriteEvent: Hashable {
var deviceId: String
var service: String
var characteristic: String
var value: FlutterStandardTypedData
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalBlePeripheralWriteEvent? {
let deviceId = pigeonVar_list[0] as! String
let service = pigeonVar_list[1] as! String
let characteristic = pigeonVar_list[2] as! String
let value = pigeonVar_list[3] as! FlutterStandardTypedData
return UniversalBlePeripheralWriteEvent(
deviceId: deviceId,
service: service,
characteristic: characteristic,
value: value
)
}
func toList() -> [Any?] {
return [
deviceId,
service,
characteristic,
value,
]
}
static func == (lhs: UniversalBlePeripheralWriteEvent, rhs: UniversalBlePeripheralWriteEvent) -> Bool {
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.deviceId, rhs.deviceId) && deepEqualsUniversalBle(lhs.service, rhs.service) && deepEqualsUniversalBle(lhs.characteristic, rhs.characteristic) && deepEqualsUniversalBle(lhs.value, rhs.value)
}
func hash(into hasher: inout Hasher) {
hasher.combine("UniversalBlePeripheralWriteEvent")
deepHashUniversalBle(value: deviceId, hasher: &hasher)
deepHashUniversalBle(value: service, hasher: &hasher)
deepHashUniversalBle(value: characteristic, hasher: &hasher)
deepHashUniversalBle(value: value, hasher: &hasher)
}
}
@@ -388,9 +678,17 @@ struct AndroidOptions: Hashable {
]
}
static func == (lhs: AndroidOptions, rhs: AndroidOptions) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.requestLocationPermission, rhs.requestLocationPermission) && deepEqualsUniversalBle(lhs.scanMode, rhs.scanMode) && deepEqualsUniversalBle(lhs.reportDelayMillis, rhs.reportDelayMillis)
}
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
hasher.combine("AndroidOptions")
deepHashUniversalBle(value: requestLocationPermission, hasher: &hasher)
deepHashUniversalBle(value: scanMode, hasher: &hasher)
deepHashUniversalBle(value: reportDelayMillis, hasher: &hasher)
}
}
@@ -413,9 +711,15 @@ struct UniversalScanConfig: Hashable {
]
}
static func == (lhs: UniversalScanConfig, rhs: UniversalScanConfig) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.android, rhs.android)
}
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
hasher.combine("UniversalScanConfig")
deepHashUniversalBle(value: android, hasher: &hasher)
}
}
@@ -448,9 +752,17 @@ struct UniversalScanFilter: Hashable {
]
}
static func == (lhs: UniversalScanFilter, rhs: UniversalScanFilter) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.withServices, rhs.withServices) && deepEqualsUniversalBle(lhs.withNamePrefix, rhs.withNamePrefix) && deepEqualsUniversalBle(lhs.withManufacturerData, rhs.withManufacturerData)
}
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
hasher.combine("UniversalScanFilter")
deepHashUniversalBle(value: withServices, hasher: &hasher)
deepHashUniversalBle(value: withNamePrefix, hasher: &hasher)
deepHashUniversalBle(value: withManufacturerData, hasher: &hasher)
}
}
@@ -481,9 +793,17 @@ struct UniversalManufacturerDataFilter: Hashable {
]
}
static func == (lhs: UniversalManufacturerDataFilter, rhs: UniversalManufacturerDataFilter) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.companyIdentifier, rhs.companyIdentifier) && deepEqualsUniversalBle(lhs.data, rhs.data) && deepEqualsUniversalBle(lhs.mask, rhs.mask)
}
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
hasher.combine("UniversalManufacturerDataFilter")
deepHashUniversalBle(value: companyIdentifier, hasher: &hasher)
deepHashUniversalBle(value: data, hasher: &hasher)
deepHashUniversalBle(value: mask, hasher: &hasher)
}
}
@@ -510,9 +830,16 @@ struct UniversalManufacturerData: Hashable {
]
}
static func == (lhs: UniversalManufacturerData, rhs: UniversalManufacturerData) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsUniversalBle(lhs.companyIdentifier, rhs.companyIdentifier) && deepEqualsUniversalBle(lhs.data, rhs.data)
}
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
hasher.combine("UniversalManufacturerData")
deepHashUniversalBle(value: companyIdentifier, hasher: &hasher)
deepHashUniversalBle(value: data, hasher: &hasher)
}
}
@@ -546,14 +873,24 @@ private class UniversalBlePigeonCodecReader: FlutterStandardReader {
case 135:
return UniversalBleDescriptor.fromList(self.readValue() as! [Any?])
case 136:
return AndroidOptions.fromList(self.readValue() as! [Any?])
return UniversalBlePeripheralConfig.fromList(self.readValue() as! [Any?])
case 137:
return UniversalScanConfig.fromList(self.readValue() as! [Any?])
return UniversalBlePeripheralService.fromList(self.readValue() as! [Any?])
case 138:
return UniversalScanFilter.fromList(self.readValue() as! [Any?])
return UniversalBlePeripheralCharacteristic.fromList(self.readValue() as! [Any?])
case 139:
return UniversalManufacturerDataFilter.fromList(self.readValue() as! [Any?])
return UniversalBlePeripheralDescriptor.fromList(self.readValue() as! [Any?])
case 140:
return UniversalBlePeripheralWriteEvent.fromList(self.readValue() as! [Any?])
case 141:
return AndroidOptions.fromList(self.readValue() as! [Any?])
case 142:
return UniversalScanConfig.fromList(self.readValue() as! [Any?])
case 143:
return UniversalScanFilter.fromList(self.readValue() as! [Any?])
case 144:
return UniversalManufacturerDataFilter.fromList(self.readValue() as! [Any?])
case 145:
return UniversalManufacturerData.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
@@ -584,21 +921,36 @@ private class UniversalBlePigeonCodecWriter: FlutterStandardWriter {
} else if let value = value as? UniversalBleDescriptor {
super.writeByte(135)
super.writeValue(value.toList())
} else if let value = value as? AndroidOptions {
} else if let value = value as? UniversalBlePeripheralConfig {
super.writeByte(136)
super.writeValue(value.toList())
} else if let value = value as? UniversalScanConfig {
} else if let value = value as? UniversalBlePeripheralService {
super.writeByte(137)
super.writeValue(value.toList())
} else if let value = value as? UniversalScanFilter {
} else if let value = value as? UniversalBlePeripheralCharacteristic {
super.writeByte(138)
super.writeValue(value.toList())
} else if let value = value as? UniversalManufacturerDataFilter {
} else if let value = value as? UniversalBlePeripheralDescriptor {
super.writeByte(139)
super.writeValue(value.toList())
} else if let value = value as? UniversalManufacturerData {
} else if let value = value as? UniversalBlePeripheralWriteEvent {
super.writeByte(140)
super.writeValue(value.toList())
} else if let value = value as? AndroidOptions {
super.writeByte(141)
super.writeValue(value.toList())
} else if let value = value as? UniversalScanConfig {
super.writeByte(142)
super.writeValue(value.toList())
} else if let value = value as? UniversalScanFilter {
super.writeByte(143)
super.writeValue(value.toList())
} else if let value = value as? UniversalManufacturerDataFilter {
super.writeByte(144)
super.writeValue(value.toList())
} else if let value = value as? UniversalManufacturerData {
super.writeByte(145)
super.writeValue(value.toList())
} else {
super.writeValue(value)
}
@@ -625,8 +977,8 @@ class UniversalBlePigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol UniversalBlePlatformChannel {
func getBluetoothAvailabilityState(completion: @escaping (Result<Int64, Error>) -> Void)
func hasPermissions(withAndroidFineLocation: Bool) throws -> Bool
func requestPermissions(withAndroidFineLocation: Bool, completion: @escaping (Result<Void, Error>) -> Void)
func hasPermissions(withAndroidFineLocation: Bool, withAndroidBluetoothAdvertise: Bool) throws -> Bool
func requestPermissions(withAndroidFineLocation: Bool, withAndroidBluetoothAdvertise: Bool, completion: @escaping (Result<Void, Error>) -> Void)
func enableBluetooth(completion: @escaping (Result<Bool, Error>) -> Void)
func disableBluetooth(completion: @escaping (Result<Bool, Error>) -> Void)
func startScan(filter: UniversalScanFilter?, config: UniversalScanConfig?) throws
@@ -646,6 +998,11 @@ protocol UniversalBlePlatformChannel {
func getConnectionState(deviceId: String) throws -> Int64
func readRssi(deviceId: String, completion: @escaping (Result<Int64, Error>) -> Void)
func requestConnectionPriority(deviceId: String, priority: Int64, completion: @escaping (Result<Void, Error>) -> Void)
func isPeripheralSupported() throws -> Bool
func startPeripheral(config: UniversalBlePeripheralConfig, completion: @escaping (Result<Void, Error>) -> Void)
func stopPeripheral(completion: @escaping (Result<Void, Error>) -> Void)
func updatePeripheralCharacteristicValue(service: String, characteristic: String, value: FlutterStandardTypedData, completion: @escaping (Result<Void, Error>) -> Void)
func notifyPeripheralCharacteristic(service: String, characteristic: String, value: FlutterStandardTypedData, indicate: Bool, completion: @escaping (Result<Void, Error>) -> Void)
func setLogLevel(logLevel: UniversalBleLogLevel) throws
}
@@ -675,8 +1032,9 @@ class UniversalBlePlatformChannelSetup {
hasPermissionsChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let withAndroidFineLocationArg = args[0] as! Bool
let withAndroidBluetoothAdvertiseArg = args[1] as! Bool
do {
let result = try api.hasPermissions(withAndroidFineLocation: withAndroidFineLocationArg)
let result = try api.hasPermissions(withAndroidFineLocation: withAndroidFineLocationArg, withAndroidBluetoothAdvertise: withAndroidBluetoothAdvertiseArg)
reply(wrapResult(result))
} catch {
reply(wrapError(error))
@@ -690,7 +1048,8 @@ class UniversalBlePlatformChannelSetup {
requestPermissionsChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let withAndroidFineLocationArg = args[0] as! Bool
api.requestPermissions(withAndroidFineLocation: withAndroidFineLocationArg) { result in
let withAndroidBluetoothAdvertiseArg = args[1] as! Bool
api.requestPermissions(withAndroidFineLocation: withAndroidFineLocationArg, withAndroidBluetoothAdvertise: withAndroidBluetoothAdvertiseArg) { result in
switch result {
case .success:
reply(wrapResult(nil))
@@ -1017,6 +1376,90 @@ class UniversalBlePlatformChannelSetup {
} else {
requestConnectionPriorityChannel.setMessageHandler(nil)
}
let isPeripheralSupportedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPeripheralSupported\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
isPeripheralSupportedChannel.setMessageHandler { _, reply in
do {
let result = try api.isPeripheralSupported()
reply(wrapResult(result))
} catch {
reply(wrapError(error))
}
}
} else {
isPeripheralSupportedChannel.setMessageHandler(nil)
}
let startPeripheralChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startPeripheral\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
startPeripheralChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let configArg = args[0] as! UniversalBlePeripheralConfig
api.startPeripheral(config: configArg) { result in
switch result {
case .success:
reply(wrapResult(nil))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
startPeripheralChannel.setMessageHandler(nil)
}
let stopPeripheralChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopPeripheral\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
stopPeripheralChannel.setMessageHandler { _, reply in
api.stopPeripheral { result in
switch result {
case .success:
reply(wrapResult(nil))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
stopPeripheralChannel.setMessageHandler(nil)
}
let updatePeripheralCharacteristicValueChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.updatePeripheralCharacteristicValue\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
updatePeripheralCharacteristicValueChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let serviceArg = args[0] as! String
let characteristicArg = args[1] as! String
let valueArg = args[2] as! FlutterStandardTypedData
api.updatePeripheralCharacteristicValue(service: serviceArg, characteristic: characteristicArg, value: valueArg) { result in
switch result {
case .success:
reply(wrapResult(nil))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
updatePeripheralCharacteristicValueChannel.setMessageHandler(nil)
}
let notifyPeripheralCharacteristicChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.notifyPeripheralCharacteristic\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
notifyPeripheralCharacteristicChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let serviceArg = args[0] as! String
let characteristicArg = args[1] as! String
let valueArg = args[2] as! FlutterStandardTypedData
let indicateArg = args[3] as! Bool
api.notifyPeripheralCharacteristic(service: serviceArg, characteristic: characteristicArg, value: valueArg, indicate: indicateArg) { result in
switch result {
case .success:
reply(wrapResult(nil))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
notifyPeripheralCharacteristicChannel.setMessageHandler(nil)
}
let setLogLevelChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
setLogLevelChannel.setMessageHandler { message, reply in
@@ -1043,6 +1486,9 @@ protocol UniversalBleCallbackChannelProtocol {
func onScanResult(result resultArg: UniversalBleScanResult, completion: @escaping (Result<Void, PigeonError>) -> Void)
func onValueChanged(deviceId deviceIdArg: String, characteristicId characteristicIdArg: String, value valueArg: FlutterStandardTypedData, timestamp timestampArg: Int64?, completion: @escaping (Result<Void, PigeonError>) -> Void)
func onConnectionChanged(deviceId deviceIdArg: String, connected connectedArg: Bool, error errorArg: String?, completion: @escaping (Result<Void, PigeonError>) -> Void)
func onPeripheralConnectionChanged(deviceId deviceIdArg: String, connected connectedArg: Bool, completion: @escaping (Result<Void, PigeonError>) -> Void)
func onPeripheralWrite(event eventArg: UniversalBlePeripheralWriteEvent, completion: @escaping (Result<Void, PigeonError>) -> Void)
func onPeripheralSubscriptionChanged(deviceId deviceIdArg: String, service serviceArg: String, characteristic characteristicArg: String, subscribed subscribedArg: Bool, completion: @escaping (Result<Void, PigeonError>) -> Void)
}
class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol {
private let binaryMessenger: FlutterBinaryMessenger
@@ -1144,4 +1590,58 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol {
}
}
}
func onPeripheralConnectionChanged(deviceId deviceIdArg: String, connected connectedArg: Bool, completion: @escaping (Result<Void, PigeonError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPeripheralConnectionChanged\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([deviceIdArg, connectedArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName: channelName)))
return
}
if listResponse.count > 1 {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(()))
}
}
}
func onPeripheralWrite(event eventArg: UniversalBlePeripheralWriteEvent, completion: @escaping (Result<Void, PigeonError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPeripheralWrite\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([eventArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName: channelName)))
return
}
if listResponse.count > 1 {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(()))
}
}
}
func onPeripheralSubscriptionChanged(deviceId deviceIdArg: String, service serviceArg: String, characteristic characteristicArg: String, subscribed subscribedArg: Bool, completion: @escaping (Result<Void, PigeonError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPeripheralSubscriptionChanged\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([deviceIdArg, serviceArg, characteristicArg, subscribedArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName: channelName)))
return
}
if listResponse.count > 1 {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(()))
}
}
}
}
@@ -58,11 +58,15 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
}
}
func hasPermissions(withAndroidFineLocation _: Bool) throws -> Bool {
func hasPermissions(withAndroidFineLocation _: Bool, withAndroidBluetoothAdvertise _: Bool) throws -> Bool {
return CBCentralManager.authorization == .allowedAlways
}
func requestPermissions(withAndroidFineLocation _: Bool, completion: @escaping (Result<Void, any Error>) -> Void) {
func requestPermissions(
withAndroidFineLocation _: Bool,
withAndroidBluetoothAdvertise _: Bool,
completion: @escaping (Result<Void, any Error>) -> Void
) {
if manager.state != .unknown {
completePermissionRequest(completion: completion)
} else {
@@ -379,6 +383,37 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
completion(.failure(createFlutterError(code: .notSupported, message: "requestConnectionPriority is not supported on Apple platforms")))
}
func isPeripheralSupported() throws -> Bool {
false
}
func startPeripheral(config _: UniversalBlePeripheralConfig, completion: @escaping (Result<Void, Error>) -> Void) {
completion(.failure(createFlutterError(code: .notSupported, message: "BLE peripheral mode is not implemented on Apple platforms yet")))
}
func stopPeripheral(completion: @escaping (Result<Void, Error>) -> Void) {
completion(.success(()))
}
func updatePeripheralCharacteristicValue(
service _: String,
characteristic _: String,
value _: FlutterStandardTypedData,
completion: @escaping (Result<Void, Error>) -> Void
) {
completion(.failure(createFlutterError(code: .notSupported, message: "BLE peripheral mode is not implemented on Apple platforms yet")))
}
func notifyPeripheralCharacteristic(
service _: String,
characteristic _: String,
value _: FlutterStandardTypedData,
indicate _: Bool,
completion: @escaping (Result<Void, Error>) -> Void
) {
completion(.failure(createFlutterError(code: .notSupported, message: "BLE peripheral mode is not implemented on Apple platforms yet")))
}
func readRssi(deviceId: String, completion: @escaping (Result<Int64, Error>) -> Void) {
UniversalBleLogger.shared.logDebug("READ_RSSI -> \(deviceId)")
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
@@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" />
+8 -10
View File
@@ -54,8 +54,7 @@ class MockUniversalBle extends UniversalBlePlatform {
}
@override
Future<List<BleService>> discoverServices(
String deviceId, bool withDescriptors) async {
Future<List<BleService>> discoverServices(String deviceId, bool withDescriptors) async {
return [_mockService];
}
@@ -87,11 +86,7 @@ class MockUniversalBle extends UniversalBlePlatform {
}
@override
Future<void> writeValue(
String deviceId,
String service,
String characteristic,
Uint8List value,
Future<void> writeValue(String deviceId, String service, String characteristic, Uint8List value,
BleOutputProperty bleOutputProperty) async {
await Future.delayed(const Duration(milliseconds: 500));
_serviceValue = value;
@@ -110,8 +105,8 @@ class MockUniversalBle extends UniversalBlePlatform {
) async {}
@override
Future<void> setNotifiable(String deviceId, String service,
String characteristic, BleInputProperty bleInputProperty) async {}
Future<void> setNotifiable(String deviceId, String service, String characteristic,
BleInputProperty bleInputProperty) async {}
@override
Future<bool> isPaired(String deviceId) async {
@@ -141,7 +136,10 @@ class MockUniversalBle extends UniversalBlePlatform {
}
@override
Future<void> requestPermissions({bool withAndroidFineLocation = false}) {
Future<void> requestPermissions({
bool withAndroidFineLocation = false,
bool withAndroidBluetoothAdvertise = false,
}) {
throw UnimplementedError();
}
+1 -1
View File
@@ -333,7 +333,7 @@ packages:
path: ".."
relative: true
source: path
version: "1.2.0"
version: "1.3.0"
vector_math:
dependency: transitive
description:
+88
View File
@@ -0,0 +1,88 @@
import 'dart:typed_data';
import 'package:universal_ble/universal_ble.dart';
enum BlePeripheralCharacteristicPermission { read, write }
class BlePeripheralConfig {
BlePeripheralConfig({required this.advertisedName, required this.services});
final String advertisedName;
final List<BlePeripheralService> services;
}
class BlePeripheralService {
BlePeripheralService({required String uuid, required this.characteristics})
: uuid = BleUuidParser.string(uuid);
final String uuid;
final List<BlePeripheralCharacteristic> characteristics;
}
class BlePeripheralCharacteristic {
BlePeripheralCharacteristic({
required String uuid,
required this.properties,
required this.permissions,
this.descriptors = const [],
Uint8List? initialValue,
}) : uuid = BleUuidParser.string(uuid),
initialValue = initialValue == null ? null : Uint8List.fromList(initialValue);
final String uuid;
final List<CharacteristicProperty> properties;
final List<BlePeripheralCharacteristicPermission> permissions;
final List<BlePeripheralDescriptor> descriptors;
final Uint8List? initialValue;
}
class BlePeripheralDescriptor {
BlePeripheralDescriptor({
required String uuid,
required this.permissions,
Uint8List? initialValue,
}) : uuid = BleUuidParser.string(uuid),
initialValue = initialValue == null ? null : Uint8List.fromList(initialValue);
final String uuid;
final List<BlePeripheralCharacteristicPermission> permissions;
final Uint8List? initialValue;
}
class BlePeripheralWriteEvent {
BlePeripheralWriteEvent({
required this.deviceId,
required String service,
required String characteristic,
required Uint8List value,
}) : service = BleUuidParser.string(service),
characteristic = BleUuidParser.string(characteristic),
value = Uint8List.fromList(value);
final String deviceId;
final String service;
final String characteristic;
final Uint8List value;
}
class BlePeripheralConnectionEvent {
BlePeripheralConnectionEvent({required this.deviceId, required this.connected});
final String deviceId;
final bool connected;
}
class BlePeripheralSubscriptionEvent {
BlePeripheralSubscriptionEvent({
required this.deviceId,
required String service,
required String characteristic,
required this.subscribed,
}) : service = BleUuidParser.string(service),
characteristic = BleUuidParser.string(characteristic);
final String deviceId;
final String service;
final String characteristic;
final bool subscribed;
}
+1
View File
@@ -13,3 +13,4 @@ export 'package:universal_ble/src/models/ble_device.dart';
export 'package:universal_ble/src/models/ble_command.dart';
export 'package:universal_ble/src/models/ble_capabilities.dart';
export 'package:universal_ble/src/models/ble_connection_priority.dart';
export 'package:universal_ble/src/models/ble_peripheral.dart';
+84 -113
View File
@@ -14,8 +14,7 @@ class UniversalBle {
static final BleCommandQueue _bleCommandQueue = BleCommandQueue();
/// Set custom platform specific implementation (e.g. for testing).
static void setInstance(UniversalBlePlatform instance) =>
_platform = instance;
static void setInstance(UniversalBlePlatform instance) => _platform = instance;
/// Set global timeout for all commands.
/// Default timeout is 10 seconds.
@@ -47,40 +46,36 @@ class UniversalBle {
static Stream<BleDevice> get scanStream => _platform.scanStream;
/// Bluetooth availability state stream
static Stream<AvailabilityState> get availabilityStream =>
_platform.availabilityStream;
static Stream<AvailabilityState> get availabilityStream => _platform.availabilityStream;
/// Connection stream of a device
static Stream<bool> connectionStream(String deviceId) =>
_platform.connectionStream(deviceId);
static Stream<bool> connectionStream(String deviceId) => _platform.connectionStream(deviceId);
/// Characteristic value stream
static Stream<Uint8List> characteristicValueStream(
String deviceId,
String characteristicId,
) => _platform.characteristicValueStream(deviceId, characteristicId);
static Stream<Uint8List> characteristicValueStream(String deviceId, String characteristicId) =>
_platform.characteristicValueStream(deviceId, characteristicId);
/// Pairing state stream
static Stream<bool> pairingStateStream(String deviceId) =>
_platform.pairingStateStream(deviceId);
static Stream<bool> pairingStateStream(String deviceId) => _platform.pairingStateStream(deviceId);
/// Get Bluetooth availability state.
/// To be notified of updates, set [onAvailabilityChange] listener.
static Future<AvailabilityState> getBluetoothAvailabilityState() async {
return await _bleCommandQueue.queueCommand(
() => _platform.getBluetoothAvailabilityState(),
);
return await _bleCommandQueue.queueCommand(() => _platform.getBluetoothAvailabilityState());
}
/// Check if has permissions.
/// [withAndroidFineLocation] is used to check fine location permission on Android 12+ (API 31+).
/// [withAndroidBluetoothAdvertise] is used to check Android 12+ advertise permission for peripheral mode.
/// On Android lower than 12, this method will check location permission regardless of the [withAndroidFineLocation] value.
/// `Windows`, `Linux` and `Web` will always return true.
static Future<bool> hasPermissions({
bool withAndroidFineLocation = false,
bool withAndroidBluetoothAdvertise = false,
}) async {
return _platform.hasPermissions(
withAndroidFineLocation: withAndroidFineLocation,
withAndroidBluetoothAdvertise: withAndroidBluetoothAdvertise,
);
}
@@ -88,13 +83,16 @@ class UniversalBle {
/// if all permissions are already granted or granted by user, this method will succeed.
/// it will throw exception if permissions are denied by user.
/// [withAndroidFineLocation] is used to request fine location permission on Android 12+ (API 31+).
/// [withAndroidBluetoothAdvertise] is used to request Android 12+ advertise permission for peripheral mode.
/// on Android lower than 12, this method will request location permission regardless of the [withAndroidFineLocation] value.
/// `Windows`, `Linux` and `Web` will always succeed.
static Future<void> requestPermissions({
bool withAndroidFineLocation = false,
bool withAndroidBluetoothAdvertise = false,
}) async {
return _platform.requestPermissions(
withAndroidFineLocation: withAndroidFineLocation,
withAndroidBluetoothAdvertise: withAndroidBluetoothAdvertise,
);
}
@@ -102,15 +100,9 @@ class UniversalBle {
/// Scan results will arrive in [onScanResult] listener.
/// It might throw errors if Bluetooth is not available.
/// `webRequestOptions` is supported on Web only.
static Future<void> startScan({
ScanFilter? scanFilter,
PlatformConfig? platformConfig,
}) async {
static Future<void> startScan({ScanFilter? scanFilter, PlatformConfig? platformConfig}) async {
return await _bleCommandQueue.queueCommandWithoutTimeout(
() => _platform.startScan(
scanFilter: scanFilter,
platformConfig: platformConfig,
),
() => _platform.startScan(scanFilter: scanFilter, platformConfig: platformConfig),
);
}
@@ -118,9 +110,7 @@ class UniversalBle {
/// Set [onScanResult] listener to `null` if you don't need it anymore.
/// It might throw errors if Bluetooth is not available.
static Future<void> stopScan() async {
return await _bleCommandQueue.queueCommandWithoutTimeout(
() => _platform.stopScan(),
);
return await _bleCommandQueue.queueCommandWithoutTimeout(() => _platform.stopScan());
}
/// Check if currently scanning for devices.
@@ -147,17 +137,14 @@ class UniversalBle {
bool autoConnect = false,
}) async {
timeout ??= const Duration(seconds: 60);
Completer<bool> completer = _connectionEventCompleter(
deviceId,
timeout: timeout,
);
Completer<bool> completer = _connectionEventCompleter(deviceId, timeout: timeout);
_platform
.connect(deviceId, connectionTimeout: timeout, autoConnect: autoConnect)
.catchError((error) {
if (completer.isCompleted) return;
completer.completeError(ConnectionException(error));
});
_platform.connect(deviceId, connectionTimeout: timeout, autoConnect: autoConnect).catchError((
error,
) {
if (completer.isCompleted) return;
completer.completeError(ConnectionException(error));
});
if (!await completer.future.timeout(timeout)) {
throw ConnectionException("Failed to connect");
@@ -176,17 +163,10 @@ class UniversalBle {
}
try {
Completer<bool> completer = _connectionEventCompleter(
deviceId,
timeout: timeout,
);
Completer<bool> completer = _connectionEventCompleter(deviceId, timeout: timeout);
await _bleCommandQueue
.queueCommand(
() => _platform.disconnect(deviceId),
timeout: timeout,
deviceId: deviceId,
)
.queueCommand(() => _platform.disconnect(deviceId), timeout: timeout, deviceId: deviceId)
.catchError((error) {
if (completer.isCompleted) return;
completer.completeError(ConnectionException(error));
@@ -204,9 +184,7 @@ class UniversalBle {
}
if (await completer.future.timeout(timeout)) {
UniversalLogger.logError(
"Device $deviceId is still connected after disconnect attempt",
);
UniversalLogger.logError("Device $deviceId is still connected after disconnect attempt");
}
} catch (e) {
UniversalLogger.logError("Disconnect failed: $e");
@@ -315,9 +293,7 @@ class UniversalBle {
BleUuidParser.string(service),
BleUuidParser.string(characteristic),
value,
withoutResponse
? BleOutputProperty.withoutResponse
: BleOutputProperty.withResponse,
withoutResponse ? BleOutputProperty.withoutResponse : BleOutputProperty.withResponse,
),
timeout: timeout,
deviceId: deviceId,
@@ -339,11 +315,7 @@ class UniversalBle {
///
/// **Best Practices:** Design for default ATT MTU (23 bytes), treat requests as
/// opportunistic, and implement fragmentation for larger payloads.
static Future<int> requestMtu(
String deviceId,
int expectedMtu, {
Duration? timeout,
}) async {
static Future<int> requestMtu(String deviceId, int expectedMtu, {Duration? timeout}) async {
return await _bleCommandQueue.queueCommand(
() => _platform.requestMtu(deviceId, expectedMtu),
timeout: timeout,
@@ -445,11 +417,7 @@ class UniversalBle {
///
/// On `Web/Windows` and `Web/Linux`, it does not work for devices that use `ConfirmOnly` pairing.
/// Can throw `PairingException`, `ConnectionException` or `PlatformException`.
static Future<void> pair(
String deviceId, {
BleCommand? pairingCommand,
Duration? timeout,
}) async {
static Future<void> pair(String deviceId, {BleCommand? pairingCommand, Duration? timeout}) async {
if (BleCapabilities.hasSystemPairingApi) {
bool paired = await _bleCommandQueue.queueCommand(
() => _platform.pair(deviceId),
@@ -461,11 +429,7 @@ class UniversalBle {
if (pairingCommand == null) {
UniversalLogger.logWarning("PairingCommand required to get result");
}
await _connectAndExecuteBleCommand(
deviceId,
pairingCommand,
timeout: timeout,
);
await _connectAndExecuteBleCommand(deviceId, pairingCommand, timeout: timeout);
}
}
@@ -497,10 +461,7 @@ class UniversalBle {
/// Returns connection state of the device.
/// All platforms will return `Connected/Disconnected` states.
/// `Android` and `Apple` can also return `Connecting/Disconnecting` states.
static Future<BleConnectionState> getConnectionState(
String deviceId, {
Duration? timeout,
}) async {
static Future<BleConnectionState> getConnectionState(String deviceId, {Duration? timeout}) async {
return await _bleCommandQueue.queueCommand(
() => _platform.getConnectionState(deviceId),
timeout: timeout,
@@ -511,10 +472,7 @@ class UniversalBle {
/// It might throw errors if Bluetooth is not available.
/// Not supported on `Web` and `Apple`.
static Future<bool> enableBluetooth({Duration? timeout}) async {
return await _bleCommandQueue.queueCommand(
() => _platform.enableBluetooth(),
timeout: timeout,
);
return await _bleCommandQueue.queueCommand(() => _platform.enableBluetooth(), timeout: timeout);
}
/// Disable Bluetooth.
@@ -540,8 +498,7 @@ class UniversalBle {
/// For this feature to work, you need to enable the `chrome://flags/#enable-experimental-web-platform-features` flag.
/// Not every browser supports this API yet.
/// Even if the browser supports it, sometimes it won't fire any advertisement events even though the device may be sending them.
static bool receivesAdvertisements(String deviceId) =>
_platform.receivesAdvertisements(deviceId);
static bool receivesAdvertisements(String deviceId) => _platform.receivesAdvertisements(deviceId);
/// Get Bluetooth state availability.
static set onAvailabilityChange(OnAvailabilityChange? onAvailabilityChange) {
@@ -555,21 +512,14 @@ class UniversalBle {
}
}
@Deprecated(
"Use [subscribeNotifications] or [subscribeIndications] or [unsubscribe] instead",
)
@Deprecated("Use [subscribeNotifications] or [subscribeIndications] or [unsubscribe] instead")
static Future<void> setNotifiable(
String deviceId,
String service,
String characteristic,
BleInputProperty bleInputProperty,
) async {
return _sendBleInputPropertyCommand(
deviceId,
service,
characteristic,
bleInputProperty,
);
return _sendBleInputPropertyCommand(deviceId, service, characteristic, bleInputProperty);
}
@Deprecated("Use [write] instead")
@@ -599,10 +549,7 @@ class UniversalBle {
return read(deviceId, service, characteristic, timeout: timeout);
}
static Completer<bool> _connectionEventCompleter(
String deviceId, {
Duration? timeout,
}) {
static Completer<bool> _connectionEventCompleter(String deviceId, {Duration? timeout}) {
timeout ??= const Duration(seconds: 60);
StreamSubscription? connectionSubscription;
Completer<bool> completer = Completer();
@@ -618,9 +565,7 @@ class UniversalBle {
completer.completeError(ConnectionException(error));
}
connectionSubscription = _platform
.bleConnectionUpdateStreamController
.stream
connectionSubscription = _platform.bleConnectionUpdateStreamController.stream
.where((e) => e.deviceId == deviceId)
.listen(
(e) {
@@ -681,10 +626,7 @@ class UniversalBle {
await connect(deviceId, timeout: timeout);
}
List<BleService> services = await discoverServices(
deviceId,
timeout: timeout,
);
List<BleService> services = await discoverServices(deviceId, timeout: timeout);
UniversalLogger.logInfo("Discovered services: ${services.length}");
if (bleCommand == null) {
@@ -737,10 +679,7 @@ class UniversalBle {
for (BleService service in services) {
if (BleUuidParser.compareStrings(service.uuid, bleCommand.service)) {
for (BleCharacteristic char in service.characteristics) {
if (BleUuidParser.compareStrings(
char.uuid,
bleCommand.characteristic,
)) {
if (BleUuidParser.compareStrings(char.uuid, bleCommand.characteristic)) {
characteristic = char;
break;
}
@@ -756,16 +695,10 @@ class UniversalBle {
bool? withoutResponse;
if (characteristic.properties.contains(CharacteristicProperty.write)) {
withoutResponse = false;
} else if (characteristic.properties.contains(
CharacteristicProperty.writeWithoutResponse,
)) {
} else if (characteristic.properties.contains(CharacteristicProperty.writeWithoutResponse)) {
withoutResponse = true;
} else if (!characteristic.properties.contains(
CharacteristicProperty.read,
)) {
throw PairingException(
"BleCommand does not support read or write operation",
);
} else if (!characteristic.properties.contains(CharacteristicProperty.read)) {
throw PairingException("BleCommand does not support read or write operation");
}
Uint8List? value = bleCommand.writeValue;
@@ -799,16 +732,14 @@ class UniversalBle {
_bleCommandQueue.onQueueUpdate = onQueueUpdate;
/// Get scan results.
static set onScanResult(OnScanResult? onScanResult) =>
_platform.onScanResult = onScanResult;
static set onScanResult(OnScanResult? onScanResult) => _platform.onScanResult = onScanResult;
/// Get connection state changes.
static set onConnectionChange(OnConnectionChange? onConnectionChange) =>
_platform.onConnectionChange = onConnectionChange;
/// Get characteristic value updates, after calling [subscribeNotifications] or [subscribeIndications]
static set onValueChange(OnValueChange? onValueChange) =>
_platform.onValueChange = onValueChange;
static set onValueChange(OnValueChange? onValueChange) => _platform.onValueChange = onValueChange;
/// Get pair state changes.
static set onPairingStateChange(OnPairingStateChange pairingStateChange) =>
@@ -822,3 +753,43 @@ class UniversalBle {
return UniversalBlePigeonChannel.instance;
}
}
class UniversalBlePeripheral {
static Stream<BlePeripheralConnectionEvent> get connectionStream =>
UniversalBle._platform.peripheralConnectionStream;
static Stream<BlePeripheralWriteEvent> get writeStream =>
UniversalBle._platform.peripheralWriteStream;
static Stream<BlePeripheralSubscriptionEvent> get subscriptionStream =>
UniversalBle._platform.peripheralSubscriptionStream;
static Future<bool> isSupported() => UniversalBle._platform.isPeripheralSupported();
static Future<void> start(BlePeripheralConfig config) =>
UniversalBle._platform.startPeripheral(config);
static Future<void> stop() => UniversalBle._platform.stopPeripheral();
static Future<void> updateCharacteristicValue(
String service,
String characteristic,
Uint8List value,
) => UniversalBle._platform.updatePeripheralCharacteristicValue(
BleUuidParser.string(service),
BleUuidParser.string(characteristic),
value,
);
static Future<void> notify(
String service,
String characteristic,
Uint8List value, {
bool indicate = false,
}) => UniversalBle._platform.notifyPeripheralCharacteristic(
BleUuidParser.string(service),
BleUuidParser.string(characteristic),
value,
indicate: indicate,
);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -5,8 +5,7 @@ import 'package:universal_ble/universal_ble.dart';
class UniversalBlePigeonChannel extends UniversalBlePlatform {
static UniversalBlePigeonChannel? _instance;
static UniversalBlePigeonChannel get instance =>
_instance ??= UniversalBlePigeonChannel._();
static UniversalBlePigeonChannel get instance => _instance ??= UniversalBlePigeonChannel._();
late final UniversalBleFilterUtil _bleFilter = UniversalBleFilterUtil();
UniversalBlePigeonChannel._() {
@@ -17,9 +16,7 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
@override
Future<AvailabilityState> getBluetoothAvailabilityState() async {
int state = await _executeWithErrorHandling(
() => _channel.getBluetoothAvailabilityState(),
);
int state = await _executeWithErrorHandling(() => _channel.getBluetoothAvailabilityState());
return AvailabilityState.parse(state);
}
@@ -40,10 +37,7 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
}
@override
Future<void> startScan({
ScanFilter? scanFilter,
PlatformConfig? platformConfig,
}) async {
Future<void> startScan({ScanFilter? scanFilter, PlatformConfig? platformConfig}) async {
await _ensureInitialized(platformConfig);
_bleFilter.scanFilter = scanFilter;
await _executeWithErrorHandling(
@@ -55,48 +49,32 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
}
@override
Future<void> stopScan() =>
_executeWithErrorHandling(() => _channel.stopScan());
Future<void> stopScan() => _executeWithErrorHandling(() => _channel.stopScan());
@override
Future<bool> isScanning() =>
_executeWithErrorHandling(() => _channel.isScanning());
Future<bool> isScanning() => _executeWithErrorHandling(() => _channel.isScanning());
@override
Future<BleConnectionState> getConnectionState(String deviceId) async {
int state = await _executeWithErrorHandling(
() => _channel.getConnectionState(deviceId),
);
int state = await _executeWithErrorHandling(() => _channel.getConnectionState(deviceId));
return BleConnectionState.parse(state);
}
@override
Future<void> connect(
String deviceId, {
Duration? connectionTimeout,
bool autoConnect = false,
}) => _executeWithErrorHandling(
() => _channel.connect(deviceId, autoConnect: autoConnect),
);
Future<void> connect(String deviceId, {Duration? connectionTimeout, bool autoConnect = false}) =>
_executeWithErrorHandling(() => _channel.connect(deviceId, autoConnect: autoConnect));
@override
Future<void> disconnect(String deviceId) =>
_executeWithErrorHandling(() => _channel.disconnect(deviceId));
@override
Future<List<BleService>> discoverServices(
String deviceId,
bool withDescriptors,
) async {
List<UniversalBleService?> universalBleServices =
await _executeWithErrorHandling(
() => _channel.discoverServices(deviceId, withDescriptors),
);
Future<List<BleService>> discoverServices(String deviceId, bool withDescriptors) async {
List<UniversalBleService?> universalBleServices = await _executeWithErrorHandling(
() => _channel.discoverServices(deviceId, withDescriptors),
);
return List<BleService>.from(
universalBleServices
.where((e) => e != null)
.map((e) => e!.toBleService(deviceId))
.toList(),
universalBleServices.where((e) => e != null).map((e) => e!.toBleService(deviceId)).toList(),
);
}
@@ -108,12 +86,7 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
BleInputProperty bleInputProperty,
) {
return _executeWithErrorHandling(
() => _channel.setNotifiable(
deviceId,
service,
characteristic,
bleInputProperty.index,
),
() => _channel.setNotifiable(deviceId, service, characteristic, bleInputProperty.index),
);
}
@@ -124,9 +97,7 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
String characteristic, {
final Duration? timeout,
}) {
return _executeWithErrorHandling(
() => _channel.readValue(deviceId, service, characteristic),
);
return _executeWithErrorHandling(() => _channel.readValue(deviceId, service, characteristic));
}
@override
@@ -138,32 +109,51 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
BleOutputProperty bleOutputProperty,
) {
return _executeWithErrorHandling(
() => _channel.writeValue(
deviceId,
service,
characteristic,
value,
bleOutputProperty.index,
),
() => _channel.writeValue(deviceId, service, characteristic, value, bleOutputProperty.index),
);
}
@override
Future<int> requestMtu(String deviceId, int expectedMtu) =>
_executeWithErrorHandling(
() => _channel.requestMtu(deviceId, expectedMtu),
);
_executeWithErrorHandling(() => _channel.requestMtu(deviceId, expectedMtu));
@override
Future<int> readRssi(String deviceId) =>
_executeWithErrorHandling(() => _channel.readRssi(deviceId));
@override
Future<void> requestConnectionPriority(
String deviceId,
BleConnectionPriority priority,
Future<void> requestConnectionPriority(String deviceId, BleConnectionPriority priority) =>
_executeWithErrorHandling(() => _channel.requestConnectionPriority(deviceId, priority.index));
@override
Future<bool> isPeripheralSupported() =>
_executeWithErrorHandling(() => _channel.isPeripheralSupported());
@override
Future<void> startPeripheral(BlePeripheralConfig config) => _executeWithErrorHandling(
() => _channel.startPeripheral(config.toUniversalPeripheralConfig()),
);
@override
Future<void> stopPeripheral() => _executeWithErrorHandling(() => _channel.stopPeripheral());
@override
Future<void> updatePeripheralCharacteristicValue(
String service,
String characteristic,
Uint8List value,
) => _executeWithErrorHandling(
() => _channel.requestConnectionPriority(deviceId, priority.index),
() => _channel.updatePeripheralCharacteristicValue(service, characteristic, value),
);
@override
Future<void> notifyPeripheralCharacteristic(
String service,
String characteristic,
Uint8List value, {
bool indicate = false,
}) => _executeWithErrorHandling(
() => _channel.notifyPeripheralCharacteristic(service, characteristic, value, indicate),
);
@override
@@ -171,26 +161,29 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
_executeWithErrorHandling(() => _channel.isPaired(deviceId));
@override
Future<bool> pair(String deviceId) =>
_executeWithErrorHandling(() => _channel.pair(deviceId));
Future<bool> pair(String deviceId) => _executeWithErrorHandling(() => _channel.pair(deviceId));
@override
Future<void> unpair(String deviceId) =>
_executeWithErrorHandling(() => _channel.unPair(deviceId));
@override
Future<bool> hasPermissions({bool withAndroidFineLocation = false}) async {
Future<bool> hasPermissions({
bool withAndroidFineLocation = false,
bool withAndroidBluetoothAdvertise = false,
}) async {
return await _executeWithErrorHandling(
() => _channel.hasPermissions(withAndroidFineLocation),
() => _channel.hasPermissions(withAndroidFineLocation, withAndroidBluetoothAdvertise),
);
}
@override
Future<void> requestPermissions({
bool withAndroidFineLocation = false,
bool withAndroidBluetoothAdvertise = false,
}) async {
await _executeWithErrorHandling(
() => _channel.requestPermissions(withAndroidFineLocation),
() => _channel.requestPermissions(withAndroidFineLocation, withAndroidBluetoothAdvertise),
);
}
@@ -199,15 +192,12 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
var devices = await _executeWithErrorHandling(
() => _channel.getSystemDevices(withServices ?? []),
);
return List<BleDevice>.from(
devices.map((e) => e.toBleDevice(isSystemDevice: true)).toList(),
);
return List<BleDevice>.from(devices.map((e) => e.toBleDevice(isSystemDevice: true)).toList());
}
@override
Future<void> setLogLevel(BleLogLevel logLevel) => _executeWithErrorHandling(
() => _channel.setLogLevel(logLevel.toUniversalBleLogLevel()),
);
Future<void> setLogLevel(BleLogLevel logLevel) =>
_executeWithErrorHandling(() => _channel.setLogLevel(logLevel.toUniversalBleLogLevel()));
/// To set listeners
void _setupListeners() {
@@ -223,6 +213,9 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
connectionChanged: updateConnection,
valueChanged: updateCharacteristicValue,
pairStateChange: updatePairingState,
peripheralConnectionChanged: updatePeripheralConnection,
peripheralWrite: updatePeripheralWrite,
peripheralSubscriptionChanged: updatePeripheralSubscription,
),
);
}
@@ -246,8 +239,7 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.macOS) {
await requestPermissions(
withAndroidFineLocation:
platformConfig?.android?.requestLocationPermission ?? false,
withAndroidFineLocation: platformConfig?.android?.requestLocationPermission ?? false,
);
}
}
@@ -277,12 +269,70 @@ extension _BleServiceExtension on UniversalBleService {
}
}
extension _BlePeripheralConfigExtension on BlePeripheralConfig {
UniversalBlePeripheralConfig toUniversalPeripheralConfig() {
return UniversalBlePeripheralConfig(
advertisedName: advertisedName,
services: services.map((service) => service.toUniversalPeripheralService()).toList(),
);
}
}
extension _BlePeripheralServiceExtension on BlePeripheralService {
UniversalBlePeripheralService toUniversalPeripheralService() {
return UniversalBlePeripheralService(
uuid: uuid,
characteristics: characteristics
.map((characteristic) => characteristic.toUniversalPeripheralCharacteristic())
.toList(),
);
}
}
extension _BlePeripheralCharacteristicExtension on BlePeripheralCharacteristic {
UniversalBlePeripheralCharacteristic toUniversalPeripheralCharacteristic() {
return UniversalBlePeripheralCharacteristic(
uuid: uuid,
properties: properties.map((property) => property.index).toList(),
permissions: permissions.map((permission) => permission.index).toList(),
descriptors: descriptors
.map((descriptor) => descriptor.toUniversalPeripheralDescriptor())
.toList(),
initialValue: initialValue,
);
}
}
extension _BlePeripheralDescriptorExtension on BlePeripheralDescriptor {
UniversalBlePeripheralDescriptor toUniversalPeripheralDescriptor() {
return UniversalBlePeripheralDescriptor(
uuid: uuid,
permissions: permissions.map((permission) => permission.index).toList(),
initialValue: initialValue,
);
}
}
extension _UniversalBlePeripheralWriteEventExtension on UniversalBlePeripheralWriteEvent {
BlePeripheralWriteEvent toBlePeripheralWriteEvent() {
return BlePeripheralWriteEvent(
deviceId: deviceId,
service: service,
characteristic: characteristic,
value: value,
);
}
}
class _UniversalBleCallbackHandler extends UniversalBleCallbackChannel {
OnAvailabilityChange availabilityChange;
OnScanResult scanResult;
OnConnectionChange connectionChanged;
OnValueChange valueChanged;
OnPairingStateChange pairStateChange;
void Function(String deviceId, bool connected) peripheralConnectionChanged;
void Function(BlePeripheralWriteEvent event) peripheralWrite;
void Function(BlePeripheralSubscriptionEvent event) peripheralSubscriptionChanged;
_UniversalBleCallbackHandler({
required this.availabilityChange,
@@ -290,31 +340,51 @@ class _UniversalBleCallbackHandler extends UniversalBleCallbackChannel {
required this.connectionChanged,
required this.valueChanged,
required this.pairStateChange,
required this.peripheralConnectionChanged,
required this.peripheralWrite,
required this.peripheralSubscriptionChanged,
});
@override
void onAvailabilityChanged(int state) =>
availabilityChange(AvailabilityState.parse(state));
void onAvailabilityChanged(int state) => availabilityChange(AvailabilityState.parse(state));
@override
void onConnectionChanged(String deviceId, bool connected, String? error) =>
connectionChanged(deviceId, connected, error);
@override
void onScanResult(UniversalBleScanResult result) =>
scanResult(result.toBleDevice());
void onScanResult(UniversalBleScanResult result) => scanResult(result.toBleDevice());
@override
void onValueChanged(
String deviceId,
String characteristicId,
Uint8List value,
int? timestamp,
) => valueChanged(deviceId, characteristicId, value, timestamp);
void onValueChanged(String deviceId, String characteristicId, Uint8List value, int? timestamp) =>
valueChanged(deviceId, characteristicId, value, timestamp);
@override
void onPairStateChange(String deviceId, bool isPaired, String? error) =>
pairStateChange(deviceId, isPaired);
@override
void onPeripheralConnectionChanged(String deviceId, bool connected) =>
peripheralConnectionChanged(deviceId, connected);
@override
void onPeripheralWrite(UniversalBlePeripheralWriteEvent event) =>
peripheralWrite(event.toBlePeripheralWriteEvent());
@override
void onPeripheralSubscriptionChanged(
String deviceId,
String service,
String characteristic,
bool subscribed,
) => peripheralSubscriptionChanged(
BlePeripheralSubscriptionEvent(
deviceId: deviceId,
service: service,
characteristic: characteristic,
subscribed: subscribed,
),
);
}
extension _UniversalBleScanResultExtension on UniversalBleScanResult {
@@ -339,8 +409,7 @@ extension _UniversalBleScanResultExtension on UniversalBleScanResult {
extension _ScanFilterExtension on ScanFilter? {
UniversalScanFilter? toUniversalScanFilter() {
List<UniversalManufacturerDataFilter>? manufacturerDataFilters = this
?.withManufacturerData
List<UniversalManufacturerDataFilter>? manufacturerDataFilters = this?.withManufacturerData
.map(
(e) => UniversalManufacturerDataFilter(
companyIdentifier: e.companyIdentifier,
+83 -54
View File
@@ -17,23 +17,26 @@ abstract class UniversalBlePlatform {
final _scanStreamController = UniversalBleStreamController<BleDevice>();
final bleConnectionUpdateStreamController =
UniversalBleStreamController<
({String deviceId, bool isConnected, String? error})
>();
UniversalBleStreamController<({String deviceId, bool isConnected, String? error})>();
final _valueStreamController =
UniversalBleStreamController<
({String deviceId, String characteristicId, Uint8List value})
>();
UniversalBleStreamController<({String deviceId, String characteristicId, Uint8List value})>();
final _pairStateStreamController =
UniversalBleStreamController<({String deviceId, bool isPaired})>();
final _peripheralConnectionStreamController =
UniversalBleStreamController<BlePeripheralConnectionEvent>();
final _peripheralWriteStreamController = UniversalBleStreamController<BlePeripheralWriteEvent>();
final _peripheralSubscriptionStreamController =
UniversalBleStreamController<BlePeripheralSubscriptionEvent>();
/// Send latest availability state upon subscribing
late final _availabilityStreamController =
UniversalBleStreamController<AvailabilityState>(
initialEvent: getBluetoothAvailabilityState,
);
late final _availabilityStreamController = UniversalBleStreamController<AvailabilityState>(
initialEvent: getBluetoothAvailabilityState,
);
Future<AvailabilityState> getBluetoothAvailabilityState();
@@ -41,35 +44,29 @@ abstract class UniversalBlePlatform {
Future<bool> disableBluetooth();
Future<bool> hasPermissions({bool withAndroidFineLocation = false}) async {
Future<bool> hasPermissions({
bool withAndroidFineLocation = false,
bool withAndroidBluetoothAdvertise = false,
}) async {
return true;
}
Future<void> requestPermissions({
bool withAndroidFineLocation = false,
bool withAndroidBluetoothAdvertise = false,
}) async {}
Future<void> startScan({
ScanFilter? scanFilter,
PlatformConfig? platformConfig,
});
Future<void> startScan({ScanFilter? scanFilter, PlatformConfig? platformConfig});
Future<void> stopScan();
Future<bool> isScanning();
Future<void> connect(
String deviceId, {
Duration? connectionTimeout,
bool autoConnect = false,
});
Future<void> connect(String deviceId, {Duration? connectionTimeout, bool autoConnect = false});
Future<void> disconnect(String deviceId);
Future<List<BleService>> discoverServices(
String deviceId,
bool withDescriptors,
);
Future<List<BleService>> discoverServices(String deviceId, bool withDescriptors);
Future<void> setNotifiable(
String deviceId,
@@ -97,10 +94,34 @@ abstract class UniversalBlePlatform {
Future<int> readRssi(String deviceId);
Future<void> requestConnectionPriority(
String deviceId,
BleConnectionPriority priority,
);
Future<void> requestConnectionPriority(String deviceId, BleConnectionPriority priority);
Future<bool> isPeripheralSupported() async => false;
Future<void> startPeripheral(BlePeripheralConfig config) {
throw UnsupportedError('BLE peripheral mode is not supported');
}
Future<void> stopPeripheral() {
throw UnsupportedError('BLE peripheral mode is not supported');
}
Future<void> updatePeripheralCharacteristicValue(
String service,
String characteristic,
Uint8List value,
) {
throw UnsupportedError('BLE peripheral mode is not supported');
}
Future<void> notifyPeripheralCharacteristic(
String service,
String characteristic,
Uint8List value, {
bool indicate = false,
}) {
throw UnsupportedError('BLE peripheral mode is not supported');
}
Future<bool> isPaired(String deviceId);
@@ -112,39 +133,39 @@ abstract class UniversalBlePlatform {
Future<List<BleDevice>> getSystemDevices(List<String>? withServices);
Future<void> setLogLevel(BleLogLevel logLevel) async =>
UniversalLogger.setLogLevel(logLevel);
Future<void> setLogLevel(BleLogLevel logLevel) async => UniversalLogger.setLogLevel(logLevel);
bool receivesAdvertisements(String deviceId) => true;
/// Streams
Stream<BleDevice> get scanStream => _scanStreamController.stream;
Stream<AvailabilityState> get availabilityStream =>
_availabilityStreamController.stream;
Stream<AvailabilityState> get availabilityStream => _availabilityStreamController.stream;
Stream<bool> connectionStream(String deviceId) =>
bleConnectionUpdateStreamController.stream
.where((e) => e.deviceId == deviceId)
.map((e) => e.isConnected);
Stream<bool> connectionStream(String deviceId) => bleConnectionUpdateStreamController.stream
.where((e) => e.deviceId == deviceId)
.map((e) => e.isConnected);
Stream<Uint8List> characteristicValueStream(
String deviceId,
String characteristicId,
) {
Stream<Uint8List> characteristicValueStream(String deviceId, String characteristicId) {
characteristicId = BleUuidParser.string(characteristicId);
return _valueStreamController.stream
.where((e) {
return e.deviceId == deviceId &&
e.characteristicId == characteristicId;
return e.deviceId == deviceId && e.characteristicId == characteristicId;
})
.map((e) => e.value);
}
Stream<bool> pairingStateStream(String deviceId) => _pairStateStreamController
.stream
.where((e) => e.deviceId == deviceId)
.map((e) => e.isPaired);
Stream<bool> pairingStateStream(String deviceId) =>
_pairStateStreamController.stream.where((e) => e.deviceId == deviceId).map((e) => e.isPaired);
Stream<BlePeripheralConnectionEvent> get peripheralConnectionStream =>
_peripheralConnectionStreamController.stream;
Stream<BlePeripheralWriteEvent> get peripheralWriteStream =>
_peripheralWriteStreamController.stream;
Stream<BlePeripheralSubscriptionEvent> get peripheralSubscriptionStream =>
_peripheralSubscriptionStreamController.stream;
/// Update Handlers
void updateScanResult(BleDevice bleDevice) {
@@ -206,19 +227,27 @@ abstract class UniversalBlePlatform {
onPairingStateChange?.call(deviceId, isPaired);
} catch (_) {}
}
void updatePeripheralConnection(String deviceId, bool connected) {
_peripheralConnectionStreamController.add(
BlePeripheralConnectionEvent(deviceId: deviceId, connected: connected),
);
}
void updatePeripheralWrite(BlePeripheralWriteEvent event) {
_peripheralWriteStreamController.add(event);
}
void updatePeripheralSubscription(BlePeripheralSubscriptionEvent event) {
_peripheralSubscriptionStreamController.add(event);
}
}
// Callback types
typedef OnConnectionChange =
void Function(String deviceId, bool isConnected, String? error);
typedef OnConnectionChange = void Function(String deviceId, bool isConnected, String? error);
typedef OnValueChange =
void Function(
String deviceId,
String characteristicId,
Uint8List value,
int? timestamp,
);
void Function(String deviceId, String characteristicId, Uint8List value, int? timestamp);
typedef OnScanResult = void Function(BleDevice scanResult);
+90 -65
View File
@@ -6,8 +6,7 @@ import 'package:pigeon/pigeon.dart';
dartPackageName: 'universal_ble',
dartOut: 'lib/src/universal_ble_pigeon/universal_ble.g.dart',
dartOptions: DartOptions(),
kotlinOut:
'android/src/main/kotlin/com/navideck/universal_ble/UniversalBle.g.kt',
kotlinOut: 'android/src/main/kotlin/com/navideck/universal_ble/UniversalBle.g.kt',
swiftOut: 'darwin/universal_ble/Sources/universal_ble/UniversalBle.g.swift',
kotlinOptions: KotlinOptions(package: 'com.navideck.universal_ble'),
swiftOptions: SwiftOptions(),
@@ -17,17 +16,16 @@ import 'package:pigeon/pigeon.dart';
debugGenerators: true,
),
)
/// Flutter -> Native
@HostApi()
abstract class UniversalBlePlatformChannel {
@async
int getBluetoothAvailabilityState();
bool hasPermissions(bool withAndroidFineLocation);
bool hasPermissions(bool withAndroidFineLocation, bool withAndroidBluetoothAdvertise);
@async
void requestPermissions(bool withAndroidFineLocation);
void requestPermissions(bool withAndroidFineLocation, bool withAndroidBluetoothAdvertise);
@async
bool enableBluetooth();
@@ -46,25 +44,13 @@ abstract class UniversalBlePlatformChannel {
void disconnect(String deviceId);
@async
void setNotifiable(
String deviceId,
String service,
String characteristic,
int bleInputProperty,
);
void setNotifiable(String deviceId, String service, String characteristic, int bleInputProperty);
@async
List<UniversalBleService> discoverServices(
String deviceId,
bool withDescriptors,
);
List<UniversalBleService> discoverServices(String deviceId, bool withDescriptors);
@async
Uint8List readValue(
String deviceId,
String service,
String characteristic,
);
Uint8List readValue(String deviceId, String service, String characteristic);
@async
int requestMtu(String deviceId, int expectedMtu);
@@ -87,9 +73,7 @@ abstract class UniversalBlePlatformChannel {
void unPair(String deviceId);
@async
List<UniversalBleScanResult> getSystemDevices(
List<String> withServices,
);
List<UniversalBleScanResult> getSystemDevices(List<String> withServices);
int getConnectionState(String deviceId);
@@ -99,6 +83,25 @@ abstract class UniversalBlePlatformChannel {
@async
void requestConnectionPriority(String deviceId, int priority);
bool isPeripheralSupported();
@async
void startPeripheral(UniversalBlePeripheralConfig config);
@async
void stopPeripheral();
@async
void updatePeripheralCharacteristicValue(String service, String characteristic, Uint8List value);
@async
void notifyPeripheralCharacteristic(
String service,
String characteristic,
Uint8List value,
bool indicate,
);
void setLogLevel(UniversalBleLogLevel logLevel);
}
@@ -111,17 +114,19 @@ abstract class UniversalBleCallbackChannel {
void onScanResult(UniversalBleScanResult result);
void onValueChanged(
String deviceId,
String characteristicId,
Uint8List value,
int? timestamp,
);
void onValueChanged(String deviceId, String characteristicId, Uint8List value, int? timestamp);
void onConnectionChanged(
void onConnectionChanged(String deviceId, bool connected, String? error);
void onPeripheralConnectionChanged(String deviceId, bool connected);
void onPeripheralWrite(UniversalBlePeripheralWriteEvent event);
void onPeripheralSubscriptionChanged(
String deviceId,
bool connected,
String? error,
String service,
String characteristic,
bool subscribed,
);
}
@@ -147,14 +152,7 @@ class UniversalBleScanResult {
});
}
enum UniversalBleLogLevel {
none,
error,
warning,
info,
debug,
verbose,
}
enum UniversalBleLogLevel { none, error, warning, info, debug, verbose }
class UniversalBleService {
String uuid;
@@ -174,14 +172,56 @@ class UniversalBleDescriptor {
UniversalBleDescriptor(this.uuid);
}
class UniversalBlePeripheralConfig {
String advertisedName;
List<UniversalBlePeripheralService> services;
UniversalBlePeripheralConfig(this.advertisedName, this.services);
}
class UniversalBlePeripheralService {
String uuid;
List<UniversalBlePeripheralCharacteristic> characteristics;
UniversalBlePeripheralService(this.uuid, this.characteristics);
}
class UniversalBlePeripheralCharacteristic {
String uuid;
List<int> properties;
List<int> permissions;
List<UniversalBlePeripheralDescriptor> descriptors;
Uint8List? initialValue;
UniversalBlePeripheralCharacteristic(
this.uuid,
this.properties,
this.permissions,
this.descriptors,
this.initialValue,
);
}
class UniversalBlePeripheralDescriptor {
String uuid;
List<int> permissions;
Uint8List? initialValue;
UniversalBlePeripheralDescriptor(this.uuid, this.permissions, this.initialValue);
}
class UniversalBlePeripheralWriteEvent {
String deviceId;
String service;
String characteristic;
Uint8List value;
UniversalBlePeripheralWriteEvent(this.deviceId, this.service, this.characteristic, this.value);
}
/// Scan config
enum AndroidScanMode {
balanced,
lowLatency,
lowPower,
opportunistic,
}
enum AndroidScanMode { balanced, lowLatency, lowPower, opportunistic }
/// Android options to scan devices
/// [requestLocationPermission] is used to request location permission on Android 12+ (API 31+).
@@ -193,11 +233,7 @@ class AndroidOptions {
bool? requestLocationPermission;
AndroidScanMode? scanMode;
int? reportDelayMillis;
AndroidOptions({
this.requestLocationPermission,
this.scanMode,
this.reportDelayMillis,
});
AndroidOptions({this.requestLocationPermission, this.scanMode, this.reportDelayMillis});
}
class UniversalScanConfig {
@@ -211,32 +247,21 @@ class UniversalScanFilter {
final List<String> withNamePrefix;
final List<UniversalManufacturerDataFilter> withManufacturerData;
UniversalScanFilter(
this.withServices,
this.withNamePrefix,
this.withManufacturerData,
);
UniversalScanFilter(this.withServices, this.withNamePrefix, this.withManufacturerData);
}
class UniversalManufacturerDataFilter {
int companyIdentifier;
Uint8List? data;
Uint8List? mask;
UniversalManufacturerDataFilter({
required this.companyIdentifier,
this.data,
this.mask,
});
UniversalManufacturerDataFilter({required this.companyIdentifier, this.data, this.mask});
}
class UniversalManufacturerData {
final int companyIdentifier;
final Uint8List data;
UniversalManufacturerData({
required this.companyIdentifier,
required this.data,
});
UniversalManufacturerData({required this.companyIdentifier, required this.data});
}
/// Unified error codes for all platforms
+1
View File
@@ -33,6 +33,7 @@ dependencies:
plugin_platform_interface: ^2.1.8
flutter_web_bluetooth: ^1.1.0
bluez: ^0.8.3
dbus: ^0.7.11
dev_dependencies:
flutter_test:
+25 -25
View File
@@ -26,8 +26,7 @@ void main() {
group('BleCharacteristic Tests', () {
test("DiscoverServices test", () async {
debugPrint("Discovering services");
List<BleService> services =
await UniversalBle.discoverServices(mockDeviceId);
List<BleService> services = await UniversalBle.discoverServices(mockDeviceId);
expect(services.length, 1);
BleService service = services.first;
@@ -35,18 +34,9 @@ void main() {
expect(service.characteristics.length, 1);
BleCharacteristic characteristic = service.characteristics.first;
expect(
BleUuidParser.compareStrings(characteristic.uuid, characteristicId),
true,
);
expect(BleUuidParser.compareStrings(characteristic.uuid, characteristicId), true);
expect(characteristic.metaData?.deviceId, mockDeviceId);
expect(
BleUuidParser.compareStrings(
characteristic.metaData!.serviceId,
serviceId,
),
true,
);
expect(BleUuidParser.compareStrings(characteristic.metaData!.serviceId, serviceId), true);
});
test("Subscription Test", () async {
@@ -87,14 +77,17 @@ class _UniversalBleMock extends UniversalBlePlatformMock {
Uint8List? charValue;
@override
Future<List<BleService>> discoverServices(
String deviceId, bool withDescriptors) async {
Future<List<BleService>> discoverServices(String deviceId, bool withDescriptors) async {
return <BleService>[mockBleService];
}
@override
Future<void> setNotifiable(String deviceId, String service,
String characteristic, BleInputProperty bleInputProperty) async {
Future<void> setNotifiable(
String deviceId,
String service,
String characteristic,
BleInputProperty bleInputProperty,
) async {
if (bleInputProperty == BleInputProperty.disabled) {
notifierTimer?.cancel();
notifierTimer = null;
@@ -113,23 +106,30 @@ class _UniversalBleMock extends UniversalBlePlatformMock {
@override
Future<void> writeValue(
String deviceId,
String service,
String characteristic,
Uint8List value,
BleOutputProperty bleOutputProperty) async {
String deviceId,
String service,
String characteristic,
Uint8List value,
BleOutputProperty bleOutputProperty,
) async {
charValue = value;
}
@override
Future<Uint8List> readValue(
String deviceId, String service, String characteristic,
{Duration? timeout}) async {
String deviceId,
String service,
String characteristic, {
Duration? timeout,
}) async {
return charValue ?? Uint8List(0);
}
@override
Future<void> requestPermissions({bool withAndroidFineLocation = false}) {
Future<void> requestPermissions({
bool withAndroidFineLocation = false,
bool withAndroidBluetoothAdvertise = false,
}) {
throw UnimplementedError();
}
+45
View File
@@ -0,0 +1,45 @@
import 'dart:typed_data';
import 'package:flutter_test/flutter_test.dart';
import 'package:universal_ble/universal_ble.dart';
void main() {
group('BlePeripheral models', () {
test('normalizes UUIDs and defensively copies characteristic values', () {
final initialValue = Uint8List.fromList([1, 2, 3]);
final characteristic = BlePeripheralCharacteristic(
uuid: 'fff1',
properties: const [CharacteristicProperty.read],
permissions: const [BlePeripheralCharacteristicPermission.read],
initialValue: initialValue,
);
initialValue[0] = 9;
expect(
BleUuidParser.compareStrings(characteristic.uuid, '0000fff1-0000-1000-8000-00805f9b34fb'),
isTrue,
);
expect(characteristic.initialValue, [1, 2, 3]);
});
test('normalizes write event UUIDs and defensively copies payloads', () {
final value = Uint8List.fromList([4, 5, 6]);
final event = BlePeripheralWriteEvent(
deviceId: 'client-1',
service: 'fff0',
characteristic: 'fff1',
value: value,
);
value[0] = 8;
expect(event.deviceId, 'client-1');
expect(BleUuidParser.compareStrings(event.service, 'fff0'), isTrue);
expect(BleUuidParser.compareStrings(event.characteristic, 'fff1'), isTrue);
expect(event.value, [4, 5, 6]);
});
});
}
File diff suppressed because it is too large Load Diff
+340 -80
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.1.4), do not edit directly.
// Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#ifndef PIGEON_UNIVERSAL_BLE_G_H_
@@ -23,17 +23,17 @@ class FlutterError {
: code_(code) {}
explicit FlutterError(const std::string& code, const std::string& message)
: code_(code), message_(message) {}
explicit FlutterError(const std::string& code, const std::string& message, const flutter::EncodableValue& details)
explicit FlutterError(const std::string& code, const std::string& message, const ::flutter::EncodableValue& details)
: code_(code), message_(message), details_(details) {}
const std::string& code() const { return code_; }
const std::string& message() const { return message_; }
const flutter::EncodableValue& details() const { return details_; }
const ::flutter::EncodableValue& details() const { return details_; }
private:
std::string code_;
std::string message_;
flutter::EncodableValue details_;
::flutter::EncodableValue details_;
};
template<class T> class ErrorOr {
@@ -152,9 +152,9 @@ class UniversalBleScanResult {
const std::string* name,
const bool* is_paired,
const int64_t* rssi,
const flutter::EncodableList* manufacturer_data_list,
const flutter::EncodableMap* service_data,
const flutter::EncodableList* services,
const ::flutter::EncodableList* manufacturer_data_list,
const ::flutter::EncodableMap* service_data,
const ::flutter::EncodableList* services,
const int64_t* timestamp);
const std::string& device_id() const;
@@ -172,25 +172,29 @@ class UniversalBleScanResult {
void set_rssi(const int64_t* value_arg);
void set_rssi(int64_t value_arg);
const flutter::EncodableList* manufacturer_data_list() const;
void set_manufacturer_data_list(const flutter::EncodableList* value_arg);
void set_manufacturer_data_list(const flutter::EncodableList& value_arg);
const ::flutter::EncodableList* manufacturer_data_list() const;
void set_manufacturer_data_list(const ::flutter::EncodableList* value_arg);
void set_manufacturer_data_list(const ::flutter::EncodableList& value_arg);
const flutter::EncodableMap* service_data() const;
void set_service_data(const flutter::EncodableMap* value_arg);
void set_service_data(const flutter::EncodableMap& value_arg);
const ::flutter::EncodableMap* service_data() const;
void set_service_data(const ::flutter::EncodableMap* value_arg);
void set_service_data(const ::flutter::EncodableMap& value_arg);
const flutter::EncodableList* services() const;
void set_services(const flutter::EncodableList* value_arg);
void set_services(const flutter::EncodableList& value_arg);
const ::flutter::EncodableList* services() const;
void set_services(const ::flutter::EncodableList* value_arg);
void set_services(const ::flutter::EncodableList& value_arg);
const int64_t* timestamp() const;
void set_timestamp(const int64_t* value_arg);
void set_timestamp(int64_t value_arg);
bool operator==(const UniversalBleScanResult& other) const;
bool operator!=(const UniversalBleScanResult& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalBleScanResult FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
static UniversalBleScanResult FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
@@ -198,9 +202,9 @@ class UniversalBleScanResult {
std::optional<std::string> name_;
std::optional<bool> is_paired_;
std::optional<int64_t> rssi_;
std::optional<flutter::EncodableList> manufacturer_data_list_;
std::optional<flutter::EncodableMap> service_data_;
std::optional<flutter::EncodableList> services_;
std::optional<::flutter::EncodableList> manufacturer_data_list_;
std::optional<::flutter::EncodableMap> service_data_;
std::optional<::flutter::EncodableList> services_;
std::optional<int64_t> timestamp_;
};
@@ -214,23 +218,27 @@ class UniversalBleService {
// Constructs an object setting all fields.
explicit UniversalBleService(
const std::string& uuid,
const flutter::EncodableList* characteristics);
const ::flutter::EncodableList* characteristics);
const std::string& uuid() const;
void set_uuid(std::string_view value_arg);
const flutter::EncodableList* characteristics() const;
void set_characteristics(const flutter::EncodableList* value_arg);
void set_characteristics(const flutter::EncodableList& value_arg);
const ::flutter::EncodableList* characteristics() const;
void set_characteristics(const ::flutter::EncodableList* value_arg);
void set_characteristics(const ::flutter::EncodableList& value_arg);
bool operator==(const UniversalBleService& other) const;
bool operator!=(const UniversalBleService& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalBleService FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
static UniversalBleService FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
std::string uuid_;
std::optional<flutter::EncodableList> characteristics_;
std::optional<::flutter::EncodableList> characteristics_;
};
@@ -240,27 +248,31 @@ class UniversalBleCharacteristic {
// Constructs an object setting all fields.
explicit UniversalBleCharacteristic(
const std::string& uuid,
const flutter::EncodableList& properties,
const flutter::EncodableList& descriptors);
const ::flutter::EncodableList& properties,
const ::flutter::EncodableList& descriptors);
const std::string& uuid() const;
void set_uuid(std::string_view value_arg);
const flutter::EncodableList& properties() const;
void set_properties(const flutter::EncodableList& value_arg);
const ::flutter::EncodableList& properties() const;
void set_properties(const ::flutter::EncodableList& value_arg);
const flutter::EncodableList& descriptors() const;
void set_descriptors(const flutter::EncodableList& value_arg);
const ::flutter::EncodableList& descriptors() const;
void set_descriptors(const ::flutter::EncodableList& value_arg);
bool operator==(const UniversalBleCharacteristic& other) const;
bool operator!=(const UniversalBleCharacteristic& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalBleCharacteristic FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
static UniversalBleCharacteristic FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
std::string uuid_;
flutter::EncodableList properties_;
flutter::EncodableList descriptors_;
::flutter::EncodableList properties_;
::flutter::EncodableList descriptors_;
};
@@ -273,9 +285,13 @@ class UniversalBleDescriptor {
const std::string& uuid() const;
void set_uuid(std::string_view value_arg);
bool operator==(const UniversalBleDescriptor& other) const;
bool operator!=(const UniversalBleDescriptor& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalBleDescriptor FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
static UniversalBleDescriptor FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
@@ -283,6 +299,195 @@ class UniversalBleDescriptor {
};
// Generated class from Pigeon that represents data sent in messages.
class UniversalBlePeripheralConfig {
public:
// Constructs an object setting all fields.
explicit UniversalBlePeripheralConfig(
const std::string& advertised_name,
const ::flutter::EncodableList& services);
const std::string& advertised_name() const;
void set_advertised_name(std::string_view value_arg);
const ::flutter::EncodableList& services() const;
void set_services(const ::flutter::EncodableList& value_arg);
bool operator==(const UniversalBlePeripheralConfig& other) const;
bool operator!=(const UniversalBlePeripheralConfig& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalBlePeripheralConfig FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
std::string advertised_name_;
::flutter::EncodableList services_;
};
// Generated class from Pigeon that represents data sent in messages.
class UniversalBlePeripheralService {
public:
// Constructs an object setting all fields.
explicit UniversalBlePeripheralService(
const std::string& uuid,
const ::flutter::EncodableList& characteristics);
const std::string& uuid() const;
void set_uuid(std::string_view value_arg);
const ::flutter::EncodableList& characteristics() const;
void set_characteristics(const ::flutter::EncodableList& value_arg);
bool operator==(const UniversalBlePeripheralService& other) const;
bool operator!=(const UniversalBlePeripheralService& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalBlePeripheralService FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
std::string uuid_;
::flutter::EncodableList characteristics_;
};
// Generated class from Pigeon that represents data sent in messages.
class UniversalBlePeripheralCharacteristic {
public:
// Constructs an object setting all non-nullable fields.
explicit UniversalBlePeripheralCharacteristic(
const std::string& uuid,
const ::flutter::EncodableList& properties,
const ::flutter::EncodableList& permissions,
const ::flutter::EncodableList& descriptors);
// Constructs an object setting all fields.
explicit UniversalBlePeripheralCharacteristic(
const std::string& uuid,
const ::flutter::EncodableList& properties,
const ::flutter::EncodableList& permissions,
const ::flutter::EncodableList& descriptors,
const std::vector<uint8_t>* initial_value);
const std::string& uuid() const;
void set_uuid(std::string_view value_arg);
const ::flutter::EncodableList& properties() const;
void set_properties(const ::flutter::EncodableList& value_arg);
const ::flutter::EncodableList& permissions() const;
void set_permissions(const ::flutter::EncodableList& value_arg);
const ::flutter::EncodableList& descriptors() const;
void set_descriptors(const ::flutter::EncodableList& value_arg);
const std::vector<uint8_t>* initial_value() const;
void set_initial_value(const std::vector<uint8_t>* value_arg);
void set_initial_value(const std::vector<uint8_t>& value_arg);
bool operator==(const UniversalBlePeripheralCharacteristic& other) const;
bool operator!=(const UniversalBlePeripheralCharacteristic& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalBlePeripheralCharacteristic FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
std::string uuid_;
::flutter::EncodableList properties_;
::flutter::EncodableList permissions_;
::flutter::EncodableList descriptors_;
std::optional<std::vector<uint8_t>> initial_value_;
};
// Generated class from Pigeon that represents data sent in messages.
class UniversalBlePeripheralDescriptor {
public:
// Constructs an object setting all non-nullable fields.
explicit UniversalBlePeripheralDescriptor(
const std::string& uuid,
const ::flutter::EncodableList& permissions);
// Constructs an object setting all fields.
explicit UniversalBlePeripheralDescriptor(
const std::string& uuid,
const ::flutter::EncodableList& permissions,
const std::vector<uint8_t>* initial_value);
const std::string& uuid() const;
void set_uuid(std::string_view value_arg);
const ::flutter::EncodableList& permissions() const;
void set_permissions(const ::flutter::EncodableList& value_arg);
const std::vector<uint8_t>* initial_value() const;
void set_initial_value(const std::vector<uint8_t>* value_arg);
void set_initial_value(const std::vector<uint8_t>& value_arg);
bool operator==(const UniversalBlePeripheralDescriptor& other) const;
bool operator!=(const UniversalBlePeripheralDescriptor& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalBlePeripheralDescriptor FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
std::string uuid_;
::flutter::EncodableList permissions_;
std::optional<std::vector<uint8_t>> initial_value_;
};
// Generated class from Pigeon that represents data sent in messages.
class UniversalBlePeripheralWriteEvent {
public:
// Constructs an object setting all fields.
explicit UniversalBlePeripheralWriteEvent(
const std::string& device_id,
const std::string& service,
const std::string& characteristic,
const std::vector<uint8_t>& value);
const std::string& device_id() const;
void set_device_id(std::string_view value_arg);
const std::string& service() const;
void set_service(std::string_view value_arg);
const std::string& characteristic() const;
void set_characteristic(std::string_view value_arg);
const std::vector<uint8_t>& value() const;
void set_value(const std::vector<uint8_t>& value_arg);
bool operator==(const UniversalBlePeripheralWriteEvent& other) const;
bool operator!=(const UniversalBlePeripheralWriteEvent& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalBlePeripheralWriteEvent FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
std::string device_id_;
std::string service_;
std::string characteristic_;
std::vector<uint8_t> value_;
};
// Android options to scan devices
// [requestLocationPermission] is used to request location permission on Android 12+ (API 31+).
// [scanMode] is used to set the scan mode for for Bluetooth LE scan.
@@ -314,9 +519,13 @@ class AndroidOptions {
void set_report_delay_millis(const int64_t* value_arg);
void set_report_delay_millis(int64_t value_arg);
bool operator==(const AndroidOptions& other) const;
bool operator!=(const AndroidOptions& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static AndroidOptions FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
static AndroidOptions FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalScanConfig;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
@@ -345,9 +554,13 @@ class UniversalScanConfig {
void set_android(const AndroidOptions* value_arg);
void set_android(const AndroidOptions& value_arg);
bool operator==(const UniversalScanConfig& other) const;
bool operator!=(const UniversalScanConfig& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalScanConfig FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
static UniversalScanConfig FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
@@ -362,28 +575,32 @@ class UniversalScanFilter {
public:
// Constructs an object setting all fields.
explicit UniversalScanFilter(
const flutter::EncodableList& with_services,
const flutter::EncodableList& with_name_prefix,
const flutter::EncodableList& with_manufacturer_data);
const ::flutter::EncodableList& with_services,
const ::flutter::EncodableList& with_name_prefix,
const ::flutter::EncodableList& with_manufacturer_data);
const flutter::EncodableList& with_services() const;
void set_with_services(const flutter::EncodableList& value_arg);
const ::flutter::EncodableList& with_services() const;
void set_with_services(const ::flutter::EncodableList& value_arg);
const flutter::EncodableList& with_name_prefix() const;
void set_with_name_prefix(const flutter::EncodableList& value_arg);
const ::flutter::EncodableList& with_name_prefix() const;
void set_with_name_prefix(const ::flutter::EncodableList& value_arg);
const flutter::EncodableList& with_manufacturer_data() const;
void set_with_manufacturer_data(const flutter::EncodableList& value_arg);
const ::flutter::EncodableList& with_manufacturer_data() const;
void set_with_manufacturer_data(const ::flutter::EncodableList& value_arg);
bool operator==(const UniversalScanFilter& other) const;
bool operator!=(const UniversalScanFilter& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalScanFilter FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
static UniversalScanFilter FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
flutter::EncodableList with_services_;
flutter::EncodableList with_name_prefix_;
flutter::EncodableList with_manufacturer_data_;
::flutter::EncodableList with_services_;
::flutter::EncodableList with_name_prefix_;
::flutter::EncodableList with_manufacturer_data_;
};
@@ -410,9 +627,13 @@ class UniversalManufacturerDataFilter {
void set_mask(const std::vector<uint8_t>* value_arg);
void set_mask(const std::vector<uint8_t>& value_arg);
bool operator==(const UniversalManufacturerDataFilter& other) const;
bool operator!=(const UniversalManufacturerDataFilter& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalManufacturerDataFilter FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
static UniversalManufacturerDataFilter FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
@@ -436,9 +657,13 @@ class UniversalManufacturerData {
const std::vector<uint8_t>& data() const;
void set_data(const std::vector<uint8_t>& value_arg);
bool operator==(const UniversalManufacturerData& other) const;
bool operator!=(const UniversalManufacturerData& other) const;
/// Returns a hash code value for the object. This method is supported for the benefit of hash tables.
size_t Hash() const;
private:
static UniversalManufacturerData FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
static UniversalManufacturerData FromEncodableList(const ::flutter::EncodableList& list);
::flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonInternalCodecSerializer;
@@ -447,7 +672,7 @@ class UniversalManufacturerData {
};
class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer {
class PigeonInternalCodecSerializer : public ::flutter::StandardCodecSerializer {
public:
PigeonInternalCodecSerializer();
inline static PigeonInternalCodecSerializer& GetInstance() {
@@ -456,12 +681,12 @@ class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer {
}
void WriteValue(
const flutter::EncodableValue& value,
flutter::ByteStreamWriter* stream) const override;
const ::flutter::EncodableValue& value,
::flutter::ByteStreamWriter* stream) const override;
protected:
flutter::EncodableValue ReadValueOfType(
::flutter::EncodableValue ReadValueOfType(
uint8_t type,
flutter::ByteStreamReader* stream) const override;
::flutter::ByteStreamReader* stream) const override;
};
// Flutter -> Native
@@ -473,9 +698,12 @@ class UniversalBlePlatformChannel {
UniversalBlePlatformChannel& operator=(const UniversalBlePlatformChannel&) = delete;
virtual ~UniversalBlePlatformChannel() {}
virtual void GetBluetoothAvailabilityState(std::function<void(ErrorOr<int64_t> reply)> result) = 0;
virtual ErrorOr<bool> HasPermissions(bool with_android_fine_location) = 0;
virtual ErrorOr<bool> HasPermissions(
bool with_android_fine_location,
bool with_android_bluetooth_advertise) = 0;
virtual void RequestPermissions(
bool with_android_fine_location,
bool with_android_bluetooth_advertise,
std::function<void(std::optional<FlutterError> reply)> result) = 0;
virtual void EnableBluetooth(std::function<void(ErrorOr<bool> reply)> result) = 0;
virtual void DisableBluetooth(std::function<void(ErrorOr<bool> reply)> result) = 0;
@@ -497,7 +725,7 @@ class UniversalBlePlatformChannel {
virtual void DiscoverServices(
const std::string& device_id,
bool with_descriptors,
std::function<void(ErrorOr<flutter::EncodableList> reply)> result) = 0;
std::function<void(ErrorOr<::flutter::EncodableList> reply)> result) = 0;
virtual void ReadValue(
const std::string& device_id,
const std::string& service,
@@ -522,8 +750,8 @@ class UniversalBlePlatformChannel {
std::function<void(ErrorOr<bool> reply)> result) = 0;
virtual std::optional<FlutterError> UnPair(const std::string& device_id) = 0;
virtual void GetSystemDevices(
const flutter::EncodableList& with_services,
std::function<void(ErrorOr<flutter::EncodableList> reply)> result) = 0;
const ::flutter::EncodableList& with_services,
std::function<void(ErrorOr<::flutter::EncodableList> reply)> result) = 0;
virtual ErrorOr<int64_t> GetConnectionState(const std::string& device_id) = 0;
virtual void ReadRssi(
const std::string& device_id,
@@ -532,20 +760,36 @@ class UniversalBlePlatformChannel {
const std::string& device_id,
int64_t priority,
std::function<void(std::optional<FlutterError> reply)> result) = 0;
virtual ErrorOr<bool> IsPeripheralSupported() = 0;
virtual void StartPeripheral(
const UniversalBlePeripheralConfig& config,
std::function<void(std::optional<FlutterError> reply)> result) = 0;
virtual void StopPeripheral(std::function<void(std::optional<FlutterError> reply)> result) = 0;
virtual void UpdatePeripheralCharacteristicValue(
const std::string& service,
const std::string& characteristic,
const std::vector<uint8_t>& value,
std::function<void(std::optional<FlutterError> reply)> result) = 0;
virtual void NotifyPeripheralCharacteristic(
const std::string& service,
const std::string& characteristic,
const std::vector<uint8_t>& value,
bool indicate,
std::function<void(std::optional<FlutterError> reply)> result) = 0;
virtual std::optional<FlutterError> SetLogLevel(const UniversalBleLogLevel& log_level) = 0;
// The codec used by UniversalBlePlatformChannel.
static const flutter::StandardMessageCodec& GetCodec();
static const ::flutter::StandardMessageCodec& GetCodec();
// Sets up an instance of `UniversalBlePlatformChannel` to handle messages through the `binary_messenger`.
static void SetUp(
flutter::BinaryMessenger* binary_messenger,
::flutter::BinaryMessenger* binary_messenger,
UniversalBlePlatformChannel* api);
static void SetUp(
flutter::BinaryMessenger* binary_messenger,
::flutter::BinaryMessenger* binary_messenger,
UniversalBlePlatformChannel* api,
const std::string& message_channel_suffix);
static flutter::EncodableValue WrapError(std::string_view error_message);
static flutter::EncodableValue WrapError(const FlutterError& error);
static ::flutter::EncodableValue WrapError(std::string_view error_message);
static ::flutter::EncodableValue WrapError(const FlutterError& error);
protected:
UniversalBlePlatformChannel() = default;
};
@@ -554,11 +798,11 @@ class UniversalBlePlatformChannel {
// Generated class from Pigeon that represents Flutter messages that can be called from C++.
class UniversalBleCallbackChannel {
public:
UniversalBleCallbackChannel(flutter::BinaryMessenger* binary_messenger);
UniversalBleCallbackChannel(::flutter::BinaryMessenger* binary_messenger);
UniversalBleCallbackChannel(
flutter::BinaryMessenger* binary_messenger,
::flutter::BinaryMessenger* binary_messenger,
const std::string& message_channel_suffix);
static const flutter::StandardMessageCodec& GetCodec();
static const ::flutter::StandardMessageCodec& GetCodec();
void OnAvailabilityChanged(
int64_t state,
std::function<void(void)>&& on_success,
@@ -586,8 +830,24 @@ class UniversalBleCallbackChannel {
const std::string* error,
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error);
void OnPeripheralConnectionChanged(
const std::string& device_id,
bool connected,
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error);
void OnPeripheralWrite(
const UniversalBlePeripheralWriteEvent& event,
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error);
void OnPeripheralSubscriptionChanged(
const std::string& device_id,
const std::string& service,
const std::string& characteristic,
bool subscribed,
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error);
private:
flutter::BinaryMessenger* binary_messenger_;
::flutter::BinaryMessenger* binary_messenger_;
std::string message_channel_suffix_;
};
+36 -2
View File
@@ -120,13 +120,14 @@ void UniversalBlePlugin::DisableBluetooth(
}
ErrorOr<bool>
UniversalBlePlugin::HasPermissions(bool with_android_fine_location) {
UniversalBlePlugin::HasPermissions(bool with_android_fine_location,
bool with_android_bluetooth_advertise) {
// Windows does not require runtime permissions for Bluetooth
return true;
}
void UniversalBlePlugin::RequestPermissions(
bool with_android_fine_location,
bool with_android_fine_location, bool with_android_bluetooth_advertise,
std::function<void(std::optional<FlutterError> reply)> result) {
// Windows does not require runtime permissions for Bluetooth
result(std::nullopt);
@@ -444,6 +445,39 @@ void UniversalBlePlugin::RequestConnectionPriority(
"requestConnectionPriority is not supported on Windows platform"));
}
ErrorOr<bool> UniversalBlePlugin::IsPeripheralSupported() { return false; }
void UniversalBlePlugin::StartPeripheral(
const UniversalBlePeripheralConfig &config,
std::function<void(std::optional<FlutterError> reply)> result) {
result(create_flutter_error(
UniversalBleErrorCode::kNotSupported,
"BLE peripheral mode is not implemented on Windows platform yet"));
}
void UniversalBlePlugin::StopPeripheral(
std::function<void(std::optional<FlutterError> reply)> result) {
result(std::nullopt);
}
void UniversalBlePlugin::UpdatePeripheralCharacteristicValue(
const std::string &service, const std::string &characteristic,
const std::vector<uint8_t> &value,
std::function<void(std::optional<FlutterError> reply)> result) {
result(create_flutter_error(
UniversalBleErrorCode::kNotSupported,
"BLE peripheral mode is not implemented on Windows platform yet"));
}
void UniversalBlePlugin::NotifyPeripheralCharacteristic(
const std::string &service, const std::string &characteristic,
const std::vector<uint8_t> &value, bool indicate,
std::function<void(std::optional<FlutterError> reply)> result) {
result(create_flutter_error(
UniversalBleErrorCode::kNotSupported,
"BLE peripheral mode is not implemented on Windows platform yet"));
}
void UniversalBlePlugin::ReadRssi(
const std::string &device_id,
std::function<void(ErrorOr<int64_t> reply)> result) {
+17 -2
View File
@@ -184,9 +184,10 @@ private:
ErrorOr<bool> IsScanning() override;
std::optional<FlutterError> Connect(const std::string &device_id, const bool *auto_connect) override;
std::optional<FlutterError> Disconnect(const std::string &device_id) override;
ErrorOr<bool> HasPermissions(bool with_android_fine_location) override;
ErrorOr<bool> HasPermissions(bool with_android_fine_location,
bool with_android_bluetooth_advertise) override;
void RequestPermissions(
bool with_android_fine_location,
bool with_android_fine_location, bool with_android_bluetooth_advertise,
std::function<void(std::optional<FlutterError> reply)> result) override;
void
DiscoverServices(const std::string &device_id, bool with_descriptors,
@@ -210,6 +211,20 @@ private:
void RequestConnectionPriority(
const std::string &device_id, int64_t priority,
std::function<void(std::optional<FlutterError> reply)> result) override;
ErrorOr<bool> IsPeripheralSupported() override;
void StartPeripheral(
const UniversalBlePeripheralConfig &config,
std::function<void(std::optional<FlutterError> reply)> result) override;
void StopPeripheral(
std::function<void(std::optional<FlutterError> reply)> result) override;
void UpdatePeripheralCharacteristicValue(
const std::string &service, const std::string &characteristic,
const std::vector<uint8_t> &value,
std::function<void(std::optional<FlutterError> reply)> result) override;
void NotifyPeripheralCharacteristic(
const std::string &service, const std::string &characteristic,
const std::vector<uint8_t> &value, bool indicate,
std::function<void(std::optional<FlutterError> reply)> result) override;
void ReadRssi(const std::string &device_id,
std::function<void(ErrorOr<int64_t> reply)> result) override;
void IsPaired(const std::string &device_id,