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

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

Fix swipe-to-bouncer on GH with face auth

When swiping up the bouncer on glanceable hub, if the user has a large
number of notifications, the notification limit is taking effect
mid-swipe and causing the bouncer to hide itself. This is because the
bouncer/alternate bouncer states are considered "lockscreen" states,
even if the bouncer is shown over the glanceable hub instead of over the
lockscreen.

This change does not consider the bouncer states as "lockscreen" if they
are being shown over the glanceable hub.

Fixes: 396689194
Test: atest SharedNotificationContainerViewModelTest
Flag: com.android.systemui.glanceable_hub_v2
Change-Id: I8cfa5ff3074d7d34f06b7db5a1051e367e290889
parent 1be4df13
Loading
Loading
Loading
Loading
+39 −13
Original line number Original line Diff line number Diff line
@@ -17,14 +17,15 @@


package com.android.systemui.statusbar.notification.stack.ui.viewmodel
package com.android.systemui.statusbar.notification.stack.ui.viewmodel


import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.coroutines.collectValues
@@ -54,11 +55,14 @@ import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.res.R
import com.android.systemui.scene.data.repository.Idle
import com.android.systemui.scene.data.repository.Idle
import com.android.systemui.scene.data.repository.Transition
import com.android.systemui.scene.data.repository.Transition
import com.android.systemui.scene.data.repository.setTransition
import com.android.systemui.scene.data.repository.setTransition
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.shade.domain.interactor.enableSingleShade
import com.android.systemui.shade.domain.interactor.enableSingleShade
@@ -125,7 +129,6 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
        kosmos.sharedNotificationContainerInteractor
        kosmos.sharedNotificationContainerInteractor
    }
    }
    private val largeScreenHeaderHelper by lazy { kosmos.mockLargeScreenHeaderHelper }
    private val largeScreenHeaderHelper by lazy { kosmos.mockLargeScreenHeaderHelper }
    private val communalSceneRepository by lazy { kosmos.communalSceneRepository }


    lateinit var underTest: SharedNotificationContainerViewModel
    lateinit var underTest: SharedNotificationContainerViewModel


@@ -590,6 +593,25 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
            assertThat(isOnLockscreen).isTrue()
            assertThat(isOnLockscreen).isTrue()
        }
        }


    @Test
    @DisableSceneContainer
    @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
    fun isOnLockscreenFalseWhenCommunalShowing() =
        kosmos.runTest {
            val isOnLockscreen by collectLastValue(underTest.isOnLockscreen)

            setTransition(
                sceneTransition = Idle(Scenes.Bouncer),
                stateTransition = TransitionStep(from = LOCKSCREEN, to = PRIMARY_BOUNCER),
            )
            assertThat(isOnLockscreen).isTrue()

            testScope.showCommunalScene()

            // If bouncer is showing over the hub, it should not be considered on lockscreen
            assertThat(isOnLockscreen).isFalse()
        }

    @Test
    @Test
    fun isOnLockscreenWithoutShade() =
    fun isOnLockscreenWithoutShade() =
        testScope.runTest {
        testScope.runTest {
@@ -1472,20 +1494,24 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
    }
    }


    private fun TestScope.showCommunalScene() {
    private fun TestScope.showCommunalScene() {
        val transitionState =
        val targetScene =
            MutableStateFlow<ObservableTransitionState>(
            if (SceneContainerFlag.isEnabled) {
                ObservableTransitionState.Idle(CommunalScenes.Communal)
                Scenes.Communal
            )
            } else {
        communalSceneRepository.setTransitionState(transitionState)
                CommunalScenes.Communal
            }
        kosmos.communalSceneInteractor.changeScene(targetScene, "test")
        runCurrent()
        runCurrent()
    }
    }


    private fun TestScope.hideCommunalScene() {
    private fun TestScope.hideCommunalScene() {
        val transitionState =
        val targetScene =
            MutableStateFlow<ObservableTransitionState>(
            if (SceneContainerFlag.isEnabled) {
                ObservableTransitionState.Idle(CommunalScenes.Blank)
                Scenes.Lockscreen
            )
            } else {
        communalSceneRepository.setTransitionState(transitionState)
                CommunalScenes.Blank
            }
        kosmos.communalSceneInteractor.changeScene(targetScene, "test")
        runCurrent()
        runCurrent()
    }
    }


+35 −12
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import android.content.Context
import android.content.Context
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.coroutines.flow.flowName
import com.android.app.tracing.coroutines.flow.flowName
import com.android.systemui.Flags.glanceableHubV2
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -87,7 +88,9 @@ import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNoti
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.kotlin.sample
@@ -299,11 +302,8 @@ constructor(
            .distinctUntilChanged()
            .distinctUntilChanged()
            .dumpWhileCollecting("configurationBasedDimensions")
            .dumpWhileCollecting("configurationBasedDimensions")


    /** If the user is visually on one of the unoccluded lockscreen states. */
    private val isOnAnyBouncer: Flow<Boolean> =
    val isOnLockscreen: Flow<Boolean> =
        anyOf(
        anyOf(
                keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f },
                keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f },
            keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER).map { it > 0f },
            keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER).map { it > 0f },
            keyguardTransitionInteractor
            keyguardTransitionInteractor
                .transitionValue(
                .transitionValue(
@@ -311,8 +311,31 @@ constructor(
                    stateWithoutSceneContainer = PRIMARY_BOUNCER,
                    stateWithoutSceneContainer = PRIMARY_BOUNCER,
                )
                )
                .map { it > 0f },
                .map { it > 0f },
        )

    /** If the user is visually on one of the unoccluded lockscreen states. */
    val isOnLockscreen: Flow<Boolean> =
        if (glanceableHubV2()) {
                anyOf(
                    keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f },
                    keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f },
                    keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
                    keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
                    allOf(
                        // Exclude bouncer showing over communal hub, as this should not be
                        // considered
                        // "lockscreen"
                        not(communalSceneInteractor.isCommunalVisible),
                        isOnAnyBouncer,
                    ),
                )
                )
            } else {
                anyOf(
                    keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f },
                    keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f },
                    keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
                    isOnAnyBouncer,
                )
            }
            .flowName("isOnLockscreen")
            .flowName("isOnLockscreen")
            .stateIn(
            .stateIn(
                scope = applicationScope,
                scope = applicationScope,