Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b32df115 authored by Lucas Silva's avatar Lucas Silva
Browse files

Fix alarms launching while on the hub

This handles a race condition between the dreaming signal and the
occlusion signal, similar to FromDreamingTransitionInteractor.

Fixes: 359094748
Test: atest KeyguardTransitionScenariosTest
Test: starting alarm while idle on the hub
Flag: com.android.systemui.communal_scene_ktf_refactor
Change-Id: Iec6d86a26eee505a946334bb83022e31c48b2e89
parent ed4b4fd5
Loading
Loading
Loading
Loading
+57 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.flow.MutableStateFlow
@@ -2135,6 +2136,14 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest
    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
    fun glanceableHubToOccluded_communalKtfRefactor() =
        testScope.runTest {
            // GIVEN device is not dreaming
            powerInteractor.setAwakeForTest()
            keyguardRepository.setDreaming(false)
            keyguardRepository.setDozeTransitionModel(
                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
            )
            advanceTimeBy(600.milliseconds)

            // GIVEN a prior transition has run to GLANCEABLE_HUB
            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
            runCurrent()
@@ -2298,6 +2307,54 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest
            coroutineContext.cancelChildren()
        }

    @Test
    @BrokenWithSceneContainer(339465026)
    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
    fun glanceableHubToOccludedDoesNotTriggerWhenDreamStateChanges_communalKtfRefactor() =
        testScope.runTest {
            // GIVEN that we are dreaming and not dozing
            powerInteractor.setAwakeForTest()
            keyguardRepository.setDreaming(true)
            keyguardRepository.setKeyguardOccluded(true)
            keyguardRepository.setDozeTransitionModel(
                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
            )
            advanceTimeBy(700.milliseconds)
            clearInvocations(transitionRepository)

            // GIVEN a prior transition has run to GLANCEABLE_HUB
            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
            runCurrent()
            assertThat(transitionRepository)
                .startedTransition(
                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
                    from = KeyguardState.DREAMING,
                    to = KeyguardState.GLANCEABLE_HUB,
                )
            clearInvocations(transitionRepository)

            // WHEN dream ends but we are still occluded
            keyguardRepository.setDreaming(false)
            runCurrent()
            assertThat(transitionRepository).noTransitionsStarted()

            // Simulate occlusion signal changing due to dream terminating and then occluding again
            // due to a new activity starting a couple milliseconds later.
            keyguardRepository.setKeyguardOccluded(false)
            advanceTimeBy(10.milliseconds)
            keyguardRepository.setKeyguardOccluded(true)
            advanceTimeBy(200.milliseconds)

            assertThat(transitionRepository)
                .startedTransition(
                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
                    from = KeyguardState.GLANCEABLE_HUB,
                    to = KeyguardState.OCCLUDED,
                )

            coroutineContext.cancelChildren()
        }

    private suspend fun TestScope.runTransitionAndSetWakefulness(
        from: KeyguardState,
        to: KeyguardState
+23 −11
Original line number Diff line number Diff line
@@ -37,16 +37,21 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.noneOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

@OptIn(FlowPreview::class)
@SysUISingleton
class FromGlanceableHubTransitionInteractor
@Inject
@@ -196,19 +201,26 @@ constructor(
            }
        } else if (communalSceneKtfRefactor()) {
            scope.launch {
                allOf(
                combine(
                        keyguardInteractor.isKeyguardOccluded,
                        noneOf(
                            // Dream is a special-case of occluded, so filter out the dreaming
                            // case here.
                            keyguardInteractor.isDreaming,
                        keyguardInteractor.isAbleToDream
                            // Debounce the dreaming signal since there is a race condition between
                            // the occluded and dreaming signals. We therefore add a small delay
                            // to give enough time for occluded to flip to false when the dream
                            // ends, to avoid transitioning to OCCLUDED erroneously when exiting
                            // the dream.
                            .debounce(100.milliseconds),
                        ::Pair
                    )
                    .sampleFilter(
                        // When launching activities from widgets on the hub, we have a
                        // custom occlusion animation.
                        communalSceneInteractor.isLaunchingWidget,
                        ),
                    )
                    .filterRelevantKeyguardStateAnd { isOccludedAndNotDreamingNorLaunchingWidget ->
                        isOccludedAndNotDreamingNorLaunchingWidget
                    ) { launchingWidget ->
                        !launchingWidget
                    }
                    .filterRelevantKeyguardStateAnd { (isOccluded, isDreaming) ->
                        isOccluded && !isDreaming
                    }
                    .collect { _ ->
                        communalSceneInteractor.changeScene(