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:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user