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

Commit b9217cc6 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Makes window focusable.

Added minimal logic to use NotificationShadeWindowController to keep the
focus flags of the window view up-to-date as scenes changes.

Fix: 316616773
Test: added unit test
Test: manually verified that we get to the RESUMED state in
SceneContainer's @Composable function, when we didn't before **for the
shade showing up on top of the unlocked device** (it was already doing
that for the locked device)
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT

Change-Id: I757cf558f32c06d6b783ea6ca4c07663d7e19c78
parent 8fa5ce38
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -265,7 +265,8 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
                powerInteractor = powerInteractor,
                bouncerInteractor = bouncerInteractor,
                simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
                authenticationInteractor = dagger.Lazy { utils.authenticationInteractor() }
                authenticationInteractor = dagger.Lazy { utils.authenticationInteractor() },
                windowController = mock(),
            )
        startable.start()

+83 −16
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -46,18 +47,24 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@SmallTest
@RunWith(AndroidJUnit4::class)
@EnableFlags(AconfigFlags.FLAG_SCENE_CONTAINER)
class SceneContainerStartableTest : SysuiTestCase() {

    @Mock private lateinit var windowController: NotificationShadeWindowController

    private val utils = SceneTestUtils(this)
    private val testScope = utils.testScope
    private val sceneInteractor = utils.sceneInteractor()
@@ -77,7 +84,13 @@ class SceneContainerStartableTest : SysuiTestCase() {
    private val falsingCollector: FalsingCollector = mock()
    private val powerInteractor = PowerInteractorFactory.create().powerInteractor

    private val underTest =
    private lateinit var underTest: SceneContainerStartable

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)

        underTest =
            SceneContainerStartable(
                applicationScope = testScope.backgroundScope,
                sceneInteractor = sceneInteractor,
@@ -92,7 +105,9 @@ class SceneContainerStartableTest : SysuiTestCase() {
                bouncerInteractor = bouncerInteractor,
                simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
                authenticationInteractor = dagger.Lazy { authenticationInteractor },
                windowController = windowController,
            )
    }

    @Test
    fun hydrateVisibility() =
@@ -655,6 +670,58 @@ class SceneContainerStartableTest : SysuiTestCase() {
            assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
        }

    @Test
    fun hydrateWindowFocus() =
        testScope.runTest {
            val currentDesiredSceneKey by
                collectLastValue(sceneInteractor.desiredScene.map { it.key })
            val transitionStateFlow =
                prepareState(
                    isDeviceUnlocked = true,
                    initialSceneKey = SceneKey.Gone,
                )
            assertThat(currentDesiredSceneKey).isEqualTo(SceneKey.Gone)
            verify(windowController, never()).setNotificationShadeFocusable(anyBoolean())

            underTest.start()
            runCurrent()
            verify(windowController, times(1)).setNotificationShadeFocusable(false)

            sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "reason")
            transitionStateFlow.value =
                ObservableTransitionState.Transition(
                    fromScene = SceneKey.Gone,
                    toScene = SceneKey.Shade,
                    progress = flowOf(0.5f),
                    isInitiatedByUserInput = false,
                    isUserInputOngoing = flowOf(false),
                )
            runCurrent()
            verify(windowController, times(1)).setNotificationShadeFocusable(false)

            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
            transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Shade)
            runCurrent()
            verify(windowController, times(1)).setNotificationShadeFocusable(true)

            sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
            transitionStateFlow.value =
                ObservableTransitionState.Transition(
                    fromScene = SceneKey.Shade,
                    toScene = SceneKey.Gone,
                    progress = flowOf(0.5f),
                    isInitiatedByUserInput = false,
                    isUserInputOngoing = flowOf(false),
                )
            runCurrent()
            verify(windowController, times(1)).setNotificationShadeFocusable(true)

            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
            transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
            runCurrent()
            verify(windowController, times(2)).setNotificationShadeFocusable(false)
        }

    private fun TestScope.prepareState(
        isDeviceUnlocked: Boolean = false,
        isBypassEnabled: Boolean = false,
+17 −0
Original line number Diff line number Diff line
@@ -44,6 +44,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.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printSection
@@ -83,6 +84,7 @@ constructor(
    private val powerInteractor: PowerInteractor,
    private val simBouncerInteractor: Lazy<SimBouncerInteractor>,
    private val authenticationInteractor: Lazy<AuthenticationInteractor>,
    private val windowController: NotificationShadeWindowController,
) : CoreStartable {

    override fun start() {
@@ -92,6 +94,7 @@ constructor(
            automaticallySwitchScenes()
            hydrateSystemUiState()
            collectFalsingSignals()
            hydrateWindowFocus()
        } else {
            sceneLogger.logFrameworkEnabled(
                isEnabled = false,
@@ -348,6 +351,20 @@ constructor(
        }
    }

    /** Keeps the focus state of the window view up-to-date. */
    private fun hydrateWindowFocus() {
        applicationScope.launch {
            sceneInteractor.transitionState
                .mapNotNull { transitionState ->
                    (transitionState as? ObservableTransitionState.Idle)?.scene
                }
                .distinctUntilChanged()
                .collect { sceneKey ->
                    windowController.setNotificationShadeFocusable(sceneKey != SceneKey.Gone)
                }
        }
    }

    private fun switchToScene(targetSceneKey: SceneKey, loggingReason: String) {
        sceneInteractor.changeScene(
            scene = SceneModel(targetSceneKey),