Add Android scan mode setting (#212)
* Add Android scan mode setting * Format pigeon generated code * format whole project * Fix windows compilation because of generated code order * Fix unwanted imports * Add doc comment for AndroidOptions * Update code doc
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
## 1.2.0
|
||||
* Add `autoConnect` parameter to `connect()` method for automatic reconnection support on Android and iOS/macOS
|
||||
* Add `serviceData` in `BleDevice`
|
||||
* Add `AndroidScanMode` and `reportDelayMillis` to `AndroidOptions` for scanning
|
||||
|
||||
## 1.1.0
|
||||
* Add readRssi method
|
||||
|
||||
@@ -96,6 +96,20 @@ enum class UniversalBleLogLevel(val raw: Int) {
|
||||
}
|
||||
}
|
||||
|
||||
/** Scan config */
|
||||
enum class AndroidScanMode(val raw: Int) {
|
||||
BALANCED(0),
|
||||
LOW_LATENCY(1),
|
||||
LOW_POWER(2),
|
||||
OPPORTUNISTIC(3);
|
||||
|
||||
companion object {
|
||||
fun ofRaw(raw: Int): AndroidScanMode? {
|
||||
return values().firstOrNull { it.raw == raw }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Unified error codes for all platforms */
|
||||
enum class UniversalBleErrorCode(val raw: Int) {
|
||||
UNKNOWN_ERROR(0),
|
||||
@@ -309,6 +323,77 @@ data class UniversalBleDescriptor (
|
||||
override fun hashCode(): Int = toList().hashCode()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Set [reportDelayMillis] timestamp for Bluetooth LE scan. If set to 0, you will be notified of scan results immediately.
|
||||
* If > 0, scan results are queued up and delivered after the requested delay or 5000 milliseconds (whichever is higher).
|
||||
* Note scan results may be delivered sooner if the internal buffers fill up.
|
||||
*
|
||||
* Generated class from Pigeon that represents data sent in messages.
|
||||
*/
|
||||
data class AndroidOptions (
|
||||
val requestLocationPermission: Boolean? = null,
|
||||
val scanMode: AndroidScanMode? = null,
|
||||
val reportDelayMillis: Long? = null
|
||||
)
|
||||
{
|
||||
companion object {
|
||||
fun fromList(pigeonVar_list: List<Any?>): AndroidOptions {
|
||||
val requestLocationPermission = pigeonVar_list[0] as Boolean?
|
||||
val scanMode = pigeonVar_list[1] as AndroidScanMode?
|
||||
val reportDelayMillis = pigeonVar_list[2] as Long?
|
||||
return AndroidOptions(requestLocationPermission, scanMode, reportDelayMillis)
|
||||
}
|
||||
}
|
||||
fun toList(): List<Any?> {
|
||||
return listOf(
|
||||
requestLocationPermission,
|
||||
scanMode,
|
||||
reportDelayMillis,
|
||||
)
|
||||
}
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is AndroidOptions) {
|
||||
return false
|
||||
}
|
||||
if (this === other) {
|
||||
return true
|
||||
}
|
||||
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
|
||||
|
||||
override fun hashCode(): Int = toList().hashCode()
|
||||
}
|
||||
|
||||
/** Generated class from Pigeon that represents data sent in messages. */
|
||||
data class UniversalScanConfig (
|
||||
val android: AndroidOptions? = null
|
||||
)
|
||||
{
|
||||
companion object {
|
||||
fun fromList(pigeonVar_list: List<Any?>): UniversalScanConfig {
|
||||
val android = pigeonVar_list[0] as AndroidOptions?
|
||||
return UniversalScanConfig(android)
|
||||
}
|
||||
}
|
||||
fun toList(): List<Any?> {
|
||||
return listOf(
|
||||
android,
|
||||
)
|
||||
}
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is UniversalScanConfig) {
|
||||
return false
|
||||
}
|
||||
if (this === other) {
|
||||
return true
|
||||
}
|
||||
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
|
||||
|
||||
override fun hashCode(): Int = toList().hashCode()
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan Filters
|
||||
*
|
||||
@@ -421,40 +506,55 @@ private open class UniversalBlePigeonCodec : StandardMessageCodec() {
|
||||
}
|
||||
130.toByte() -> {
|
||||
return (readValue(buffer) as Long?)?.let {
|
||||
UniversalBleErrorCode.ofRaw(it.toInt())
|
||||
AndroidScanMode.ofRaw(it.toInt())
|
||||
}
|
||||
}
|
||||
131.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalBleScanResult.fromList(it)
|
||||
return (readValue(buffer) as Long?)?.let {
|
||||
UniversalBleErrorCode.ofRaw(it.toInt())
|
||||
}
|
||||
}
|
||||
132.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalBleService.fromList(it)
|
||||
UniversalBleScanResult.fromList(it)
|
||||
}
|
||||
}
|
||||
133.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalBleCharacteristic.fromList(it)
|
||||
UniversalBleService.fromList(it)
|
||||
}
|
||||
}
|
||||
134.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalBleDescriptor.fromList(it)
|
||||
UniversalBleCharacteristic.fromList(it)
|
||||
}
|
||||
}
|
||||
135.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalScanFilter.fromList(it)
|
||||
UniversalBleDescriptor.fromList(it)
|
||||
}
|
||||
}
|
||||
136.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalManufacturerDataFilter.fromList(it)
|
||||
AndroidOptions.fromList(it)
|
||||
}
|
||||
}
|
||||
137.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalScanConfig.fromList(it)
|
||||
}
|
||||
}
|
||||
138.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalScanFilter.fromList(it)
|
||||
}
|
||||
}
|
||||
139.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalManufacturerDataFilter.fromList(it)
|
||||
}
|
||||
}
|
||||
140.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalManufacturerData.fromList(it)
|
||||
}
|
||||
@@ -468,38 +568,50 @@ private open class UniversalBlePigeonCodec : StandardMessageCodec() {
|
||||
stream.write(129)
|
||||
writeValue(stream, value.raw.toLong())
|
||||
}
|
||||
is UniversalBleErrorCode -> {
|
||||
is AndroidScanMode -> {
|
||||
stream.write(130)
|
||||
writeValue(stream, value.raw.toLong())
|
||||
}
|
||||
is UniversalBleScanResult -> {
|
||||
is UniversalBleErrorCode -> {
|
||||
stream.write(131)
|
||||
writeValue(stream, value.toList())
|
||||
writeValue(stream, value.raw.toLong())
|
||||
}
|
||||
is UniversalBleService -> {
|
||||
is UniversalBleScanResult -> {
|
||||
stream.write(132)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalBleCharacteristic -> {
|
||||
is UniversalBleService -> {
|
||||
stream.write(133)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalBleDescriptor -> {
|
||||
is UniversalBleCharacteristic -> {
|
||||
stream.write(134)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalScanFilter -> {
|
||||
is UniversalBleDescriptor -> {
|
||||
stream.write(135)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalManufacturerDataFilter -> {
|
||||
is AndroidOptions -> {
|
||||
stream.write(136)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalManufacturerData -> {
|
||||
is UniversalScanConfig -> {
|
||||
stream.write(137)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalScanFilter -> {
|
||||
stream.write(138)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalManufacturerDataFilter -> {
|
||||
stream.write(139)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalManufacturerData -> {
|
||||
stream.write(140)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
else -> super.writeValue(stream, value)
|
||||
}
|
||||
}
|
||||
@@ -517,7 +629,7 @@ interface UniversalBlePlatformChannel {
|
||||
fun requestPermissions(withAndroidFineLocation: Boolean, callback: (Result<Unit>) -> Unit)
|
||||
fun enableBluetooth(callback: (Result<Boolean>) -> Unit)
|
||||
fun disableBluetooth(callback: (Result<Boolean>) -> Unit)
|
||||
fun startScan(filter: UniversalScanFilter?)
|
||||
fun startScan(filter: UniversalScanFilter?, config: UniversalScanConfig?)
|
||||
fun stopScan()
|
||||
fun isScanning(): Boolean
|
||||
fun connect(deviceId: String, autoConnect: Boolean?)
|
||||
@@ -640,8 +752,9 @@ interface UniversalBlePlatformChannel {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val filterArg = args[0] as UniversalScanFilter?
|
||||
val configArg = args[1] as UniversalScanConfig?
|
||||
val wrapped: List<Any?> = try {
|
||||
api.startScan(filterArg)
|
||||
api.startScan(filterArg, configArg)
|
||||
listOf(null)
|
||||
} catch (exception: Throwable) {
|
||||
UniversalBlePigeonUtils.wrapError(exception)
|
||||
|
||||
@@ -12,6 +12,8 @@ import android.bluetooth.le.ScanCallback.SCAN_FAILED_INTERNAL_ERROR
|
||||
import android.bluetooth.le.ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES
|
||||
import android.bluetooth.le.ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY
|
||||
import android.bluetooth.le.ScanResult
|
||||
import android.bluetooth.le.ScanSettings
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.util.SparseArray
|
||||
import java.nio.ByteBuffer
|
||||
@@ -224,6 +226,20 @@ fun gattStatusToUniversalBleErrorCode(code: Int): UniversalBleErrorCode {
|
||||
}
|
||||
}
|
||||
|
||||
fun AndroidScanMode.parse(): Int? {
|
||||
return when (this) {
|
||||
AndroidScanMode.BALANCED -> ScanSettings.SCAN_MODE_BALANCED
|
||||
AndroidScanMode.LOW_LATENCY -> ScanSettings.SCAN_MODE_LOW_LATENCY
|
||||
AndroidScanMode.LOW_POWER -> ScanSettings.SCAN_MODE_LOW_POWER
|
||||
AndroidScanMode.OPPORTUNISTIC -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
ScanSettings.SCAN_MODE_OPPORTUNISTIC
|
||||
} else {
|
||||
UniversalBleLogger.logError("Scan mode OPPORTUNISTIC is not supported on this Android version.")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Int.parseBluetoothStatusCodeError(): UniversalBleErrorCode? {
|
||||
if (this == BluetoothStatusCodes.SUCCESS) return null
|
||||
return when (this) {
|
||||
|
||||
@@ -160,7 +160,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
bluetoothDisableRequestFuture = callback
|
||||
}
|
||||
|
||||
override fun startScan(filter: UniversalScanFilter?) {
|
||||
override fun startScan(filter: UniversalScanFilter?, config: UniversalScanConfig?) {
|
||||
if (!isBluetoothAvailable()) throw createFlutterError(
|
||||
UniversalBleErrorCode.BLUETOOTH_NOT_ENABLED,
|
||||
"Bluetooth not enabled"
|
||||
@@ -171,6 +171,14 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
builder.setPhy(ScanSettings.PHY_LE_ALL_SUPPORTED)
|
||||
builder.setLegacy(false)
|
||||
}
|
||||
config?.android?.let { androidConfig ->
|
||||
androidConfig.scanMode?.parse()?.let { scanMode ->
|
||||
builder.setScanMode(scanMode)
|
||||
}
|
||||
androidConfig.reportDelayMillis?.let { reportDelay ->
|
||||
builder.setReportDelay(reportDelay)
|
||||
}
|
||||
}
|
||||
val settings = builder.build()
|
||||
|
||||
val usesCustomFilters = filter?.usesCustomFilters() ?: false
|
||||
@@ -1151,7 +1159,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
|
||||
val deviceId = gatt.device.address
|
||||
val shouldAutoConnect = autoConnectDevices.contains(deviceId)
|
||||
|
||||
|
||||
// Always clean up internal state (futures, etc.)
|
||||
cleanUpConnection(deviceId)
|
||||
|
||||
@@ -1161,7 +1169,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
deviceId, false, status.parseHciErrorCode()
|
||||
) {}
|
||||
}
|
||||
|
||||
|
||||
if (!shouldAutoConnect) {
|
||||
// Only close GATT resources when autoConnect is disabled
|
||||
gatt.removeCache()
|
||||
|
||||
@@ -141,6 +141,14 @@ enum UniversalBleLogLevel: Int {
|
||||
case verbose = 5
|
||||
}
|
||||
|
||||
/// Scan config
|
||||
enum AndroidScanMode: Int {
|
||||
case balanced = 0
|
||||
case lowLatency = 1
|
||||
case lowPower = 2
|
||||
case opportunistic = 3
|
||||
}
|
||||
|
||||
/// Unified error codes for all platforms
|
||||
enum UniversalBleErrorCode: Int {
|
||||
case unknownError = 0
|
||||
@@ -346,6 +354,71 @@ struct UniversalBleDescriptor: Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// Set [reportDelayMillis] timestamp for Bluetooth LE scan. If set to 0, you will be notified of scan results immediately.
|
||||
/// If > 0, scan results are queued up and delivered after the requested delay or 5000 milliseconds (whichever is higher).
|
||||
/// Note scan results may be delivered sooner if the internal buffers fill up.
|
||||
///
|
||||
/// Generated class from Pigeon that represents data sent in messages.
|
||||
struct AndroidOptions: Hashable {
|
||||
var requestLocationPermission: Bool? = nil
|
||||
var scanMode: AndroidScanMode? = nil
|
||||
var reportDelayMillis: Int64? = nil
|
||||
|
||||
|
||||
// swift-format-ignore: AlwaysUseLowerCamelCase
|
||||
static func fromList(_ pigeonVar_list: [Any?]) -> AndroidOptions? {
|
||||
let requestLocationPermission: Bool? = nilOrValue(pigeonVar_list[0])
|
||||
let scanMode: AndroidScanMode? = nilOrValue(pigeonVar_list[1])
|
||||
let reportDelayMillis: Int64? = nilOrValue(pigeonVar_list[2])
|
||||
|
||||
return AndroidOptions(
|
||||
requestLocationPermission: requestLocationPermission,
|
||||
scanMode: scanMode,
|
||||
reportDelayMillis: reportDelayMillis
|
||||
)
|
||||
}
|
||||
func toList() -> [Any?] {
|
||||
return [
|
||||
requestLocationPermission,
|
||||
scanMode,
|
||||
reportDelayMillis,
|
||||
]
|
||||
}
|
||||
static func == (lhs: AndroidOptions, rhs: AndroidOptions) -> Bool {
|
||||
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
|
||||
func hash(into hasher: inout Hasher) {
|
||||
deepHashUniversalBle(value: toList(), hasher: &hasher)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generated class from Pigeon that represents data sent in messages.
|
||||
struct UniversalScanConfig: Hashable {
|
||||
var android: AndroidOptions? = nil
|
||||
|
||||
|
||||
// swift-format-ignore: AlwaysUseLowerCamelCase
|
||||
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalScanConfig? {
|
||||
let android: AndroidOptions? = nilOrValue(pigeonVar_list[0])
|
||||
|
||||
return UniversalScanConfig(
|
||||
android: android
|
||||
)
|
||||
}
|
||||
func toList() -> [Any?] {
|
||||
return [
|
||||
android
|
||||
]
|
||||
}
|
||||
static func == (lhs: UniversalScanConfig, rhs: UniversalScanConfig) -> Bool {
|
||||
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
|
||||
func hash(into hasher: inout Hasher) {
|
||||
deepHashUniversalBle(value: toList(), hasher: &hasher)
|
||||
}
|
||||
}
|
||||
|
||||
/// Scan Filters
|
||||
///
|
||||
/// Generated class from Pigeon that represents data sent in messages.
|
||||
@@ -455,22 +528,32 @@ private class UniversalBlePigeonCodecReader: FlutterStandardReader {
|
||||
case 130:
|
||||
let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
|
||||
if let enumResultAsInt = enumResultAsInt {
|
||||
return UniversalBleErrorCode(rawValue: enumResultAsInt)
|
||||
return AndroidScanMode(rawValue: enumResultAsInt)
|
||||
}
|
||||
return nil
|
||||
case 131:
|
||||
return UniversalBleScanResult.fromList(self.readValue() as! [Any?])
|
||||
let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
|
||||
if let enumResultAsInt = enumResultAsInt {
|
||||
return UniversalBleErrorCode(rawValue: enumResultAsInt)
|
||||
}
|
||||
return nil
|
||||
case 132:
|
||||
return UniversalBleService.fromList(self.readValue() as! [Any?])
|
||||
return UniversalBleScanResult.fromList(self.readValue() as! [Any?])
|
||||
case 133:
|
||||
return UniversalBleCharacteristic.fromList(self.readValue() as! [Any?])
|
||||
return UniversalBleService.fromList(self.readValue() as! [Any?])
|
||||
case 134:
|
||||
return UniversalBleDescriptor.fromList(self.readValue() as! [Any?])
|
||||
return UniversalBleCharacteristic.fromList(self.readValue() as! [Any?])
|
||||
case 135:
|
||||
return UniversalScanFilter.fromList(self.readValue() as! [Any?])
|
||||
return UniversalBleDescriptor.fromList(self.readValue() as! [Any?])
|
||||
case 136:
|
||||
return UniversalManufacturerDataFilter.fromList(self.readValue() as! [Any?])
|
||||
return AndroidOptions.fromList(self.readValue() as! [Any?])
|
||||
case 137:
|
||||
return UniversalScanConfig.fromList(self.readValue() as! [Any?])
|
||||
case 138:
|
||||
return UniversalScanFilter.fromList(self.readValue() as! [Any?])
|
||||
case 139:
|
||||
return UniversalManufacturerDataFilter.fromList(self.readValue() as! [Any?])
|
||||
case 140:
|
||||
return UniversalManufacturerData.fromList(self.readValue() as! [Any?])
|
||||
default:
|
||||
return super.readValue(ofType: type)
|
||||
@@ -483,30 +566,39 @@ private class UniversalBlePigeonCodecWriter: FlutterStandardWriter {
|
||||
if let value = value as? UniversalBleLogLevel {
|
||||
super.writeByte(129)
|
||||
super.writeValue(value.rawValue)
|
||||
} else if let value = value as? UniversalBleErrorCode {
|
||||
} else if let value = value as? AndroidScanMode {
|
||||
super.writeByte(130)
|
||||
super.writeValue(value.rawValue)
|
||||
} else if let value = value as? UniversalBleScanResult {
|
||||
} else if let value = value as? UniversalBleErrorCode {
|
||||
super.writeByte(131)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalBleService {
|
||||
super.writeValue(value.rawValue)
|
||||
} else if let value = value as? UniversalBleScanResult {
|
||||
super.writeByte(132)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalBleCharacteristic {
|
||||
} else if let value = value as? UniversalBleService {
|
||||
super.writeByte(133)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalBleDescriptor {
|
||||
} else if let value = value as? UniversalBleCharacteristic {
|
||||
super.writeByte(134)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalScanFilter {
|
||||
} else if let value = value as? UniversalBleDescriptor {
|
||||
super.writeByte(135)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalManufacturerDataFilter {
|
||||
} else if let value = value as? AndroidOptions {
|
||||
super.writeByte(136)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalManufacturerData {
|
||||
} else if let value = value as? UniversalScanConfig {
|
||||
super.writeByte(137)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalScanFilter {
|
||||
super.writeByte(138)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalManufacturerDataFilter {
|
||||
super.writeByte(139)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalManufacturerData {
|
||||
super.writeByte(140)
|
||||
super.writeValue(value.toList())
|
||||
} else {
|
||||
super.writeValue(value)
|
||||
}
|
||||
@@ -537,7 +629,7 @@ protocol UniversalBlePlatformChannel {
|
||||
func requestPermissions(withAndroidFineLocation: 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?) throws
|
||||
func startScan(filter: UniversalScanFilter?, config: UniversalScanConfig?) throws
|
||||
func stopScan() throws
|
||||
func isScanning() throws -> Bool
|
||||
func connect(deviceId: String, autoConnect: Bool?) throws
|
||||
@@ -644,8 +736,9 @@ class UniversalBlePlatformChannelSetup {
|
||||
startScanChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let filterArg: UniversalScanFilter? = nilOrValue(args[0])
|
||||
let configArg: UniversalScanConfig? = nilOrValue(args[1])
|
||||
do {
|
||||
try api.startScan(filter: filterArg)
|
||||
try api.startScan(filter: filterArg, config: configArg)
|
||||
reply(wrapResult(nil))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
|
||||
@@ -91,7 +91,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
completion(Result.failure(createFlutterError(code: .notSupported)))
|
||||
}
|
||||
|
||||
func startScan(filter: UniversalScanFilter?) throws {
|
||||
func startScan(filter: UniversalScanFilter?, config _: UniversalScanConfig?) throws {
|
||||
// If filter has any other filter other than official one
|
||||
let usesCustomFilters = filter?.usesCustomFilters ?? false
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@ class MockUniversalBle extends UniversalBlePlatform {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> connect(String deviceId, {bool autoConnect = false, Duration? connectionTimeout}) async {
|
||||
Future<void> connect(String deviceId,
|
||||
{bool autoConnect = false, Duration? connectionTimeout}) async {
|
||||
updateConnection(deviceId, true);
|
||||
_connectionStateMap[deviceId] = BleConnectionState.connected;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,11 @@ class _ScannerScreenState extends State<ScannerScreen> {
|
||||
_isScanning = true;
|
||||
});
|
||||
try {
|
||||
PlatformConfig? platformConfig;
|
||||
PlatformConfig platformConfig = PlatformConfig(
|
||||
android: AndroidOptions(
|
||||
scanMode: AndroidScanMode.lowLatency,
|
||||
),
|
||||
);
|
||||
if (kIsWeb && _webServicesController.text.isNotEmpty) {
|
||||
List<String> webServices = _webServicesController.text
|
||||
.split(',')
|
||||
@@ -96,10 +100,9 @@ class _ScannerScreenState extends State<ScannerScreen> {
|
||||
return s.trim();
|
||||
}
|
||||
}).toList();
|
||||
platformConfig = PlatformConfig(
|
||||
web: WebOptions(optionalServices: webServices),
|
||||
);
|
||||
platformConfig.web = WebOptions(optionalServices: webServices);
|
||||
}
|
||||
|
||||
await UniversalBle.startScan(
|
||||
scanFilter: scanFilter,
|
||||
platformConfig: platformConfig,
|
||||
|
||||
@@ -9,7 +9,7 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
class AppDrawer extends StatefulWidget {
|
||||
final QueueType? queueType;
|
||||
final Function(QueueType)? onQueueTypeChanged;
|
||||
|
||||
|
||||
const AppDrawer({
|
||||
super.key,
|
||||
this.queueType,
|
||||
@@ -87,7 +87,8 @@ class _AppDrawerState extends State<AppDrawer> {
|
||||
),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildQueueOption(
|
||||
|
||||
@@ -66,7 +66,8 @@ class _ScanFilterWidgetState extends State<ScanFilterWidget> {
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList();
|
||||
for (String manufacturer in manufacturerData) {
|
||||
final companyIdentifier = companyService.parseCompanyIdentifier(manufacturer);
|
||||
final companyIdentifier =
|
||||
companyService.parseCompanyIdentifier(manufacturer);
|
||||
|
||||
if (companyIdentifier == null) {
|
||||
throw Exception(
|
||||
|
||||
@@ -2,23 +2,23 @@ import 'package:flutter/material.dart';
|
||||
import 'package:universal_ble_example/data/company_identifier_service.dart';
|
||||
|
||||
/// A reusable widget that displays company information for a given company ID.
|
||||
///
|
||||
///
|
||||
/// This widget fetches the company name from the CompanyIdentifierService
|
||||
/// and displays it in a consistent format. If no company name is found,
|
||||
/// the widget returns an empty SizedBox.
|
||||
class CompanyInfoWidget extends StatelessWidget {
|
||||
/// The company ID to look up
|
||||
final int companyId;
|
||||
|
||||
|
||||
/// Optional text style for the "Company:" label
|
||||
final TextStyle? labelStyle;
|
||||
|
||||
|
||||
/// Optional text style for the company name
|
||||
final TextStyle? nameStyle;
|
||||
|
||||
|
||||
/// Optional padding around the widget
|
||||
final EdgeInsets? padding;
|
||||
|
||||
|
||||
/// Optional color scheme. If not provided, will be obtained from Theme
|
||||
final ColorScheme? colorScheme;
|
||||
|
||||
@@ -33,8 +33,9 @@ class CompanyInfoWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final companyName = CompanyIdentifierService.instance.getCompanyName(companyId);
|
||||
|
||||
final companyName =
|
||||
CompanyIdentifierService.instance.getCompanyName(companyId);
|
||||
|
||||
if (companyName == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart';
|
||||
import 'package:universal_ble/src/utils/cache_handler.dart';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
@@ -19,7 +18,8 @@ extension BleDeviceExtension on BleDevice {
|
||||
/// Connects to the device.
|
||||
/// [autoConnect] enables automatic reconnection when the device becomes available.
|
||||
Future<void> connect({bool autoConnect = false, Duration? timeout}) =>
|
||||
UniversalBle.connect(deviceId, autoConnect: autoConnect, timeout: timeout);
|
||||
UniversalBle.connect(deviceId,
|
||||
autoConnect: autoConnect, timeout: timeout);
|
||||
|
||||
/// Disconnects from the device.
|
||||
Future<void> disconnect() => UniversalBle.disconnect(deviceId);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
/// Extension methods for [BleService] objects.
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart';
|
||||
|
||||
/// Platform specific config to scan devices
|
||||
///
|
||||
/// If more than 1 platform supports a certain parameter then it should be in the high level APIs instead of platform specific options.
|
||||
class PlatformConfig {
|
||||
WebOptions? web;
|
||||
AndroidOptions? android;
|
||||
@@ -22,10 +26,3 @@ class WebOptions {
|
||||
this.optionalManufacturerData = const [],
|
||||
});
|
||||
}
|
||||
|
||||
/// Android options to scan devices
|
||||
class AndroidOptions {
|
||||
bool requestLocationPermission;
|
||||
|
||||
AndroidOptions({this.requestLocationPermission = true});
|
||||
}
|
||||
|
||||
@@ -50,6 +50,14 @@ enum UniversalBleLogLevel {
|
||||
verbose,
|
||||
}
|
||||
|
||||
/// Scan config
|
||||
enum AndroidScanMode {
|
||||
balanced,
|
||||
lowLatency,
|
||||
lowPower,
|
||||
opportunistic,
|
||||
}
|
||||
|
||||
/// Unified error codes for all platforms
|
||||
enum UniversalBleErrorCode {
|
||||
unknownError,
|
||||
@@ -334,6 +342,104 @@ class UniversalBleDescriptor {
|
||||
int get hashCode => Object.hashAll(_toList());
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// Set [reportDelayMillis] timestamp for Bluetooth LE scan. If set to 0, you will be notified of scan results immediately.
|
||||
/// If > 0, scan results are queued up and delivered after the requested delay or 5000 milliseconds (whichever is higher).
|
||||
/// Note scan results may be delivered sooner if the internal buffers fill up.
|
||||
class AndroidOptions {
|
||||
AndroidOptions({
|
||||
this.requestLocationPermission,
|
||||
this.scanMode,
|
||||
this.reportDelayMillis,
|
||||
});
|
||||
|
||||
bool? requestLocationPermission;
|
||||
|
||||
AndroidScanMode? scanMode;
|
||||
|
||||
int? reportDelayMillis;
|
||||
|
||||
List<Object?> _toList() {
|
||||
return <Object?>[
|
||||
requestLocationPermission,
|
||||
scanMode,
|
||||
reportDelayMillis,
|
||||
];
|
||||
}
|
||||
|
||||
Object encode() {
|
||||
return _toList();
|
||||
}
|
||||
|
||||
static AndroidOptions decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return AndroidOptions(
|
||||
requestLocationPermission: result[0] as bool?,
|
||||
scanMode: result[1] as AndroidScanMode?,
|
||||
reportDelayMillis: result[2] as int?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||
bool operator ==(Object other) {
|
||||
if (other is! AndroidOptions || other.runtimeType != runtimeType) {
|
||||
return false;
|
||||
}
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
return _deepEquals(encode(), other.encode());
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||
int get hashCode => Object.hashAll(_toList());
|
||||
}
|
||||
|
||||
class UniversalScanConfig {
|
||||
UniversalScanConfig({
|
||||
this.android,
|
||||
});
|
||||
|
||||
AndroidOptions? android;
|
||||
|
||||
List<Object?> _toList() {
|
||||
return <Object?>[
|
||||
android,
|
||||
];
|
||||
}
|
||||
|
||||
Object encode() {
|
||||
return _toList();
|
||||
}
|
||||
|
||||
static UniversalScanConfig decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return UniversalScanConfig(
|
||||
android: result[0] as AndroidOptions?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||
bool operator ==(Object other) {
|
||||
if (other is! UniversalScanConfig || other.runtimeType != runtimeType) {
|
||||
return false;
|
||||
}
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
return _deepEquals(encode(), other.encode());
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||
int get hashCode => Object.hashAll(_toList());
|
||||
}
|
||||
|
||||
/// Scan Filters
|
||||
class UniversalScanFilter {
|
||||
UniversalScanFilter({
|
||||
@@ -496,30 +602,39 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
} else if (value is UniversalBleLogLevel) {
|
||||
buffer.putUint8(129);
|
||||
writeValue(buffer, value.index);
|
||||
} else if (value is UniversalBleErrorCode) {
|
||||
} else if (value is AndroidScanMode) {
|
||||
buffer.putUint8(130);
|
||||
writeValue(buffer, value.index);
|
||||
} else if (value is UniversalBleScanResult) {
|
||||
} else if (value is UniversalBleErrorCode) {
|
||||
buffer.putUint8(131);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalBleService) {
|
||||
writeValue(buffer, value.index);
|
||||
} else if (value is UniversalBleScanResult) {
|
||||
buffer.putUint8(132);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalBleCharacteristic) {
|
||||
} else if (value is UniversalBleService) {
|
||||
buffer.putUint8(133);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalBleDescriptor) {
|
||||
} else if (value is UniversalBleCharacteristic) {
|
||||
buffer.putUint8(134);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalScanFilter) {
|
||||
} else if (value is UniversalBleDescriptor) {
|
||||
buffer.putUint8(135);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerDataFilter) {
|
||||
} else if (value is AndroidOptions) {
|
||||
buffer.putUint8(136);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerData) {
|
||||
} else if (value is UniversalScanConfig) {
|
||||
buffer.putUint8(137);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalScanFilter) {
|
||||
buffer.putUint8(138);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerDataFilter) {
|
||||
buffer.putUint8(139);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerData) {
|
||||
buffer.putUint8(140);
|
||||
writeValue(buffer, value.encode());
|
||||
} else {
|
||||
super.writeValue(buffer, value);
|
||||
}
|
||||
@@ -533,20 +648,27 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
return value == null ? null : UniversalBleLogLevel.values[value];
|
||||
case 130:
|
||||
final value = readValue(buffer) as int?;
|
||||
return value == null ? null : UniversalBleErrorCode.values[value];
|
||||
return value == null ? null : AndroidScanMode.values[value];
|
||||
case 131:
|
||||
return UniversalBleScanResult.decode(readValue(buffer)!);
|
||||
final value = readValue(buffer) as int?;
|
||||
return value == null ? null : UniversalBleErrorCode.values[value];
|
||||
case 132:
|
||||
return UniversalBleService.decode(readValue(buffer)!);
|
||||
return UniversalBleScanResult.decode(readValue(buffer)!);
|
||||
case 133:
|
||||
return UniversalBleCharacteristic.decode(readValue(buffer)!);
|
||||
return UniversalBleService.decode(readValue(buffer)!);
|
||||
case 134:
|
||||
return UniversalBleDescriptor.decode(readValue(buffer)!);
|
||||
return UniversalBleCharacteristic.decode(readValue(buffer)!);
|
||||
case 135:
|
||||
return UniversalScanFilter.decode(readValue(buffer)!);
|
||||
return UniversalBleDescriptor.decode(readValue(buffer)!);
|
||||
case 136:
|
||||
return UniversalManufacturerDataFilter.decode(readValue(buffer)!);
|
||||
return AndroidOptions.decode(readValue(buffer)!);
|
||||
case 137:
|
||||
return UniversalScanConfig.decode(readValue(buffer)!);
|
||||
case 138:
|
||||
return UniversalScanFilter.decode(readValue(buffer)!);
|
||||
case 139:
|
||||
return UniversalManufacturerDataFilter.decode(readValue(buffer)!);
|
||||
case 140:
|
||||
return UniversalManufacturerData.decode(readValue(buffer)!);
|
||||
default:
|
||||
return super.readValueOfType(type, buffer);
|
||||
@@ -707,7 +829,8 @@ class UniversalBlePlatformChannel {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> startScan(UniversalScanFilter? filter) async {
|
||||
Future<void> startScan(
|
||||
UniversalScanFilter? filter, UniversalScanConfig? config) async {
|
||||
final pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan$pigeonVar_messageChannelSuffix';
|
||||
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
@@ -716,7 +839,7 @@ class UniversalBlePlatformChannel {
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture =
|
||||
pigeonVar_channel.send(<Object?>[filter]);
|
||||
pigeonVar_channel.send(<Object?>[filter, config]);
|
||||
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||
if (pigeonVar_replyList == null) {
|
||||
throw _createConnectionError(pigeonVar_channelName);
|
||||
|
||||
@@ -49,6 +49,7 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
|
||||
await _executeWithErrorHandling(
|
||||
() => _channel.startScan(
|
||||
scanFilter.toUniversalScanFilter(),
|
||||
platformConfig?.toUniversalScanConfig(),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -69,8 +70,10 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
|
||||
}
|
||||
|
||||
@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) =>
|
||||
@@ -329,3 +332,11 @@ extension _BleLogLevelExtension on BleLogLevel {
|
||||
BleLogLevel.verbose => UniversalBleLogLevel.verbose,
|
||||
};
|
||||
}
|
||||
|
||||
extension _PlatformConfigExtension on PlatformConfig? {
|
||||
UniversalScanConfig? toUniversalScanConfig() {
|
||||
return UniversalScanConfig(
|
||||
android: this?.android,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ abstract class UniversalBlePlatform {
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import 'dart:collection';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_web_bluetooth/flutter_web_bluetooth.dart';
|
||||
import 'package:universal_ble/src/utils/universal_logger.dart';
|
||||
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
class UniversalBleWeb extends UniversalBlePlatform {
|
||||
|
||||
@@ -5,3 +5,9 @@ export 'package:universal_ble/src/universal_ble_platform_interface.dart';
|
||||
export 'package:universal_ble/src/universal_ble.dart';
|
||||
export 'package:universal_ble/src/models/model_exports.dart';
|
||||
export 'package:universal_ble/src/extensions/exports.dart';
|
||||
export 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart'
|
||||
show
|
||||
AndroidOptions,
|
||||
AndroidScanMode,
|
||||
UniversalBleErrorCode,
|
||||
UniversalBleLogLevel;
|
||||
|
||||
@@ -35,7 +35,7 @@ abstract class UniversalBlePlatformChannel {
|
||||
@async
|
||||
bool disableBluetooth();
|
||||
|
||||
void startScan(UniversalScanFilter? filter);
|
||||
void startScan(UniversalScanFilter? filter, UniversalScanConfig? config);
|
||||
|
||||
void stopScan();
|
||||
|
||||
@@ -171,6 +171,37 @@ class UniversalBleDescriptor {
|
||||
UniversalBleDescriptor(this.uuid);
|
||||
}
|
||||
|
||||
/// Scan config
|
||||
|
||||
enum AndroidScanMode {
|
||||
balanced,
|
||||
lowLatency,
|
||||
lowPower,
|
||||
opportunistic,
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// Set [reportDelayMillis] timestamp for Bluetooth LE scan. If set to 0, you will be notified of scan results immediately.
|
||||
/// If > 0, scan results are queued up and delivered after the requested delay or 5000 milliseconds (whichever is higher).
|
||||
/// Note scan results may be delivered sooner if the internal buffers fill up.
|
||||
class AndroidOptions {
|
||||
bool? requestLocationPermission;
|
||||
AndroidScanMode? scanMode;
|
||||
int? reportDelayMillis;
|
||||
AndroidOptions({
|
||||
this.requestLocationPermission,
|
||||
this.scanMode,
|
||||
this.reportDelayMillis,
|
||||
});
|
||||
}
|
||||
|
||||
class UniversalScanConfig {
|
||||
AndroidOptions? android;
|
||||
UniversalScanConfig(this.android);
|
||||
}
|
||||
|
||||
/// Scan Filters
|
||||
class UniversalScanFilter {
|
||||
final List<String> withServices;
|
||||
|
||||
@@ -331,6 +331,127 @@ UniversalBleDescriptor UniversalBleDescriptor::FromEncodableList(const Encodable
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// AndroidOptions
|
||||
|
||||
AndroidOptions::AndroidOptions() {}
|
||||
|
||||
AndroidOptions::AndroidOptions(
|
||||
const bool* request_location_permission,
|
||||
const AndroidScanMode* scan_mode,
|
||||
const int64_t* report_delay_millis)
|
||||
: request_location_permission_(request_location_permission ? std::optional<bool>(*request_location_permission) : std::nullopt),
|
||||
scan_mode_(scan_mode ? std::optional<AndroidScanMode>(*scan_mode) : std::nullopt),
|
||||
report_delay_millis_(report_delay_millis ? std::optional<int64_t>(*report_delay_millis) : std::nullopt) {}
|
||||
|
||||
const bool* AndroidOptions::request_location_permission() const {
|
||||
return request_location_permission_ ? &(*request_location_permission_) : nullptr;
|
||||
}
|
||||
|
||||
void AndroidOptions::set_request_location_permission(const bool* value_arg) {
|
||||
request_location_permission_ = value_arg ? std::optional<bool>(*value_arg) : std::nullopt;
|
||||
}
|
||||
|
||||
void AndroidOptions::set_request_location_permission(bool value_arg) {
|
||||
request_location_permission_ = value_arg;
|
||||
}
|
||||
|
||||
|
||||
const AndroidScanMode* AndroidOptions::scan_mode() const {
|
||||
return scan_mode_ ? &(*scan_mode_) : nullptr;
|
||||
}
|
||||
|
||||
void AndroidOptions::set_scan_mode(const AndroidScanMode* value_arg) {
|
||||
scan_mode_ = value_arg ? std::optional<AndroidScanMode>(*value_arg) : std::nullopt;
|
||||
}
|
||||
|
||||
void AndroidOptions::set_scan_mode(const AndroidScanMode& value_arg) {
|
||||
scan_mode_ = value_arg;
|
||||
}
|
||||
|
||||
|
||||
const int64_t* AndroidOptions::report_delay_millis() const {
|
||||
return report_delay_millis_ ? &(*report_delay_millis_) : nullptr;
|
||||
}
|
||||
|
||||
void AndroidOptions::set_report_delay_millis(const int64_t* value_arg) {
|
||||
report_delay_millis_ = value_arg ? std::optional<int64_t>(*value_arg) : std::nullopt;
|
||||
}
|
||||
|
||||
void AndroidOptions::set_report_delay_millis(int64_t value_arg) {
|
||||
report_delay_millis_ = value_arg;
|
||||
}
|
||||
|
||||
|
||||
EncodableList AndroidOptions::ToEncodableList() const {
|
||||
EncodableList list;
|
||||
list.reserve(3);
|
||||
list.push_back(request_location_permission_ ? EncodableValue(*request_location_permission_) : EncodableValue());
|
||||
list.push_back(scan_mode_ ? CustomEncodableValue(*scan_mode_) : EncodableValue());
|
||||
list.push_back(report_delay_millis_ ? EncodableValue(*report_delay_millis_) : EncodableValue());
|
||||
return list;
|
||||
}
|
||||
|
||||
AndroidOptions AndroidOptions::FromEncodableList(const EncodableList& list) {
|
||||
AndroidOptions decoded;
|
||||
auto& encodable_request_location_permission = list[0];
|
||||
if (!encodable_request_location_permission.IsNull()) {
|
||||
decoded.set_request_location_permission(std::get<bool>(encodable_request_location_permission));
|
||||
}
|
||||
auto& encodable_scan_mode = list[1];
|
||||
if (!encodable_scan_mode.IsNull()) {
|
||||
decoded.set_scan_mode(std::any_cast<const AndroidScanMode&>(std::get<CustomEncodableValue>(encodable_scan_mode)));
|
||||
}
|
||||
auto& encodable_report_delay_millis = list[2];
|
||||
if (!encodable_report_delay_millis.IsNull()) {
|
||||
decoded.set_report_delay_millis(std::get<int64_t>(encodable_report_delay_millis));
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// UniversalScanConfig
|
||||
|
||||
UniversalScanConfig::UniversalScanConfig() {}
|
||||
|
||||
UniversalScanConfig::UniversalScanConfig(const AndroidOptions* android)
|
||||
: android_(android ? std::make_unique<AndroidOptions>(*android) : nullptr) {}
|
||||
|
||||
UniversalScanConfig::UniversalScanConfig(const UniversalScanConfig& other)
|
||||
: android_(other.android_ ? std::make_unique<AndroidOptions>(*other.android_) : nullptr) {}
|
||||
|
||||
UniversalScanConfig& UniversalScanConfig::operator=(const UniversalScanConfig& other) {
|
||||
android_ = other.android_ ? std::make_unique<AndroidOptions>(*other.android_) : nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const AndroidOptions* UniversalScanConfig::android() const {
|
||||
return android_.get();
|
||||
}
|
||||
|
||||
void UniversalScanConfig::set_android(const AndroidOptions* value_arg) {
|
||||
android_ = value_arg ? std::make_unique<AndroidOptions>(*value_arg) : nullptr;
|
||||
}
|
||||
|
||||
void UniversalScanConfig::set_android(const AndroidOptions& value_arg) {
|
||||
android_ = std::make_unique<AndroidOptions>(value_arg);
|
||||
}
|
||||
|
||||
|
||||
EncodableList UniversalScanConfig::ToEncodableList() const {
|
||||
EncodableList list;
|
||||
list.reserve(1);
|
||||
list.push_back(android_ ? CustomEncodableValue(*android_) : EncodableValue());
|
||||
return list;
|
||||
}
|
||||
|
||||
UniversalScanConfig UniversalScanConfig::FromEncodableList(const EncodableList& list) {
|
||||
UniversalScanConfig decoded;
|
||||
auto& encodable_android = list[0];
|
||||
if (!encodable_android.IsNull()) {
|
||||
decoded.set_android(std::any_cast<const AndroidOptions&>(std::get<CustomEncodableValue>(encodable_android)));
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
|
||||
// UniversalScanFilter
|
||||
|
||||
UniversalScanFilter::UniversalScanFilter(
|
||||
@@ -512,27 +633,38 @@ EncodableValue PigeonInternalCodecSerializer::ReadValueOfType(
|
||||
case 130: {
|
||||
const auto& encodable_enum_arg = ReadValue(stream);
|
||||
const int64_t enum_arg_value = encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue();
|
||||
return encodable_enum_arg.IsNull() ? EncodableValue() : CustomEncodableValue(static_cast<UniversalBleErrorCode>(enum_arg_value));
|
||||
return encodable_enum_arg.IsNull() ? EncodableValue() : CustomEncodableValue(static_cast<AndroidScanMode>(enum_arg_value));
|
||||
}
|
||||
case 131: {
|
||||
return CustomEncodableValue(UniversalBleScanResult::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
const auto& encodable_enum_arg = ReadValue(stream);
|
||||
const int64_t enum_arg_value = encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue();
|
||||
return encodable_enum_arg.IsNull() ? EncodableValue() : CustomEncodableValue(static_cast<UniversalBleErrorCode>(enum_arg_value));
|
||||
}
|
||||
case 132: {
|
||||
return CustomEncodableValue(UniversalBleService::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
return CustomEncodableValue(UniversalBleScanResult::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 133: {
|
||||
return CustomEncodableValue(UniversalBleCharacteristic::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
return CustomEncodableValue(UniversalBleService::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 134: {
|
||||
return CustomEncodableValue(UniversalBleDescriptor::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
return CustomEncodableValue(UniversalBleCharacteristic::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 135: {
|
||||
return CustomEncodableValue(UniversalScanFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
return CustomEncodableValue(UniversalBleDescriptor::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 136: {
|
||||
return CustomEncodableValue(UniversalManufacturerDataFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
return CustomEncodableValue(AndroidOptions::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 137: {
|
||||
return CustomEncodableValue(UniversalScanConfig::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 138: {
|
||||
return CustomEncodableValue(UniversalScanFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 139: {
|
||||
return CustomEncodableValue(UniversalManufacturerDataFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 140: {
|
||||
return CustomEncodableValue(UniversalManufacturerData::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
default:
|
||||
@@ -549,43 +681,58 @@ void PigeonInternalCodecSerializer::WriteValue(
|
||||
WriteValue(EncodableValue(static_cast<int>(std::any_cast<UniversalBleLogLevel>(*custom_value))), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalBleErrorCode)) {
|
||||
if (custom_value->type() == typeid(AndroidScanMode)) {
|
||||
stream->WriteByte(130);
|
||||
WriteValue(EncodableValue(static_cast<int>(std::any_cast<AndroidScanMode>(*custom_value))), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalBleErrorCode)) {
|
||||
stream->WriteByte(131);
|
||||
WriteValue(EncodableValue(static_cast<int>(std::any_cast<UniversalBleErrorCode>(*custom_value))), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalBleScanResult)) {
|
||||
stream->WriteByte(131);
|
||||
stream->WriteByte(132);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalBleScanResult>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalBleService)) {
|
||||
stream->WriteByte(132);
|
||||
stream->WriteByte(133);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalBleService>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalBleCharacteristic)) {
|
||||
stream->WriteByte(133);
|
||||
stream->WriteByte(134);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalBleCharacteristic>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalBleDescriptor)) {
|
||||
stream->WriteByte(134);
|
||||
stream->WriteByte(135);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalBleDescriptor>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(AndroidOptions)) {
|
||||
stream->WriteByte(136);
|
||||
WriteValue(EncodableValue(std::any_cast<AndroidOptions>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalScanConfig)) {
|
||||
stream->WriteByte(137);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalScanConfig>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalScanFilter)) {
|
||||
stream->WriteByte(135);
|
||||
stream->WriteByte(138);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalScanFilter>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalManufacturerDataFilter)) {
|
||||
stream->WriteByte(136);
|
||||
stream->WriteByte(139);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalManufacturerDataFilter>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalManufacturerData)) {
|
||||
stream->WriteByte(137);
|
||||
stream->WriteByte(140);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalManufacturerData>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
@@ -741,7 +888,9 @@ void UniversalBlePlatformChannel::SetUp(
|
||||
const auto& args = std::get<EncodableList>(message);
|
||||
const auto& encodable_filter_arg = args.at(0);
|
||||
const auto* filter_arg = encodable_filter_arg.IsNull() ? nullptr : &(std::any_cast<const UniversalScanFilter&>(std::get<CustomEncodableValue>(encodable_filter_arg)));
|
||||
std::optional<FlutterError> output = api->StartScan(filter_arg);
|
||||
const auto& encodable_config_arg = args.at(1);
|
||||
const auto* config_arg = encodable_config_arg.IsNull() ? nullptr : &(std::any_cast<const UniversalScanConfig&>(std::get<CustomEncodableValue>(encodable_config_arg)));
|
||||
std::optional<FlutterError> output = api->StartScan(filter_arg, config_arg);
|
||||
if (output.has_value()) {
|
||||
reply(WrapError(output.value()));
|
||||
return;
|
||||
|
||||
@@ -66,6 +66,14 @@ enum class UniversalBleLogLevel {
|
||||
kVerbose = 5
|
||||
};
|
||||
|
||||
// Scan config
|
||||
enum class AndroidScanMode {
|
||||
kBalanced = 0,
|
||||
kLowLatency = 1,
|
||||
kLowPower = 2,
|
||||
kOpportunistic = 3
|
||||
};
|
||||
|
||||
// Unified error codes for all platforms
|
||||
enum class UniversalBleErrorCode {
|
||||
kUnknownError = 0,
|
||||
@@ -275,6 +283,78 @@ class UniversalBleDescriptor {
|
||||
};
|
||||
|
||||
|
||||
// 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.
|
||||
// Set [reportDelayMillis] timestamp for Bluetooth LE scan. If set to 0, you will be notified of scan results immediately.
|
||||
// If > 0, scan results are queued up and delivered after the requested delay or 5000 milliseconds (whichever is higher).
|
||||
// Note scan results may be delivered sooner if the internal buffers fill up.
|
||||
//
|
||||
// Generated class from Pigeon that represents data sent in messages.
|
||||
class AndroidOptions {
|
||||
public:
|
||||
// Constructs an object setting all non-nullable fields.
|
||||
AndroidOptions();
|
||||
|
||||
// Constructs an object setting all fields.
|
||||
explicit AndroidOptions(
|
||||
const bool* request_location_permission,
|
||||
const AndroidScanMode* scan_mode,
|
||||
const int64_t* report_delay_millis);
|
||||
|
||||
const bool* request_location_permission() const;
|
||||
void set_request_location_permission(const bool* value_arg);
|
||||
void set_request_location_permission(bool value_arg);
|
||||
|
||||
const AndroidScanMode* scan_mode() const;
|
||||
void set_scan_mode(const AndroidScanMode* value_arg);
|
||||
void set_scan_mode(const AndroidScanMode& value_arg);
|
||||
|
||||
const int64_t* report_delay_millis() const;
|
||||
void set_report_delay_millis(const int64_t* value_arg);
|
||||
void set_report_delay_millis(int64_t value_arg);
|
||||
|
||||
private:
|
||||
static AndroidOptions FromEncodableList(const flutter::EncodableList& list);
|
||||
flutter::EncodableList ToEncodableList() const;
|
||||
friend class UniversalScanConfig;
|
||||
friend class UniversalBlePlatformChannel;
|
||||
friend class UniversalBleCallbackChannel;
|
||||
friend class PigeonInternalCodecSerializer;
|
||||
std::optional<bool> request_location_permission_;
|
||||
std::optional<AndroidScanMode> scan_mode_;
|
||||
std::optional<int64_t> report_delay_millis_;
|
||||
};
|
||||
|
||||
|
||||
// Generated class from Pigeon that represents data sent in messages.
|
||||
class UniversalScanConfig {
|
||||
public:
|
||||
// Constructs an object setting all non-nullable fields.
|
||||
UniversalScanConfig();
|
||||
|
||||
// Constructs an object setting all fields.
|
||||
explicit UniversalScanConfig(const AndroidOptions* android);
|
||||
|
||||
~UniversalScanConfig() = default;
|
||||
UniversalScanConfig(const UniversalScanConfig& other);
|
||||
UniversalScanConfig& operator=(const UniversalScanConfig& other);
|
||||
UniversalScanConfig(UniversalScanConfig&& other) = default;
|
||||
UniversalScanConfig& operator=(UniversalScanConfig&& other) noexcept = default;
|
||||
const AndroidOptions* android() const;
|
||||
void set_android(const AndroidOptions* value_arg);
|
||||
void set_android(const AndroidOptions& value_arg);
|
||||
|
||||
private:
|
||||
static UniversalScanConfig FromEncodableList(const flutter::EncodableList& list);
|
||||
flutter::EncodableList ToEncodableList() const;
|
||||
friend class UniversalBlePlatformChannel;
|
||||
friend class UniversalBleCallbackChannel;
|
||||
friend class PigeonInternalCodecSerializer;
|
||||
std::unique_ptr<AndroidOptions> android_;
|
||||
};
|
||||
|
||||
|
||||
// Scan Filters
|
||||
//
|
||||
// Generated class from Pigeon that represents data sent in messages.
|
||||
@@ -399,7 +479,9 @@ class UniversalBlePlatformChannel {
|
||||
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;
|
||||
virtual std::optional<FlutterError> StartScan(const UniversalScanFilter* filter) = 0;
|
||||
virtual std::optional<FlutterError> StartScan(
|
||||
const UniversalScanFilter* filter,
|
||||
const UniversalScanConfig* config) = 0;
|
||||
virtual std::optional<FlutterError> StopScan() = 0;
|
||||
virtual ErrorOr<bool> IsScanning() = 0;
|
||||
virtual std::optional<FlutterError> Connect(
|
||||
|
||||
@@ -134,7 +134,7 @@ void UniversalBlePlugin::RequestPermissions(
|
||||
}
|
||||
|
||||
std::optional<FlutterError>
|
||||
UniversalBlePlugin::StartScan(const UniversalScanFilter *filter) {
|
||||
UniversalBlePlugin::StartScan(const UniversalScanFilter *filter, const UniversalScanConfig *config) {
|
||||
|
||||
if (!bluetooth_radio_ || bluetooth_radio_.State() != RadioState::On) {
|
||||
return create_flutter_error(UniversalBleErrorCode::kBluetoothNotAvailable,
|
||||
|
||||
@@ -174,7 +174,7 @@ private:
|
||||
std::optional<FlutterError>
|
||||
SetLogLevel(const UniversalBleLogLevel &log_level) override;
|
||||
std::optional<FlutterError>
|
||||
StartScan(const UniversalScanFilter *filter) override;
|
||||
StartScan(const UniversalScanFilter *filter, const UniversalScanConfig *config) override;
|
||||
std::optional<FlutterError> StopScan() override;
|
||||
ErrorOr<bool> IsScanning() override;
|
||||
std::optional<FlutterError> Connect(const std::string &device_id, const bool *auto_connect) override;
|
||||
|
||||
Reference in New Issue
Block a user