From 324878d68db5b585224a6b8623de83b7effb4804 Mon Sep 17 00:00:00 2001 From: darken Date: Thu, 10 Feb 2022 10:19:07 +0100 Subject: [PATCH] Add compatibility mode. --- .../capod/common/bluetooth/BleScanner.kt | 71 ++++++++++++++----- .../darken/capod/main/core/GeneralSettings.kt | 6 ++ .../main/ui/overview/OverviewFragment.kt | 2 + .../darken/capod/monitor/core/PodMonitor.kt | 25 ++++--- .../res/drawable/ic_baseline_ghost_24.xml | 10 +++ app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/preferences_general.xml | 6 ++ 7 files changed, 92 insertions(+), 30 deletions(-) create mode 100644 app/src/main/res/drawable/ic_baseline_ghost_24.xml diff --git a/app/src/main/java/eu/darken/capod/common/bluetooth/BleScanner.kt b/app/src/main/java/eu/darken/capod/common/bluetooth/BleScanner.kt index c4b8b9a6..bcd9e2d0 100644 --- a/app/src/main/java/eu/darken/capod/common/bluetooth/BleScanner.kt +++ b/app/src/main/java/eu/darken/capod/common/bluetooth/BleScanner.kt @@ -31,8 +31,20 @@ class BleScanner @Inject constructor( @SuppressLint("MissingPermission") fun scan( filters: Set, scannerMode: ScannerMode, + compatMode: Boolean, ): Flow> = callbackFlow { + if (compatMode) log(TAG, WARN) { "Using compatibilityMode!" } + val adapter = bluetoothManager.adapter + + val supportsOffloadFiltering = adapter.isOffloadedFilteringSupported.also { + log(TAG, if (it) DEBUG else WARN) { "isOffloadedFilteringSupported=$it" } + } && !compatMode + + val supportsOffloadBatching = adapter.isOffloadedScanBatchingSupported.also { + log(TAG, if (it) DEBUG else WARN) { "isOffloadedScanBatchingSupported=$it" } + } && !compatMode + val scanner = bluetoothManager.scanner val callback = object : ScanCallback() { @@ -43,7 +55,13 @@ class BleScanner @Inject constructor( lastScanAt = System.currentTimeMillis() "onScanResult(delay=${delay}ms, callbackType=$callbackType, result=$result)" } - trySend(listOf(BleScanResult.fromScanResult(result))) + val toSend = if (supportsOffloadFiltering || filters.isEmpty() || filters.any { it.matches(result) }) { + listOf(BleScanResult.fromScanResult(result)) + } else { + log(TAG, VERBOSE) { "Manual filtering: No match for $result" } + emptyList() + } + trySend(toSend) } override fun onBatchScanResults(results: MutableList) { @@ -52,7 +70,19 @@ class BleScanner @Inject constructor( lastScanAt = System.currentTimeMillis() "onBatchScanResults(delay=${delay}ms, results=$results)" } - trySend(results.map { BleScanResult.fromScanResult(it) }) + + val toSend = results + .filter { result -> + val passed = when { + supportsOffloadFiltering -> true + filters.isEmpty() -> true + else -> filters.any { it.matches(result) } + } + if (!passed) log(TAG, VERBOSE) { "Manually filtered $result" } + passed + } + .map { BleScanResult.fromScanResult(it) } + trySend(toSend) } override fun onScanFailed(errorCode: Int) { @@ -60,6 +90,7 @@ class BleScanner @Inject constructor( } } + val settings = ScanSettings.Builder().apply { setScanMode( when (scannerMode) { @@ -68,20 +99,24 @@ class BleScanner @Inject constructor( ScannerMode.LOW_LATENCY -> ScanSettings.SCAN_MODE_LOW_LATENCY } ) - if (adapter.isOffloadedScanBatchingSupported) { - when (scannerMode) { - ScannerMode.LOW_POWER -> setReportDelay(2000) - ScannerMode.BALANCED -> setReportDelay(1000) - ScannerMode.LOW_LATENCY -> setReportDelay(500) - } - } else { - log(TAG, WARN) { "isOffloadedScanBatchingSupported=false" } + if (supportsOffloadBatching) { + setReportDelay( + when (scannerMode) { + ScannerMode.LOW_POWER -> 2000L + ScannerMode.BALANCED -> 1000L + ScannerMode.LOW_LATENCY -> 500L + } + ) } }.build() + log(TAG, VERBOSE) { "Settings created for offloaded filtering: $settings" } + val flushJob = launch { + log(TAG) { "Flush job launched" } while (isActive) { // Can undercut the minimum setReportDelay(), e.g. 5000ms on a Pixel5@12 + log(TAG, VERBOSE) { "Flushing scan results." } adapter.bluetoothLeScanner.flushPendingScanResults(callback) when (scannerMode) { ScannerMode.LOW_POWER -> break @@ -91,19 +126,17 @@ class BleScanner @Inject constructor( } } - if (adapter.isOffloadedFilteringSupported) { - scanner.startScan(filters.toList(), settings, callback) - log(TAG, VERBOSE) { "BleScanner started (filters=$filters, settings=$settings)" } - } else { - log(TAG, WARN) { "isOffloadedFilteringSupported=false" } - scanner.startScan(callback) - log(TAG, VERBOSE) { "BleScanner started" } - } + scanner.startScan( + if (supportsOffloadFiltering) filters.toList() else listOf(ScanFilter.Builder().build()), + settings, + callback + ) + log(TAG) { "BleScanner started (filters=$filters, settings=$settings)" } awaitClose { flushJob.cancel() scanner.stopScan(callback) - log(TAG, INFO) { "BleScanner stopped" } + log(TAG) { "BleScanner stopped" } } } .map { fakeBleData.maybeAddfakeData(it) } diff --git a/app/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt b/app/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt index c286f75a..43a78d8d 100644 --- a/app/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt +++ b/app/src/main/java/eu/darken/capod/main/core/GeneralSettings.kt @@ -34,6 +34,11 @@ class GeneralSettings @Inject constructor( moshi ) + val compatibilityMode = preferences.createFlowPreference( + "core.compatibility.enabled", + false + ) + val showAll = preferences.createFlowPreference( "core.showall.enabled", false @@ -58,6 +63,7 @@ class GeneralSettings @Inject constructor( override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper( monitorMode, scannerMode, + compatibilityMode, showAll, minimumSignalQuality, mainDeviceAddress, diff --git a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt index fa128bcb..0f699947 100644 --- a/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt +++ b/app/src/main/java/eu/darken/capod/main/ui/overview/OverviewFragment.kt @@ -10,6 +10,7 @@ import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.viewModels import dagger.hilt.android.AndroidEntryPoint +import eu.darken.capod.BuildConfig import eu.darken.capod.R import eu.darken.capod.common.colorString import eu.darken.capod.common.debug.logging.log @@ -71,6 +72,7 @@ class OverviewFragment : Fragment3(R.layout.main_fragment) { } vm.listItems.observe2(ui) { + if (BuildConfig.DEBUG) toolbar.subtitle = "${it.size} items" adapter.update(it) } diff --git a/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt b/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt index 08a21357..1beb04e0 100644 --- a/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt +++ b/app/src/main/java/eu/darken/capod/monitor/core/PodMonitor.kt @@ -61,19 +61,22 @@ class PodMonitor @Inject constructor( log(TAG) { "Bluetooth is enabled" } combine( generalSettings.scannerMode.flow, + generalSettings.compatibilityMode.flow, debugSettings.showUnfiltered.flow - ) { mode, unfiltered -> mode to unfiltered } - .flatMapLatest { (mode, unfiltered) -> - val filters = if (unfiltered) { - setOf(getUnfilteredFilter()) - } else { - ProximityPairing.getBleScanFilter() - } - bleScanner.scan( - filters = filters, - scannerMode = mode - ).map { it.preFilterAndMap(mode) } + ) { scannerMode, compatMode, unfiltered -> + Triple(scannerMode, compatMode, unfiltered) + }.flatMapLatest { (mode, compat, unfiltered) -> + val filters = if (unfiltered) { + setOf(getUnfilteredFilter()) + } else { + ProximityPairing.getBleScanFilter() } + bleScanner.scan( + filters = filters, + scannerMode = mode, + compatMode = compat, + ).map { it.preFilterAndMap(mode) } + } } else { log(TAG, WARN) { "Bluetooth is currently disabled" } flowOf(null) diff --git a/app/src/main/res/drawable/ic_baseline_ghost_24.xml b/app/src/main/res/drawable/ic_baseline_ghost_24.xml new file mode 100644 index 00000000..a9fe9319 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_ghost_24.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ab704d0a..e51ee002 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -142,4 +142,6 @@ Unfiltered BLE data Remove any filters from the BLE scanner to show all broadcasted BLE data. Useful to add support for new headphone types. The following permission is required: + Compatibility mode + Disable optimizations to improve compatibility. Try this if you are not seeing any data. \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_general.xml b/app/src/main/res/xml/preferences_general.xml index 340e8af3..5c85b3a4 100644 --- a/app/src/main/res/xml/preferences_general.xml +++ b/app/src/main/res/xml/preferences_general.xml @@ -14,6 +14,12 @@ android:summary="@string/settings_scanner_mode_description" android:title="@string/settings_scanner_mode_label" /> + +