From 250d0ab15405c92b294872aad59448d032ff6a8c Mon Sep 17 00:00:00 2001 From: darken Date: Fri, 17 Apr 2026 21:36:06 +0200 Subject: [PATCH] fix(monitor): Gate AAP lifecycle on BLUETOOTH_CONNECT permission --- .../monitor/core/aap/AapLifecycleManager.kt | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/eu/darken/capod/monitor/core/aap/AapLifecycleManager.kt b/app/src/main/java/eu/darken/capod/monitor/core/aap/AapLifecycleManager.kt index f399a506..3631288d 100644 --- a/app/src/main/java/eu/darken/capod/monitor/core/aap/AapLifecycleManager.kt +++ b/app/src/main/java/eu/darken/capod/monitor/core/aap/AapLifecycleManager.kt @@ -6,12 +6,20 @@ import eu.darken.capod.common.debug.logging.asLog 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.permissions.Permission +import eu.darken.capod.main.core.PermissionTool import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.retryWhen import javax.inject.Inject import javax.inject.Singleton +import kotlin.time.Duration.Companion.milliseconds /** * Launches AAP auto-connect and key persistence in [appScope], @@ -27,17 +35,33 @@ class AapLifecycleManager @Inject constructor( private val aapLearnedSettingsPersister: AapLearnedSettingsPersister, private val stemConfigSender: StemConfigSender, private val stemPressReaction: StemPressReaction, + private val permissionTool: PermissionTool, ) { fun start() { log(TAG) { "start()" } - merge( - aapAutoConnect.monitor(), - aapKeyPersister.monitor(), - aapLearnedSettingsPersister.monitor(), - stemConfigSender.monitor(), - stemPressReaction.monitor(), - ) - .catch { e -> log(TAG, WARN) { "AAP lifecycle error: ${e.asLog()}" } } + permissionTool.missingPermissions + .map { Permission.BLUETOOTH_CONNECT in it } + .distinctUntilChanged() + .flatMapLatest { missingConnect -> + if (missingConnect) { + log(TAG) { "AAP lifecycle paused: BLUETOOTH_CONNECT missing" } + emptyFlow() + } else { + log(TAG) { "AAP lifecycle active: BLUETOOTH_CONNECT granted" } + merge( + aapAutoConnect.monitor(), + aapKeyPersister.monitor(), + aapLearnedSettingsPersister.monitor(), + stemConfigSender.monitor(), + stemPressReaction.monitor(), + ) + .retryWhen { cause, attempt -> + log(TAG, WARN) { "AAP lifecycle error (attempt ${attempt + 1}): ${cause.asLog()}" } + delay((1_000L * (attempt + 1).coerceAtMost(5)).milliseconds) + true + } + } + } .setupCommonEventHandlers(TAG) { "aapActive" } .launchIn(appScope) }