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

Commit 744b87a4 authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Android (Google) Code Review
Browse files

Merge changes Id4e6a2ab,I707a46b5 into main

* changes:
  [flexiglass] Clicks on shade empty space go back to lockscreen.
  [flexiglass] Fixes back/home navigation when occluded.
parents c43c53c1 7212fb02
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -261,6 +261,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
                shadeInteractor = kosmos.shadeInteractor,
                footerActionsController = kosmos.footerActionsController,
                footerActionsViewModelFactory = kosmos.footerActionsViewModelFactory,
                sceneInteractor = sceneInteractor,
            )

        kosmos.fakeDeviceEntryRepository.setUnlocked(false)
+44 −2
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepo
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.SysUiState
import com.android.systemui.model.sysUiState
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.PowerInteractorFactory
@@ -51,6 +51,7 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
@@ -78,6 +79,7 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -99,7 +101,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
    private val faceAuthRepository by lazy { kosmos.fakeDeviceEntryFaceAuthRepository }
    private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
    private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
    private val sysUiState: SysUiState = mock()
    private val sysUiState = spy(kosmos.sysUiState)
    private val falsingCollector: FalsingCollector = mock()
    private val powerInteractor = PowerInteractorFactory.create().powerInteractor
    private val fakeSceneDataSource = kosmos.fakeSceneDataSource
@@ -397,6 +399,46 @@ class SceneContainerStartableTest : SysuiTestCase() {
                }
        }

    @Test
    fun hydrateSystemUiState_onLockscreen_basedOnOcclusion() =
        testScope.runTest {
            prepareState(
                initialSceneKey = Scenes.Lockscreen,
            )
            underTest.start()
            runCurrent()
            clearInvocations(sysUiState)

            kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
                true,
                mock()
            )
            runCurrent()
            assertThat(
                    sysUiState.flags and
                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED != 0
                )
                .isTrue()
            assertThat(
                    sysUiState.flags and
                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0
                )
                .isFalse()

            kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(false)
            runCurrent()
            assertThat(
                    sysUiState.flags and
                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED != 0
                )
                .isFalse()
            assertThat(
                    sysUiState.flags and
                        QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0
                )
                .isTrue()
        }

    @Test
    fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNone() =
        testScope.runTest {
+3 −17
Original line number Diff line number Diff line
@@ -129,6 +129,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
                shadeInteractor = kosmos.shadeInteractor,
                footerActionsViewModelFactory = kosmos.footerActionsViewModelFactory,
                footerActionsController = kosmos.footerActionsController,
                sceneInteractor = kosmos.sceneInteractor,
            )
    }

@@ -215,22 +216,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
        }

    @Test
    fun onContentClicked_deviceUnlocked_switchesToGone() =
        testScope.runTest {
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pin
            )
            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
            runCurrent()

            underTest.onContentClicked()

            assertThat(currentScene).isEqualTo(Scenes.Gone)
        }

    @Test
    fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
    fun onContentClicked_deviceLockedSecurely_switchesToLockscreen() =
        testScope.runTest {
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
@@ -241,7 +227,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {

            underTest.onContentClicked()

            assertThat(currentScene).isEqualTo(Scenes.Bouncer)
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
        }

    @Test
+33 −9
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.model
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
@@ -27,6 +28,7 @@ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICA
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
import dagger.Lazy
import javax.inject.Inject

@@ -38,8 +40,10 @@ import javax.inject.Inject
class SceneContainerPlugin
@Inject
constructor(
    private val interactor: Lazy<SceneInteractor>,
    private val sceneInteractor: Lazy<SceneInteractor>,
    private val occlusionInteractor: Lazy<SceneContainerOcclusionInteractor>,
) {

    /**
     * Returns an override value for the given [flag] or `null` if the scene framework isn't enabled
     * or if the flag value doesn't need to be overridden.
@@ -49,10 +53,18 @@ constructor(
            return null
        }

        val transitionState = interactor.get().transitionState.value
        val transitionState = sceneInteractor.get().transitionState.value
        val idleTransitionStateOrNull = transitionState as? ObservableTransitionState.Idle
        val currentSceneOrNull = idleTransitionStateOrNull?.scene
        return currentSceneOrNull?.let { sceneKey -> EvaluatorByFlag[flag]?.invoke(sceneKey) }
        val invisibleDueToOcclusion = occlusionInteractor.get().invisibleDueToOcclusion.value
        return currentSceneOrNull?.let { sceneKey ->
            EvaluatorByFlag[flag]?.invoke(
                SceneContainerPluginState(
                    scene = sceneKey,
                    invisibleDueToOcclusion = invisibleDueToOcclusion,
                )
            )
        }
    }

    companion object {
@@ -67,12 +79,24 @@ constructor(
         * to be overridden by the scene framework.
         */
        val EvaluatorByFlag =
            mapOf<Int, (SceneKey) -> Boolean>(
                SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it != Scenes.Gone },
                SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to { it == Scenes.Shade },
                SYSUI_STATE_QUICK_SETTINGS_EXPANDED to { it == Scenes.QuickSettings },
                SYSUI_STATE_BOUNCER_SHOWING to { it == Scenes.Bouncer },
                SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to { it == Scenes.Lockscreen },
            mapOf<Int, (SceneContainerPluginState) -> Boolean>(
                SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it.scene != Scenes.Gone },
                SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to { it.scene == Scenes.Shade },
                SYSUI_STATE_QUICK_SETTINGS_EXPANDED to { it.scene == Scenes.QuickSettings },
                SYSUI_STATE_BOUNCER_SHOWING to { it.scene == Scenes.Bouncer },
                SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to
                    {
                        it.scene == Scenes.Lockscreen && !it.invisibleDueToOcclusion
                    },
                SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED to
                    {
                        it.scene == Scenes.Lockscreen && it.invisibleDueToOcclusion
                    },
            )
    }

    data class SceneContainerPluginState(
        val scene: SceneKey,
        val invisibleDueToOcclusion: Boolean,
    )
}
+59 −13
Original line number Diff line number Diff line
@@ -19,45 +19,91 @@ package com.android.systemui.scene.domain.interactor
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn

