mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-16 19:26:12 -04:00
New compatibility option for receiving BLE data (#82)
* Improve compat options: Add alternative method for receiving BLE scan results via PendingIntents * Add missing logtags * Reduce log spam * Make the linter happy
This commit is contained in:
@@ -23,4 +23,13 @@
|
|||||||
android:name="android.permission.BLUETOOTH_SCAN"
|
android:name="android.permission.BLUETOOTH_SCAN"
|
||||||
android:usesPermissionFlags="neverForLocation" />
|
android:usesPermissionFlags="neverForLocation" />
|
||||||
|
|
||||||
|
<application>
|
||||||
|
<receiver
|
||||||
|
android:name=".bluetooth.BleScanResultReceiver"
|
||||||
|
android:exported="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="eu.darken.capod.bluetooth.DELIVER_SCAN_RESULTS" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package eu.darken.capod.common.bluetooth
|
||||||
|
|
||||||
|
import android.bluetooth.le.ScanResult
|
||||||
|
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||||
|
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||||
|
import eu.darken.capod.common.debug.logging.log
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class BleScanResultForwarder @Inject constructor() {
|
||||||
|
|
||||||
|
private val forwarder = MutableSharedFlow<Collection<ScanResult>>(
|
||||||
|
replay = 0,
|
||||||
|
extraBufferCapacity = 128,
|
||||||
|
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||||
|
)
|
||||||
|
val results: Flow<Collection<ScanResult>> = forwarder
|
||||||
|
|
||||||
|
fun forward(scanResults: Collection<ScanResult>) {
|
||||||
|
log(TAG, VERBOSE) { "forward($scanResults)" }
|
||||||
|
val success = forwarder.tryEmit(scanResults)
|
||||||
|
if (!success) log(TAG, WARN) { "Failed to forward (overflow?) $scanResults" }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = logTag("Bluetooth", "BleScanner", "Forwarder")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package eu.darken.capod.common.bluetooth
|
||||||
|
|
||||||
|
import android.bluetooth.le.BluetoothLeScanner
|
||||||
|
import android.bluetooth.le.ScanResult
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import eu.darken.capod.common.coroutine.AppScope
|
||||||
|
import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE
|
||||||
|
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||||
|
import eu.darken.capod.common.debug.logging.log
|
||||||
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class BleScanResultReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
|
@Inject @AppScope lateinit var appScope: CoroutineScope
|
||||||
|
@Inject lateinit var scanResultForwarder: BleScanResultForwarder
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
log(TAG, VERBOSE) { "onReceive($context, $intent)" }
|
||||||
|
if (intent.action != ACTION) {
|
||||||
|
log(TAG, WARN) { "Unknown action: ${intent.action}" }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (intent.extras == null) {
|
||||||
|
log(TAG) { "Extras are null!" }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val errorCode = intent.getIntExtra(BluetoothLeScanner.EXTRA_ERROR_CODE, 0)
|
||||||
|
log(TAG, VERBOSE) { "errorCode=$errorCode" }
|
||||||
|
if (errorCode != 0) {
|
||||||
|
log(TAG, WARN) { "ScanCallback error code: $errorCode" }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val callbackType = intent.getIntExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, -1)
|
||||||
|
log(TAG, VERBOSE) { "callbackType=$callbackType" }
|
||||||
|
|
||||||
|
val scanResults = intent.getParcelableArrayListExtra<ScanResult>(BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT)
|
||||||
|
log(TAG, VERBOSE) { "scanResults=$scanResults" }
|
||||||
|
|
||||||
|
if (scanResults == null) {
|
||||||
|
log(TAG) { "Scan results were empty!" }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val pending = goAsync()
|
||||||
|
appScope.launch {
|
||||||
|
scanResultForwarder.forward(scanResults)
|
||||||
|
pending.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = logTag("Bluetooth", "BleScanner", "Forwarder", "Receiver")
|
||||||
|
const val ACTION = "eu.darken.capod.bluetooth.DELIVER_SCAN_RESULTS"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,21 @@
|
|||||||
package eu.darken.capod.common.bluetooth
|
package eu.darken.capod.common.bluetooth
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.bluetooth.le.ScanCallback
|
import android.bluetooth.le.ScanCallback
|
||||||
import android.bluetooth.le.ScanFilter
|
import android.bluetooth.le.ScanFilter
|
||||||
import android.bluetooth.le.ScanResult
|
import android.bluetooth.le.ScanResult
|
||||||
import android.bluetooth.le.ScanSettings
|
import android.bluetooth.le.ScanSettings
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import eu.darken.capod.common.debug.logging.Logging.Priority.*
|
import eu.darken.capod.common.debug.logging.Logging.Priority.*
|
||||||
import eu.darken.capod.common.debug.logging.log
|
import eu.darken.capod.common.debug.logging.log
|
||||||
import eu.darken.capod.common.debug.logging.logTag
|
import eu.darken.capod.common.debug.logging.logTag
|
||||||
|
import eu.darken.capod.common.notifications.PendingIntentCompat
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -25,13 +26,15 @@ class BleScanner @Inject constructor(
|
|||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val bluetoothManager: BluetoothManager2,
|
private val bluetoothManager: BluetoothManager2,
|
||||||
private val fakeBleData: FakeBleData,
|
private val fakeBleData: FakeBleData,
|
||||||
|
private val scanResultForwarder: BleScanResultForwarder,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@SuppressLint("MissingPermission") fun scan(
|
@SuppressLint("MissingPermission") fun scan(
|
||||||
filters: Set<ScanFilter>,
|
filters: Set<ScanFilter>,
|
||||||
scannerMode: ScannerMode = ScannerMode.BALANCED,
|
scannerMode: ScannerMode = ScannerMode.BALANCED,
|
||||||
offloadFiltering: Boolean = true,
|
disableOffloadFiltering: Boolean = true,
|
||||||
offloadBatching: Boolean = true,
|
disableOffloadBatching: Boolean = true,
|
||||||
|
disableDirectScanCallback: Boolean = true,
|
||||||
): Flow<Collection<BleScanResult>> = callbackFlow {
|
): Flow<Collection<BleScanResult>> = callbackFlow {
|
||||||
log(TAG) { "scan(filters=$filters, scannerMode=$scannerMode)" }
|
log(TAG) { "scan(filters=$filters, scannerMode=$scannerMode)" }
|
||||||
|
|
||||||
@@ -39,17 +42,19 @@ class BleScanner @Inject constructor(
|
|||||||
|
|
||||||
val useOffloadedFiltering = adapter.isOffloadedFilteringSupported.also {
|
val useOffloadedFiltering = adapter.isOffloadedFilteringSupported.also {
|
||||||
log(TAG, if (it) DEBUG else WARN) { "isOffloadedFilteringSupported=$it" }
|
log(TAG, if (it) DEBUG else WARN) { "isOffloadedFilteringSupported=$it" }
|
||||||
} && offloadFiltering
|
} && !disableOffloadFiltering
|
||||||
if (!offloadFiltering) log(TAG, WARN) { "Offloaded filtering is disabled!" }
|
if (disableOffloadFiltering) log(TAG, WARN) { "Offloaded filtering is disabled!" }
|
||||||
|
|
||||||
val useOffloadedBatching = adapter.isOffloadedScanBatchingSupported.also {
|
val useOffloadedBatching = adapter.isOffloadedScanBatchingSupported.also {
|
||||||
log(TAG, if (it) DEBUG else WARN) { "isOffloadedScanBatchingSupported=$it" }
|
log(TAG, if (it) DEBUG else WARN) { "isOffloadedScanBatchingSupported=$it" }
|
||||||
} && offloadBatching
|
} && !disableOffloadBatching
|
||||||
if (!offloadBatching) log(TAG, WARN) { "Offloaded scan-batching is disabled!" }
|
if (disableOffloadBatching) log(TAG, WARN) { "Offloaded scan-batching is disabled!" }
|
||||||
|
|
||||||
|
if (disableDirectScanCallback) log(TAG, WARN) { "Direct scan callback is disabled!" }
|
||||||
|
|
||||||
val scanner = bluetoothManager.scanner ?: throw IllegalStateException("BLE scanner unavailable")
|
val scanner = bluetoothManager.scanner ?: throw IllegalStateException("BLE scanner unavailable")
|
||||||
|
|
||||||
val resultFilter: (Collection<ScanResult>) -> Collection<BleScanResult> = { results ->
|
val filterResults: (Collection<ScanResult>) -> Collection<BleScanResult> = { results ->
|
||||||
results
|
results
|
||||||
.filter { result ->
|
.filter { result ->
|
||||||
val passed = when {
|
val passed = when {
|
||||||
@@ -72,7 +77,7 @@ class BleScanner @Inject constructor(
|
|||||||
"onScanResult(delay=${delay}ms, callbackType=$callbackType, result=$result)"
|
"onScanResult(delay=${delay}ms, callbackType=$callbackType, result=$result)"
|
||||||
}
|
}
|
||||||
|
|
||||||
trySend(resultFilter(setOf(result)))
|
trySend(filterResults(setOf(result)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBatchScanResults(results: MutableList<ScanResult>) {
|
override fun onBatchScanResults(results: MutableList<ScanResult>) {
|
||||||
@@ -82,7 +87,7 @@ class BleScanner @Inject constructor(
|
|||||||
"onBatchScanResults(delay=${delay}ms, results=$results)"
|
"onBatchScanResults(delay=${delay}ms, results=$results)"
|
||||||
}
|
}
|
||||||
|
|
||||||
trySend(resultFilter(results))
|
trySend(filterResults(results))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onScanFailed(errorCode: Int) {
|
override fun onScanFailed(errorCode: Int) {
|
||||||
@@ -90,6 +95,37 @@ class BleScanner @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val forwarderConsumer = if (disableDirectScanCallback) {
|
||||||
|
scanResultForwarder.results
|
||||||
|
.onEach { results -> trySend(filterResults(results)) }
|
||||||
|
.launchIn(this)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val flushJob = if (!disableDirectScanCallback) {
|
||||||
|
launch {
|
||||||
|
log(TAG) { "Flush job launched" }
|
||||||
|
while (isActive) {
|
||||||
|
log(TAG, VERBOSE) { "Flushing scan results." }
|
||||||
|
// Can undercut the minimum setReportDelay(), e.g. 5000ms on a Pixel5@12
|
||||||
|
adapter.bluetoothLeScanner.flushPendingScanResults(callback)
|
||||||
|
when (scannerMode) {
|
||||||
|
ScannerMode.LOW_POWER -> break
|
||||||
|
ScannerMode.BALANCED -> delay(2000)
|
||||||
|
ScannerMode.LOW_LATENCY -> delay(500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val filterList = when {
|
||||||
|
useOffloadedFiltering -> filters.toList()
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
val scanSettings = ScanSettings.Builder().apply {
|
val scanSettings = ScanSettings.Builder().apply {
|
||||||
setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
|
setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
|
||||||
when (scannerMode) {
|
when (scannerMode) {
|
||||||
@@ -122,38 +158,50 @@ class BleScanner @Inject constructor(
|
|||||||
setReportDelay(delay)
|
setReportDelay(delay)
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
|
if (disableDirectScanCallback) {
|
||||||
val flushJob = launch {
|
val callbackIntent = createStartIntent()
|
||||||
log(TAG) { "Flush job launched" }
|
log(TAG) { "Intent callback: startScan(filters=$filters, settings=$scanSettings, callbackIntent=$callbackIntent)" }
|
||||||
while (isActive) {
|
scanner.startScan(filterList, scanSettings, callbackIntent)
|
||||||
log(TAG, VERBOSE) { "Flushing scan results." }
|
} else {
|
||||||
// Can undercut the minimum setReportDelay(), e.g. 5000ms on a Pixel5@12
|
log(TAG) { "Direct callback: startScan(filters=$filters, settings=$scanSettings, callback=$callback)" }
|
||||||
adapter.bluetoothLeScanner.flushPendingScanResults(callback)
|
scanner.startScan(filterList, scanSettings, callback)
|
||||||
when (scannerMode) {
|
|
||||||
ScannerMode.LOW_POWER -> break
|
|
||||||
ScannerMode.BALANCED -> delay(2000)
|
|
||||||
ScannerMode.LOW_LATENCY -> delay(500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log(TAG) { "startScan(filters=$filters, settings=$scanSettings, callback=$callback)" }
|
|
||||||
val filterList = when {
|
|
||||||
useOffloadedFiltering -> filters.toList()
|
|
||||||
else -> emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
scanner.startScan(filterList, scanSettings, callback)
|
|
||||||
|
|
||||||
awaitClose {
|
awaitClose {
|
||||||
flushJob.cancel()
|
forwarderConsumer?.cancel()
|
||||||
scanner.stopScan(callback)
|
flushJob?.cancel()
|
||||||
|
if (disableDirectScanCallback) {
|
||||||
|
scanner.stopScan(createStopIntent())
|
||||||
|
} else {
|
||||||
|
scanner.stopScan(callback)
|
||||||
|
}
|
||||||
log(TAG) { "BleScanner stopped" }
|
log(TAG) { "BleScanner stopped" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.map { fakeBleData.maybeAddfakeData(it) }
|
.map { fakeBleData.maybeAddfakeData(it) }
|
||||||
|
|
||||||
|
private val receiverIntent by lazy {
|
||||||
|
Intent(context, BleScanResultReceiver::class.java).apply {
|
||||||
|
action = BleScanResultReceiver.ACTION
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createStartIntent(): PendingIntent = PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
CALLBACK_INTENT_REQUESTCODE,
|
||||||
|
receiverIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_MUTABLE
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun createStopIntent(): PendingIntent = PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
270,
|
||||||
|
receiverIntent,
|
||||||
|
PendingIntentCompat.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val CALLBACK_INTENT_REQUESTCODE = 270
|
||||||
private val TAG = logTag("Bluetooth", "BleScanner")
|
private val TAG = logTag("Bluetooth", "BleScanner")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+5
@@ -9,4 +9,9 @@ object PendingIntentCompat {
|
|||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
val FLAG_MUTABLE: Int = if (hasApiLevel(31)) {
|
||||||
|
PendingIntent.FLAG_MUTABLE
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -33,9 +33,12 @@ class GeneralSettings @Inject constructor(
|
|||||||
val mainDeviceAddress = preferences.createFlowPreference<String?>("core.maindevice.address", null)
|
val mainDeviceAddress = preferences.createFlowPreference<String?>("core.maindevice.address", null)
|
||||||
val mainDeviceModel = preferences.createFlowPreference("core.maindevice.model", PodDevice.Model.UNKNOWN, moshi)
|
val mainDeviceModel = preferences.createFlowPreference("core.maindevice.model", PodDevice.Model.UNKNOWN, moshi)
|
||||||
|
|
||||||
val isOffloadedFilteringDisabled =
|
val isOffloadedFilteringDisabled = preferences.createFlowPreference(
|
||||||
preferences.createFlowPreference("core.compat.offloaded.filtering.disabled", false)
|
"core.compat.offloaded.filtering.disabled",
|
||||||
|
false
|
||||||
|
)
|
||||||
val isOffloadedBatchingDisabled = preferences.createFlowPreference("core.compat.offloaded.batching.disabled", false)
|
val isOffloadedBatchingDisabled = preferences.createFlowPreference("core.compat.offloaded.batching.disabled", false)
|
||||||
|
val useIndirectScanResultCallback = preferences.createFlowPreference("core.compat.indirectcallback.enabled", false)
|
||||||
|
|
||||||
override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper(
|
override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper(
|
||||||
monitorMode,
|
monitorMode,
|
||||||
@@ -45,6 +48,7 @@ class GeneralSettings @Inject constructor(
|
|||||||
mainDeviceAddress,
|
mainDeviceAddress,
|
||||||
isOffloadedFilteringDisabled,
|
isOffloadedFilteringDisabled,
|
||||||
isOffloadedBatchingDisabled,
|
isOffloadedBatchingDisabled,
|
||||||
|
useIndirectScanResultCallback,
|
||||||
debugSettings.isAutoReportingEnabled,
|
debugSettings.isAutoReportingEnabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -88,7 +88,8 @@ class PodMonitor @Inject constructor(
|
|||||||
val scannerMode: ScannerMode,
|
val scannerMode: ScannerMode,
|
||||||
val showUnfiltered: Boolean,
|
val showUnfiltered: Boolean,
|
||||||
val offloadedFilteringDisabled: Boolean,
|
val offloadedFilteringDisabled: Boolean,
|
||||||
val offloadedBatchingDisabled: Boolean
|
val offloadedBatchingDisabled: Boolean,
|
||||||
|
val disableDirectCallback: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun createBleScanner() = combine(
|
private fun createBleScanner() = combine(
|
||||||
@@ -96,12 +97,20 @@ class PodMonitor @Inject constructor(
|
|||||||
debugSettings.showUnfiltered.flow,
|
debugSettings.showUnfiltered.flow,
|
||||||
generalSettings.isOffloadedBatchingDisabled.flow,
|
generalSettings.isOffloadedBatchingDisabled.flow,
|
||||||
generalSettings.isOffloadedFilteringDisabled.flow,
|
generalSettings.isOffloadedFilteringDisabled.flow,
|
||||||
) { scannermode, showUnfiltered, isOffloadedBatchingDisabled, isOffloadedFilteringDisabled ->
|
generalSettings.useIndirectScanResultCallback.flow,
|
||||||
|
) {
|
||||||
|
scannermode,
|
||||||
|
showUnfiltered,
|
||||||
|
isOffloadedBatchingDisabled,
|
||||||
|
isOffloadedFilteringDisabled,
|
||||||
|
useIndirectScanResultCallback,
|
||||||
|
->
|
||||||
ScannerOptions(
|
ScannerOptions(
|
||||||
scannerMode = scannermode,
|
scannerMode = scannermode,
|
||||||
showUnfiltered = showUnfiltered,
|
showUnfiltered = showUnfiltered,
|
||||||
offloadedFilteringDisabled = isOffloadedFilteringDisabled,
|
offloadedFilteringDisabled = isOffloadedFilteringDisabled,
|
||||||
offloadedBatchingDisabled = isOffloadedBatchingDisabled,
|
offloadedBatchingDisabled = isOffloadedBatchingDisabled,
|
||||||
|
disableDirectCallback = useIndirectScanResultCallback,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.flatMapLatest { options ->
|
.flatMapLatest { options ->
|
||||||
@@ -116,8 +125,9 @@ class PodMonitor @Inject constructor(
|
|||||||
bleScanner.scan(
|
bleScanner.scan(
|
||||||
filters = filters,
|
filters = filters,
|
||||||
scannerMode = options.scannerMode,
|
scannerMode = options.scannerMode,
|
||||||
offloadFiltering = !options.offloadedFilteringDisabled,
|
disableOffloadFiltering = options.offloadedFilteringDisabled,
|
||||||
offloadBatching = !options.offloadedBatchingDisabled
|
disableOffloadBatching = options.offloadedBatchingDisabled,
|
||||||
|
disableDirectScanCallback = options.disableDirectCallback,
|
||||||
).map { preFilterAndMap(it) }
|
).map { preFilterAndMap(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M6.91 5.5L9.21 7.79L7.79 9.21L5.5 6.91L3.21 9.21L1.79 7.79L4.09 5.5L1.79 3.21L3.21 1.79L5.5 4.09L7.79 1.79L9.21 3.21M22.21 16.21L20.79 14.79L18.5 17.09L16.21 14.79L14.79 16.21L17.09 18.5L14.79 20.79L16.21 22.21L18.5 19.91L20.79 22.21L22.21 20.79L19.91 18.5M20.4 6.83L17.18 11L15.6 9.73L16.77 8.23A9.08 9.08 0 0 0 10.11 13.85A4.5 4.5 0 1 1 7.5 13A4 4 0 0 1 8.28 13.08A11.27 11.27 0 0 1 16.43 6.26L15 5.18L16.27 3.6M10 17.5A2.5 2.5 0 1 0 7.5 20A2.5 2.5 0 0 0 10 17.5Z" />
|
||||||
|
</vector>
|
||||||
@@ -25,9 +25,9 @@ class BluetoothEventReceiver : BroadcastReceiver() {
|
|||||||
@Inject @AppScope lateinit var appScope: CoroutineScope
|
@Inject @AppScope lateinit var appScope: CoroutineScope
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
log { "onReceive($context, $intent)" }
|
log(TAG) { "onReceive($context, $intent)" }
|
||||||
if (!EXPECTED_ACTIONS.contains(intent.action)) {
|
if (!EXPECTED_ACTIONS.contains(intent.action)) {
|
||||||
log(WARN) { "Unknown action: $intent.action" }
|
log(TAG, WARN) { "Unknown action: ${intent.action}" }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ class BluetoothEventReceiver : BroadcastReceiver() {
|
|||||||
val supportedFeatures = ContinuityProtocol.BLE_FEATURE_UUIDS.filter { bluetoothDevice.hasFeature(it) }
|
val supportedFeatures = ContinuityProtocol.BLE_FEATURE_UUIDS.filter { bluetoothDevice.hasFeature(it) }
|
||||||
|
|
||||||
if (supportedFeatures.isEmpty()) {
|
if (supportedFeatures.isEmpty()) {
|
||||||
log { "Device has no features we support." }
|
log(TAG) { "Device has no features we support." }
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
log { "Device has the following we features we support $supportedFeatures" }
|
log { "Device has the following we features we support $supportedFeatures" }
|
||||||
@@ -49,7 +49,7 @@ class BluetoothEventReceiver : BroadcastReceiver() {
|
|||||||
|
|
||||||
val pending = goAsync()
|
val pending = goAsync()
|
||||||
appScope.launch {
|
appScope.launch {
|
||||||
log { "Starting monitor" }
|
log(TAG) { "Starting monitor" }
|
||||||
monitorControl.startMonitor(bluetoothDevice, forceStart = false)
|
monitorControl.startMonitor(bluetoothDevice, forceStart = false)
|
||||||
pending.finish()
|
pending.finish()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,4 +100,6 @@
|
|||||||
<string name="translators_thanks_description">darken</string>
|
<string name="translators_thanks_description">darken</string>
|
||||||
|
|
||||||
<string name="widget_description">A widget showing the last known device status.</string>
|
<string name="widget_description">A widget showing the last known device status.</string>
|
||||||
|
<string name="settings_compat_indirectcallback_title">Indirect data delivery</string>
|
||||||
|
<string name="settings_compat_indirectcallback_summary">Use an alternative method to receive BLE data from the system (broadcast instead of callback).</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -61,6 +61,12 @@
|
|||||||
android:summary="@string/settings_compat_offloaded_batching_disabled_summary"
|
android:summary="@string/settings_compat_offloaded_batching_disabled_summary"
|
||||||
android:title="@string/settings_compat_offloaded_batching_disabled_title" />
|
android:title="@string/settings_compat_offloaded_batching_disabled_title" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:icon="@drawable/ic_strategy_24"
|
||||||
|
android:key="core.compat.indirectcallback.enabled"
|
||||||
|
android:summary="@string/settings_compat_indirectcallback_summary"
|
||||||
|
android:title="@string/settings_compat_indirectcallback_title" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/settings_category_other_label">
|
<PreferenceCategory android:title="@string/settings_category_other_label">
|
||||||
|
|||||||
Reference in New Issue
Block a user