From 7b41f83a5341df24256d272c1cc9ff66e08c0cb9 Mon Sep 17 00:00:00 2001 From: darken Date: Sun, 7 Sep 2025 16:26:34 +0200 Subject: [PATCH] Refactor device profiles and settings migration This commit introduces a new device profile system, moving away from a single "main device" configuration in `GeneralSettings`. Key changes: - `DeviceProfilesRepo` now manages a list of `DeviceProfile` objects, allowing for multiple device configurations. - On first launch with this update, existing "main device" settings from `GeneralSettings` (address, model, keys, signal quality) are migrated into a new default `AppleDeviceProfile`. - The `MonitorWorker`, `PopUpReaction`, `AutoConnect`, and `PodHistoryRepo` have been updated to use the new `DeviceProfilesRepo` instead of the old `GeneralSettings` for device-specific information. - `AppleFactory` now uses `DeviceProfilesRepo` to find matching profiles based on IRK. - `TroubleShooterFragmentVM` now interacts with `DeviceProfilesRepo` for profile management during troubleshooting. - The `create` method in `ApplePodsFactory` and its implementations are now `suspend` functions to allow for asynchronous operations like fetching profiles from the repository. - Old "main device" preference keys in `GeneralSettings` have been renamed with an "old" prefix and will be removed in a future update. - A new `currentProfiles()` extension function provides a convenient way to get the current list of profiles. --- .../darken/capod/main/core/GeneralSettings.kt | 17 ++--- .../darken/capod/monitor/core/PodMonitor.kt | 8 +-- .../monitor/core/worker/MonitorWorker.kt | 25 +++++--- .../capod/pods/core/apple/AppleFactory.kt | 4 +- .../capod/pods/core/apple/ApplePodsFactory.kt | 3 +- .../pods/core/apple/airpods/AirPodsGen1.kt | 2 +- .../pods/core/apple/airpods/AirPodsGen2.kt | 2 +- .../pods/core/apple/airpods/AirPodsGen3.kt | 2 +- .../pods/core/apple/airpods/AirPodsGen4.kt | 2 +- .../pods/core/apple/airpods/AirPodsGen4Anc.kt | 2 +- .../pods/core/apple/airpods/AirPodsMax.kt | 2 +- .../pods/core/apple/airpods/AirPodsMaxUsbc.kt | 2 +- .../pods/core/apple/airpods/AirPodsPro.kt | 2 +- .../pods/core/apple/airpods/AirPodsPro2.kt | 2 +- .../core/apple/airpods/AirPodsPro2Usbc.kt | 2 +- .../pods/core/apple/beats/BeatsFitPro.kt | 2 +- .../capod/pods/core/apple/beats/BeatsFlex.kt | 2 +- .../capod/pods/core/apple/beats/BeatsSolo3.kt | 2 +- .../pods/core/apple/beats/BeatsStudio3.kt | 2 +- .../capod/pods/core/apple/beats/BeatsX.kt | 2 +- .../pods/core/apple/beats/PowerBeats3.kt | 2 +- .../pods/core/apple/beats/PowerBeats4.kt | 2 +- .../pods/core/apple/beats/PowerBeatsPro.kt | 2 +- .../pods/core/apple/beats/PowerBeatsPro2.kt | 2 +- .../pods/core/apple/history/PodHistoryRepo.kt | 22 ++++--- .../pods/core/apple/misc/FakeAirPodsGen1.kt | 2 +- .../pods/core/apple/misc/FakeAirPodsGen2.kt | 2 +- .../pods/core/apple/misc/FakeAirPodsGen3.kt | 2 +- .../pods/core/apple/misc/FakeAirPodsPro.kt | 2 +- .../pods/core/apple/misc/FakeAirPodsPro2.kt | 2 +- .../core/apple/misc/UnknownAppleDevice.kt | 2 +- .../profiles/core/DeviceProfilesExtensions.kt | 5 ++ .../capod/profiles/core/DeviceProfilesRepo.kt | 62 +++++++++++++++++-- .../profiles/core/DeviceProfilesSettings.kt | 5 +- .../DeviceProfileCreationFragmentVM.kt | 9 +-- .../reaction/core/autoconnect/AutoConnect.kt | 6 +- .../reaction/core/popup/PopUpReaction.kt | 14 ++--- .../ui/TroubleShooterFragmentVM.kt | 37 +++++++---- 38 files changed, 174 insertions(+), 93 deletions(-) create mode 100644 app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesExtensions.kt 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 0f837b18..51d0b152 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 @@ -35,16 +35,19 @@ class GeneralSettings @Inject constructor( val scannerMode = preferences.createFlowPreference("core.scanner.mode", ScannerMode.BALANCED, moshi) - // TODO migrate these to new profiles - val minimumSignalQuality = preferences.createFlowPreference("core.signal.minimum", 0.20f) - val mainDeviceAddress = preferences.createFlowPreference("core.maindevice.address", null) - val mainDeviceModel = preferences.createFlowPreference("core.maindevice.model", PodDevice.Model.UNKNOWN, moshi) - val mainDeviceIdentityKey = preferences.createFlowPreference( + val oldMinimumSignalQuality = preferences.createFlowPreference("core.signal.minimum", 0.20f) + + val oldMainDeviceAddress = preferences.createFlowPreference("core.maindevice.address", null) + + val oldMainDeviceModel = preferences.createFlowPreference("core.maindevice.model", PodDevice.Model.UNKNOWN, moshi) + + val oldMainDeviceIdentityKey = preferences.createFlowPreference( "core.maindevice.identitykey", null, moshi ) - val mainDeviceEncryptionKey = preferences.createFlowPreference( + + val oldMainDeviceEncryptionKey = preferences.createFlowPreference( "core.maindevice.encryptionkey", null, moshi @@ -64,8 +67,6 @@ class GeneralSettings @Inject constructor( useExtraMonitorNotification, keepConnectedNotificationAfterDisconnect, scannerMode, - minimumSignalQuality, - mainDeviceAddress, isOffloadedFilteringDisabled, isOffloadedBatchingDisabled, useIndirectScanResultCallback, 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 d78aa9c0..b661583e 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 @@ -14,17 +14,17 @@ import eu.darken.capod.common.debug.logging.log import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.common.flow.replayingShare import eu.darken.capod.common.flow.throttleLatest -import eu.darken.capod.profiles.core.DeviceProfilesRepo import eu.darken.capod.main.core.GeneralSettings import eu.darken.capod.main.core.PermissionTool import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.PodFactory import eu.darken.capod.pods.core.apple.protocol.ProximityPairing +import eu.darken.capod.profiles.core.DeviceProfilesRepo +import eu.darken.capod.profiles.core.currentProfiles import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf @@ -85,7 +85,7 @@ class PodMonitor @Inject constructor( private suspend fun sortPodsToInterest(devices: Collection): List { val now = Instant.now() - val profiles = profilesRepo.profiles.firstOrNull() ?: emptyList() + val profiles = profilesRepo.currentProfiles() ?: emptyList() return devices.sortedWith( compareBy { device -> @@ -185,7 +185,7 @@ class PodMonitor @Inject constructor( val currentMain = devices.firstOrNull()?.firstOrNull() log(TAG) { "Live mainDevice is $currentMain" } - return currentMain ?: profilesRepo.profiles.first().firstOrNull() + return currentMain ?: profilesRepo.currentProfiles().firstOrNull() ?.let { podDeviceCache.load(it.id) } ?.let { podFactory.createPod(it)?.device } .also { log(TAG) { "Cached mainDevice is $it" } } diff --git a/app/src/main/java/eu/darken/capod/monitor/core/worker/MonitorWorker.kt b/app/src/main/java/eu/darken/capod/monitor/core/worker/MonitorWorker.kt index 3295d512..448f351a 100644 --- a/app/src/main/java/eu/darken/capod/monitor/core/worker/MonitorWorker.kt +++ b/app/src/main/java/eu/darken/capod/monitor/core/worker/MonitorWorker.kt @@ -8,7 +8,6 @@ import androidx.work.ForegroundInfo import androidx.work.WorkerParameters import dagger.assisted.Assisted import dagger.assisted.AssistedInject -import eu.darken.capod.common.bluetooth.BluetoothAddress import eu.darken.capod.common.bluetooth.BluetoothDevice2 import eu.darken.capod.common.bluetooth.BluetoothManager2 import eu.darken.capod.common.coroutine.DispatcherProvider @@ -27,6 +26,8 @@ import eu.darken.capod.monitor.core.MonitorCoroutineScope import eu.darken.capod.monitor.core.PodMonitor import eu.darken.capod.monitor.core.primaryDevice import eu.darken.capod.monitor.ui.MonitorNotifications +import eu.darken.capod.profiles.core.DeviceProfile +import eu.darken.capod.profiles.core.DeviceProfilesRepo import eu.darken.capod.reaction.core.autoconnect.AutoConnect import eu.darken.capod.reaction.core.playpause.PlayPause import eu.darken.capod.reaction.core.popup.PopUpReaction @@ -62,6 +63,7 @@ class MonitorWorker @AssistedInject constructor( private val autoConnect: AutoConnect, private val popUpReaction: PopUpReaction, private val popUpWindow: PopUpWindow, + private val profilesRepo: DeviceProfilesRepo, ) : CoroutineWorker(context, params) { private val workerScope = MonitorCoroutineScope() @@ -145,22 +147,29 @@ class MonitorWorker @AssistedInject constructor( } else { combine( generalSettings.monitorMode.flow, - generalSettings.mainDeviceAddress.flow, + profilesRepo.profiles, bluetoothManager.connectedDevices(), - ) { monitorMode, mainAddress, connectedDevices -> - listOf(monitorMode, mainAddress, connectedDevices) + ) { monitorMode, profiles, connectedDevices -> + listOf(monitorMode, profiles, connectedDevices) } } } .setupCommonEventHandlers(TAG) { "MonitorMode" } .flatMapLatest { arguments -> val monitorMode = arguments[0] as MonitorMode - val mainAddress = arguments[1] as BluetoothAddress? + @Suppress("UNCHECKED_CAST") + val profiles = arguments[1] as List @Suppress("UNCHECKED_CAST") val devices = arguments[2] as Collection + + val connectedAddresses = devices.map { it.address }.toSet() + val knownAddresses = profiles.mapNotNull { it.address }.toSet() log(TAG) { "Monitor mode: $monitorMode" } + log(TAG) { "connectedAddresses: $connectedAddresses" } + log(TAG) { "knownAddresses: $knownAddresses" } + when (monitorMode) { MonitorMode.MANUAL -> flow { // Cancel worker, ui scans manually @@ -170,12 +179,12 @@ class MonitorWorker @AssistedInject constructor( MonitorMode.ALWAYS -> emptyFlow() MonitorMode.AUTOMATIC -> flow { when { - mainAddress == null && devices.isNotEmpty() -> { + profiles.isEmpty() && devices.isNotEmpty() -> { log(TAG, WARN) { "Main device address not set, staying alive while any is connected" } } - devices.any { it.address == mainAddress } -> { - log(TAG) { "Main device is connected ($mainAddress), aborting any timeout." } + knownAddresses.any { it in connectedAddresses } -> { + log(TAG) { "A device is connected, aborting any timeout." } } else -> { diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt index d3ec4af2..0a0677e1 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/AppleFactory.kt @@ -15,7 +15,7 @@ import eu.darken.capod.pods.core.apple.protocol.ProximityPayload import eu.darken.capod.pods.core.apple.protocol.RPAChecker import eu.darken.capod.profiles.core.AppleDeviceProfile import eu.darken.capod.profiles.core.DeviceProfilesRepo -import kotlinx.coroutines.flow.first +import eu.darken.capod.profiles.core.currentProfiles import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import javax.inject.Inject @@ -63,7 +63,7 @@ class AppleFactory @Inject constructor( val proximityMessage = getMessage(scanResult) ?: return@withLock null val factory = podFactories.firstOrNull { it.isResponsible(proximityMessage) } ?: unknownAppleFactory - val profiles = profilesRepo.profiles.first().filterIsInstance() + val profiles = profilesRepo.currentProfiles().filterIsInstance() var profile = profiles.firstOrNull { it.identityKey != null && rpaChecker.verify(scanResult.address, it.identityKey) } diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt index 6cc4befa..d0d82713 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/ApplePodsFactory.kt @@ -1,14 +1,13 @@ package eu.darken.capod.pods.core.apple import eu.darken.capod.common.bluetooth.BleScanResult -import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.history.KnownDevice import eu.darken.capod.pods.core.apple.protocol.ProximityMessage import eu.darken.capod.pods.core.apple.protocol.ProximityPayload interface ApplePodsFactory { fun isResponsible(message: ProximityMessage): Boolean - fun create(scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta): ApplePods + suspend fun create(scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta): ApplePods data class ModelInfo( val full: UShort, diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt index 7a9d4625..af37de14 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen1.kt @@ -47,7 +47,7 @@ data class AirPodsGen1( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt index 41ce1d04..d148a19c 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen2.kt @@ -47,7 +47,7 @@ data class AirPodsGen2( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt index b4174fc2..b86e1e8e 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen3.kt @@ -47,7 +47,7 @@ data class AirPodsGen3( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4.kt index 6a69ce1a..f3bf75e5 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4.kt @@ -47,7 +47,7 @@ data class AirPodsGen4( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4Anc.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4Anc.kt index 51dedf3d..1c84bef0 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4Anc.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsGen4Anc.kt @@ -45,7 +45,7 @@ data class AirPodsGen4Anc( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt index cf52b71c..cb62ada8 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMax.kt @@ -52,7 +52,7 @@ data class AirPodsMax( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMaxUsbc.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMaxUsbc.kt index 7420d97e..2a0a65c9 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMaxUsbc.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsMaxUsbc.kt @@ -52,7 +52,7 @@ data class AirPodsMaxUsbc( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt index 6cc95eb2..964c512c 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro.kt @@ -64,7 +64,7 @@ data class AirPodsPro( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2.kt index 90ddb7c0..947d3eab 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2.kt @@ -64,7 +64,7 @@ data class AirPodsPro2( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2Usbc.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2Usbc.kt index 1882251d..c74d5f8d 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2Usbc.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/airpods/AirPodsPro2Usbc.kt @@ -64,7 +64,7 @@ data class AirPodsPro2Usbc( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFitPro.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFitPro.kt index 11b756e8..5de224b1 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFitPro.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFitPro.kt @@ -45,7 +45,7 @@ data class BeatsFitPro( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt index 3273df38..81c14f38 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsFlex.kt @@ -37,7 +37,7 @@ data class BeatsFlex( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt index eb8aa872..26c16c62 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsSolo3.kt @@ -36,7 +36,7 @@ data class BeatsSolo3( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt index fb8b94f4..4c90b932 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsStudio3.kt @@ -34,7 +34,7 @@ data class BeatsStudio3( getModelInfo().dirty == DEVICE_CODE_DIRTY && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt index 56648ca5..45aaf4ff 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/BeatsX.kt @@ -37,7 +37,7 @@ data class BeatsX( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt index 48473e1a..be92f189 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats3.kt @@ -37,7 +37,7 @@ data class PowerBeats3( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats4.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats4.kt index 918fd893..01676d7f 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats4.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeats4.kt @@ -38,7 +38,7 @@ data class PowerBeats4( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt index b049ba6d..f1445e0a 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro.kt @@ -45,7 +45,7 @@ data class PowerBeatsPro( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro2.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro2.kt index 891ce677..e48d48cc 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro2.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/beats/PowerBeatsPro2.kt @@ -45,7 +45,7 @@ data class PowerBeatsPro2( getModelInfo().full == DEVICE_CODE && length == ProximityPairing.PAIRING_MESSAGE_LENGTH } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/history/PodHistoryRepo.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/history/PodHistoryRepo.kt index 7a806f49..d09985c4 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/history/PodHistoryRepo.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/history/PodHistoryRepo.kt @@ -7,20 +7,22 @@ import eu.darken.capod.common.debug.logging.log import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.common.lowerNibble import eu.darken.capod.common.upperNibble -import eu.darken.capod.main.core.GeneralSettings import eu.darken.capod.pods.core.HasCase import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.ApplePods import eu.darken.capod.pods.core.apple.DualApplePods import eu.darken.capod.pods.core.apple.protocol.ProximityPayload import eu.darken.capod.pods.core.apple.protocol.RPAChecker +import eu.darken.capod.profiles.core.AppleDeviceProfile +import eu.darken.capod.profiles.core.DeviceProfilesRepo +import eu.darken.capod.profiles.core.currentProfiles import java.time.Duration import javax.inject.Inject import javax.inject.Singleton @Singleton class PodHistoryRepo @Inject constructor( - private val generalSettings: GeneralSettings, + private val profilesRepo: DeviceProfilesRepo, private val rpaChecker: RPAChecker, ) { @@ -66,7 +68,7 @@ class PodHistoryRepo @Inject constructor( val model: PodDevice.Model, ) - fun search(current: ApplePods): KnownDevice? { + suspend fun search(current: ApplePods): KnownDevice? { val basicResult = baseSearch(current) val caseIgnored = if (current is DualApplePods) { @@ -97,7 +99,7 @@ class PodHistoryRepo @Inject constructor( } } - private fun baseSearch(current: ApplePods): KnownDevice? { + private suspend fun baseSearch(current: ApplePods): KnownDevice? { val scanResult = current.scanResult val payload = current.payload @@ -120,14 +122,14 @@ class PodHistoryRepo @Inject constructor( ?.also { log(TAG, VERBOSE) { "search1: Recovered previous ID via address: $it" } } if (recognizedDevice == null) { - generalSettings.mainDeviceIdentityKey.value?.let { key -> - if (!rpaChecker.verify(current.address, key)) { - log(TAG, VERBOSE) { "search1: Current device does not match IRK" } - return@let - } + val profile = profilesRepo.currentProfiles() + .filterIsInstance() + .filter { it.identityKey != null } + .firstOrNull { rpaChecker.verify(current.address, it.identityKey!!) } + if (profile != null) { recognizedDevice = knownDevices.values - .firstOrNull { rpaChecker.verify(it.lastAddress, key) } + .firstOrNull { rpaChecker.verify(it.lastAddress, profile.identityKey!!) } .also { log(TAG, VERBOSE) { "search1: Recovered previous ID via IRK: $it" } } } } diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen1.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen1.kt index 06fd7ead..d1dc24fa 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen1.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen1.kt @@ -49,7 +49,7 @@ data class FakeAirPodsGen1( getModelInfo().full == DEVICE_CODE && length == 19 } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen2.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen2.kt index 2143082d..0489ca9c 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen2.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen2.kt @@ -47,7 +47,7 @@ data class FakeAirPodsGen2( getModelInfo().full == DEVICE_CODE && length == 19 } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen3.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen3.kt index dede2d32..4128118a 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen3.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsGen3.kt @@ -49,7 +49,7 @@ data class FakeAirPodsGen3( getModelInfo().full == DEVICE_CODE && length == 19 } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro.kt index f7166389..b93c4f91 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro.kt @@ -67,7 +67,7 @@ data class FakeAirPodsPro( getModelInfo().full == DEVICE_CODE && length == 19 } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro2.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro2.kt index 5b7b4835..e18ed853 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro2.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/FakeAirPodsPro2.kt @@ -71,7 +71,7 @@ data class FakeAirPodsPro2( getModelInfo().full == DEVICE_CODE && length == 19 } - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta diff --git a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/UnknownAppleDevice.kt b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/UnknownAppleDevice.kt index 956d87c6..323a3654 100644 --- a/app/src/main/java/eu/darken/capod/pods/core/apple/misc/UnknownAppleDevice.kt +++ b/app/src/main/java/eu/darken/capod/pods/core/apple/misc/UnknownAppleDevice.kt @@ -37,7 +37,7 @@ data class UnknownAppleDevice( ) : ApplePodsFactory { override fun isResponsible(message: ProximityMessage): Boolean = true - override fun create( + override suspend fun create( scanResult: BleScanResult, payload: ProximityPayload, meta: ApplePods.AppleMeta, diff --git a/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesExtensions.kt b/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesExtensions.kt new file mode 100644 index 00000000..7145a6ae --- /dev/null +++ b/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesExtensions.kt @@ -0,0 +1,5 @@ +package eu.darken.capod.profiles.core + +import kotlinx.coroutines.flow.first + +suspend fun DeviceProfilesRepo.currentProfiles(): List = profiles.first() \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesRepo.kt b/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesRepo.kt index 818cd634..52d6f606 100644 --- a/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesRepo.kt +++ b/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesRepo.kt @@ -1,44 +1,94 @@ package eu.darken.capod.profiles.core +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.capod.R +import eu.darken.capod.common.coroutine.AppScope import eu.darken.capod.common.debug.logging.Logging.Priority.VERBOSE import eu.darken.capod.common.debug.logging.log +import eu.darken.capod.common.debug.logging.logTag +import eu.darken.capod.main.core.GeneralSettings +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import javax.inject.Inject import javax.inject.Singleton @Singleton class DeviceProfilesRepo @Inject constructor( + @AppScope private val scope: CoroutineScope, + @ApplicationContext private val context: Context, + private val generalSettings: GeneralSettings, private val settings: DeviceProfilesSettings, ) { + private val mutex = Mutex() + + init { + scope.launch { + if (settings.defaultProfileCreated.value) return@launch + + + log(TAG) { "Creating default profile" } + var defaultProfile = AppleDeviceProfile( + label = context.getString(R.string.profiles_name_default), + ) + if (!settings.singleToMultiMigrationDone.value) { + log(TAG) { "Migrating settings default profile" } + defaultProfile = defaultProfile.copy( + minimumSignalQuality = generalSettings.oldMinimumSignalQuality.value, + model = generalSettings.oldMainDeviceModel.value, + address = generalSettings.oldMainDeviceAddress.value, + identityKey = generalSettings.oldMainDeviceIdentityKey.value, + encryptionKey = generalSettings.oldMainDeviceEncryptionKey.value, + ) + settings.singleToMultiMigrationDone.value = true + } + + log(TAG) { "Default profile: $defaultProfile" } + addProfile(defaultProfile) + settings.defaultProfileCreated.value = true + } + } + val profiles: Flow> = settings.profiles.flow.map { it.profiles } - fun addProfile(profile: DeviceProfile) { + suspend fun addProfile(profile: DeviceProfile) = mutex.withLock { val currentContainer = settings.profiles.value val updatedProfiles = currentContainer.profiles + profile settings.profiles.value = DeviceProfilesContainer(updatedProfiles) log(VERBOSE) { "Added device profile: ${profile.label}" } } - fun updateProfile(profile: DeviceProfile) { + suspend fun updateProfile(profile: DeviceProfile) = mutex.withLock { val currentContainer = settings.profiles.value - val updatedProfiles = currentContainer.profiles.map { - if (it.id == profile.id) profile else it + val updatedProfiles = currentContainer.profiles.map { + if (it.id == profile.id) profile else it } settings.profiles.value = DeviceProfilesContainer(updatedProfiles) log(VERBOSE) { "Updated device profile: ${profile.label}" } } - fun removeProfile(profileId: String) { + suspend fun removeProfile(profileId: String) = mutex.withLock { val currentContainer = settings.profiles.value val updatedProfiles = currentContainer.profiles.filter { it.id != profileId } settings.profiles.value = DeviceProfilesContainer(updatedProfiles) log(VERBOSE) { "Removed device profile with ID: $profileId" } } - fun reorderProfiles(profiles: List) { + suspend fun reorderProfiles(profiles: List) = mutex.withLock { settings.profiles.value = DeviceProfilesContainer(profiles.toList()) log(VERBOSE) { "Reordered ${profiles.size} device profiles" } } + + suspend fun clear() { + settings.profiles.value = DeviceProfilesContainer(emptyList()) + } + + companion object { + val TAG = logTag("Profiles", "Repo") + } } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesSettings.kt b/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesSettings.kt index 290655bb..5a6f174d 100644 --- a/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesSettings.kt +++ b/app/src/main/java/eu/darken/capod/profiles/core/DeviceProfilesSettings.kt @@ -4,7 +4,6 @@ import android.content.Context import android.content.SharedPreferences import androidx.preference.PreferenceDataStore import com.squareup.moshi.Moshi -import com.squareup.moshi.Types import dagger.hilt.android.qualifiers.ApplicationContext import eu.darken.capod.common.preferences.PreferenceStoreMapper import eu.darken.capod.common.preferences.Settings @@ -21,10 +20,12 @@ class DeviceProfilesSettings @Inject constructor( override val preferences: SharedPreferences = context.getSharedPreferences("device_profiles", Context.MODE_PRIVATE) val profiles = preferences.createFlowPreference( - "profiles", + "profiles.data", DeviceProfilesContainer(), moshi ) + val singleToMultiMigrationDone = preferences.createFlowPreference("profiles.migration.v2.done", false) + val defaultProfileCreated = preferences.createFlowPreference("profiles.default.v2.created", false) override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper() } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationFragmentVM.kt b/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationFragmentVM.kt index 3be1cf93..74402d13 100644 --- a/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationFragmentVM.kt +++ b/app/src/main/java/eu/darken/capod/profiles/ui/creation/DeviceProfileCreationFragmentVM.kt @@ -6,20 +6,21 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.asLiveData import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext +import eu.darken.capod.R import eu.darken.capod.common.bluetooth.BluetoothDevice2 import eu.darken.capod.common.bluetooth.BluetoothManager2 import eu.darken.capod.common.coroutine.DispatcherProvider import eu.darken.capod.common.debug.logging.log import eu.darken.capod.common.debug.logging.logTag -import eu.darken.capod.R import eu.darken.capod.common.uix.ViewModel3 -import eu.darken.capod.profiles.core.AppleDeviceProfile -import eu.darken.capod.profiles.core.DeviceProfilesRepo import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.apple.protocol.IdentityResolvingKey import eu.darken.capod.pods.core.apple.protocol.ProximityEncryptionKey +import eu.darken.capod.profiles.core.AppleDeviceProfile import eu.darken.capod.profiles.core.DeviceProfile +import eu.darken.capod.profiles.core.DeviceProfilesRepo import eu.darken.capod.profiles.core.ProfileId +import eu.darken.capod.profiles.core.currentProfiles import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine @@ -188,7 +189,7 @@ class DeviceProfileCreationFragmentVM @Inject constructor( private fun loadProfile(profileId: String) { launch { try { - val profiles = deviceProfilesRepo.profiles.first() + val profiles = deviceProfilesRepo.currentProfiles() val profile = profiles.find { it.id == profileId } if (profile != null) { // Set current values diff --git a/app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnect.kt b/app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnect.kt index 6a4ad5cc..d4585c45 100644 --- a/app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnect.kt +++ b/app/src/main/java/eu/darken/capod/reaction/core/autoconnect/AutoConnect.kt @@ -12,6 +12,7 @@ import eu.darken.capod.monitor.core.primaryDevice import eu.darken.capod.pods.core.HasEarDetection import eu.darken.capod.pods.core.HasEarDetectionDual import eu.darken.capod.pods.core.apple.DualApplePods +import eu.darken.capod.profiles.core.DeviceProfilesRepo import eu.darken.capod.reaction.core.ReactionSettings import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -30,7 +31,8 @@ class AutoConnect @Inject constructor( private val bluetoothManager: BluetoothManager2, private val podMonitor: PodMonitor, private val generalSettings: GeneralSettings, - private val reactionSettings: ReactionSettings + private val reactionSettings: ReactionSettings, + private val deviceProfilesRepo: DeviceProfilesRepo, ) { fun monitor(): Flow = reactionSettings.autoConnect.flow @@ -49,7 +51,7 @@ class AutoConnect @Inject constructor( .map { (connectedDevices, mainDevice) -> log(TAG, VERBOSE) { "mainPodDevice is $mainDevice" } - val mainDeviceAddr = generalSettings.mainDeviceAddress.value + val mainDeviceAddr = mainDevice.meta.profile?.address if (mainDeviceAddr.isNullOrEmpty()) { log(TAG, WARN) { "mainDeviceAddress is null" } return@map diff --git a/app/src/main/java/eu/darken/capod/reaction/core/popup/PopUpReaction.kt b/app/src/main/java/eu/darken/capod/reaction/core/popup/PopUpReaction.kt index 5f822d14..b4580360 100644 --- a/app/src/main/java/eu/darken/capod/reaction/core/popup/PopUpReaction.kt +++ b/app/src/main/java/eu/darken/capod/reaction/core/popup/PopUpReaction.kt @@ -9,7 +9,6 @@ import eu.darken.capod.common.debug.logging.log import eu.darken.capod.common.debug.logging.logTag import eu.darken.capod.common.flow.setupCommonEventHandlers import eu.darken.capod.common.flow.withPrevious -import eu.darken.capod.main.core.GeneralSettings import eu.darken.capod.monitor.core.PodMonitor import eu.darken.capod.monitor.core.primaryDevice import eu.darken.capod.pods.core.PodDevice @@ -32,7 +31,6 @@ import javax.inject.Singleton class PopUpReaction @Inject constructor( private val podMonitor: PodMonitor, private val reactionSettings: ReactionSettings, - private val generalSettings: GeneralSettings, private val bluetoothManager: BluetoothManager2, ) { @@ -118,17 +116,17 @@ class PopUpReaction @Inject constructor( if (!isEnabled) return@flatMapLatest emptyFlow() combine( - generalSettings.mainDeviceAddress.flow, bluetoothManager.connectedDevices().distinctUntilChanged(), podMonitor.primaryDevice().distinctUntilChangedBy { it?.rawDataHex }, - ) { targetAddress, devices, broadcast -> - log(TAG) { "$targetAddress $broadcast $devices " } - val direct = devices.singleOrNull { it.address == targetAddress }.also { + ) { devices, broadcast -> + log(TAG) { "$broadcast $devices " } + val primaryAddr = broadcast?.meta?.profile?.address + val direct = devices.singleOrNull { it.address == primaryAddr }.also { log(TAG, VERBOSE) { "Connected main device is $it" } } if (direct == null) { - connectionCoolDowns.remove(targetAddress).also { - if (it != null) log(TAG) { "Cleared connection cooldown for $targetAddress due to disconect" } + connectionCoolDowns.remove(primaryAddr).also { + if (it != null) log(TAG) { "Cleared connection cooldown for $primaryAddr due to disconect" } } } if (direct != null && broadcast != null) direct to broadcast else null diff --git a/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterFragmentVM.kt b/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterFragmentVM.kt index 7a95c6c8..b7873833 100644 --- a/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterFragmentVM.kt +++ b/app/src/main/java/eu/darken/capod/troubleshooter/ui/TroubleShooterFragmentVM.kt @@ -13,7 +13,16 @@ import eu.darken.capod.monitor.core.PodMonitor import eu.darken.capod.monitor.core.primaryDevice import eu.darken.capod.pods.core.PodDevice import eu.darken.capod.pods.core.unknown.UnknownDevice -import kotlinx.coroutines.flow.* +import eu.darken.capod.profiles.core.AppleDeviceProfile +import eu.darken.capod.profiles.core.DeviceProfilesRepo +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.flow.takeWhile +import kotlinx.coroutines.flow.toList import kotlinx.coroutines.withTimeoutOrNull import javax.inject.Inject @@ -22,6 +31,7 @@ class TroubleShooterFragmentVM @Inject constructor( @Suppress("UNUSED_PARAMETER") handle: SavedStateHandle, private val dispatcherProvider: DispatcherProvider, private val generalSettings: GeneralSettings, + private val profilesRepo: DeviceProfilesRepo, private val podMonitor: PodMonitor, private val debugSettings: DebugSettings, ) : ViewModel3(dispatcherProvider = dispatcherProvider) { @@ -192,25 +202,28 @@ class TroubleShooterFragmentVM @Inject constructor( progress("Headphones found nearby, but not detected as yours. Resetting filters.\n") - generalSettings.mainDeviceModel.value = PodDevice.Model.UNKNOWN - generalSettings.mainDeviceAddress.value = null - generalSettings.minimumSignalQuality.value = 0.25f + profilesRepo.clear() progress("Setting headphone with strongest signal as yours.") - generalSettings.mainDeviceModel.value = otherDevices.maxBy { it.signalQuality }.model + profilesRepo.addProfile( + AppleDeviceProfile( + label = "Test", + model = otherDevices.maxBy { it.signalQuality }.model, + ) + ) val mainDevice = withTimeoutOrNull(STEP_TIME) { podMonitor.primaryDevice().filterNotNull().firstOrNull() } - if (mainDevice != null) { - generalSettings.mainDeviceModel.value = mainDevice.model - generalSettings.scannerMode.value = ScannerMode.BALANCED - success("Success! Detected your headphones.") - return@launch - } - failure("No headphones detected near your device.", BleState.Result.Failure.Type.HEADPHONES) + generalSettings.scannerMode.value = ScannerMode.BALANCED + + if (mainDevice != null) { + success("Success! Detected your headphones.") + } else { + failure("No headphones detected near your device.", BleState.Result.Failure.Type.HEADPHONES) + } } }