/** Encapsulates logic regarding the occlusion state of the scene container. */
@SysUISingleton
class SceneContainerOcclusionInteractor
@Inject
constructor(
    @Application applicationScope: CoroutineScope,
    keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
    sceneInteractor: SceneInteractor,
    keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
    /** Whether a show-when-locked activity is at the top of the current activity stack. */
    private val isOccludingActivityShown: StateFlow<Boolean> =
        keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop.stateIn(
            scope = applicationScope,
            started = SharingStarted.WhileSubscribed(),
            initialValue = false,
        )

    /**
     * Whether the scene container should become invisible due to "occlusion" by an in-foreground
     * "show when locked" activity.
     * Whether AOD is fully shown (not transitioning) or partially shown during a transition to/from
     * AOD.
     */
    val invisibleDueToOcclusion: Flow<Boolean> =
        combine(
                keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop,
                sceneInteractor.transitionState,
    private val isAodFullyOrPartiallyShown: StateFlow<Boolean> =
        keyguardTransitionInteractor
            .transitionValue(KeyguardState.AOD)
            .onStart { emit(0f) }
            .map { it > 0 }
                    .distinctUntilChanged(),
            .stateIn(
                scope = applicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = false,
            )

    /**
     * Whether the scene container should become invisible due to "occlusion" by an in-foreground
     * "show when locked" activity.
     */
    val invisibleDueToOcclusion: StateFlow<Boolean> =
        combine(
                isOccludingActivityShown,
                sceneInteractor.transitionState,
                isAodFullyOrPartiallyShown,
            ) { isOccludingActivityShown, sceneTransitionState, isAodFullyOrPartiallyShown ->
                isOccludingActivityShown &&
                invisibleDueToOcclusion(
                    isOccludingActivityShown = isOccludingActivityShown,
                    sceneTransitionState = sceneTransitionState,
                    isAodFullyOrPartiallyShown = isAodFullyOrPartiallyShown,
                )
            }
            .stateIn(
                scope = applicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue =
                    invisibleDueToOcclusion(
                        isOccludingActivityShown = isOccludingActivityShown.value,
                        sceneTransitionState = sceneInteractor.transitionState.value,
                        isAodFullyOrPartiallyShown = isAodFullyOrPartiallyShown.value,
                    ),
            )

    private fun invisibleDueToOcclusion(
        isOccludingActivityShown: Boolean,
        sceneTransitionState: ObservableTransitionState,
        isAodFullyOrPartiallyShown: Boolean,
    ): Boolean {
        return isOccludingActivityShown &&
            // Cannot be occluded in AOD.
            !isAodFullyOrPartiallyShown &&
            // Only some scenes can be occluded.
            sceneTransitionState.canBeOccluded
    }
            .distinctUntilChanged()

    private val ObservableTransitionState.canBeOccluded: Boolean
        get() =
Loading