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

Commit f9a8c06b authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update flags on the window to allow the window to be focusable." into main

parents 807c9c04 c57b81f5
Loading
Loading
Loading
Loading
+0 −35
Original line number Diff line number Diff line
@@ -1191,41 +1191,6 @@ class SceneContainerStartableTest : SysuiTestCase() {
            verify(notificationShadeWindowController, times(1)).setKeyguardShowing(true)
        }

    @Test
    fun hydrateWindowController_setBouncerShowing() =
        testScope.runTest {
            underTest.start()
            val notificationShadeWindowController = kosmos.notificationShadeWindowController
            val transitionStateFlow = prepareState(initialSceneKey = Scenes.Lockscreen)
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            verify(notificationShadeWindowController, never()).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(false)

            emulateSceneTransition(transitionStateFlow, Scenes.Bouncer)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(false)

            emulateSceneTransition(transitionStateFlow, Scenes.Lockscreen)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)

            kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
                SuccessFingerprintAuthenticationStatus(0, true)
            )
            assertThat(currentScene).isEqualTo(Scenes.Gone)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)

            emulateSceneTransition(transitionStateFlow, Scenes.Lockscreen)
            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)

            emulateSceneTransition(transitionStateFlow, Scenes.Bouncer)
            verify(notificationShadeWindowController, times(2)).setBouncerShowing(true)
            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)
        }

    @Test
    fun hydrateWindowController_setKeyguardOccluded() =
        testScope.runTest {
+96 −0
Original line number Diff line number Diff line
@@ -16,18 +16,28 @@

package com.android.systemui.shade.ui.viewmodel

import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -150,4 +160,90 @@ class NotificationShadeWindowModelTest : SysuiTestCase() {
            )
            assertThat(isKeyguardOccluded).isTrue()
        }

    @Test
    @EnableSceneContainer
    fun withSceneContainer_bouncerShowing_providesTheCorrectState() =
        testScope.runTest {
            val bouncerShowing by collectLastValue(underTest.isBouncerShowing)

            val transitionState =
                MutableStateFlow<ObservableTransitionState>(
                    ObservableTransitionState.Idle(Scenes.Lockscreen)
                )
            kosmos.sceneInteractor.setTransitionState(transitionState)
            runCurrent()
            assertThat(bouncerShowing).isFalse()

            transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer)
            runCurrent()
            assertThat(bouncerShowing).isTrue()
        }

    @Test
    @EnableFlags(com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER)
    fun withComposeBouncer_bouncerShowing_providesTheCorrectState() =
        testScope.runTest {
            val bouncerShowing by collectLastValue(underTest.isBouncerShowing)

            kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = false)
            runCurrent()
            assertThat(bouncerShowing).isFalse()

            kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = true)
            runCurrent()
            assertThat(bouncerShowing).isTrue()
        }

    @Test
    @EnableSceneContainer
    fun withSceneContainer_doesBouncerRequireIme_providesTheCorrectState() =
        testScope.runTest {
            val bouncerRequiresIme by collectLastValue(underTest.doesBouncerRequireIme)
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pin
            )

            val transitionState =
                MutableStateFlow<ObservableTransitionState>(
                    ObservableTransitionState.Idle(Scenes.Bouncer)
                )
            kosmos.sceneInteractor.setTransitionState(transitionState)
            runCurrent()
            assertThat(bouncerRequiresIme).isFalse()

            // go back to lockscreen
            transitionState.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
            runCurrent()

            // change auth method
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Password
            )
            // go back to bouncer
            transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer)
            runCurrent()
            assertThat(bouncerRequiresIme).isTrue()
        }

    @Test
    @EnableFlags(com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER)
    fun withComposeBouncer_doesBouncerRequireIme_providesTheCorrectState() =
        testScope.runTest {
            val bouncerRequiresIme by collectLastValue(underTest.doesBouncerRequireIme)
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pin
            )

            kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = true)
            runCurrent()
            assertThat(bouncerRequiresIme).isFalse()

            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Password
            )
            kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = true)
            runCurrent()
            assertThat(bouncerRequiresIme).isFalse()
        }
}
+0 −9
Original line number Diff line number Diff line
@@ -570,15 +570,6 @@ constructor(
            }
        }

        applicationScope.launch {
            sceneInteractor.currentScene
                .map { it == Scenes.Bouncer }
                .distinctUntilChanged()
                .collect { isBouncerShowing ->
                    windowController.setBouncerShowing(isBouncerShowing)
                }
        }

        applicationScope.launch {
            occlusionInteractor.invisibleDueToOcclusion.collect { invisibleDueToOcclusion ->
                windowController.setKeyguardOccluded(invisibleDueToOcclusion)
+7 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.Flags;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dagger.SysUISingleton;
@@ -342,6 +343,12 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
                    this::setKeyguardOccluded
            );
        }
        if (ComposeBouncerFlags.INSTANCE.isComposeBouncerOrSceneContainerEnabled()) {
            collectFlow(mWindowRootView, mNotificationShadeWindowModel.isBouncerShowing(),
                    this::setBouncerShowing);
            collectFlow(mWindowRootView, mNotificationShadeWindowModel.getDoesBouncerRequireIme(),
                    this::setKeyguardNeedsInput);
        }
    }

    @Override
+61 −0
Original line number Diff line number Diff line
@@ -16,16 +16,25 @@

package com.android.systemui.shade.ui.viewmodel

import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.BooleanFlowOperators.any
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map

/** Models UI state for the shade window. */
@@ -34,6 +43,9 @@ class NotificationShadeWindowModel
@Inject
constructor(
    keyguardTransitionInteractor: KeyguardTransitionInteractor,
    sceneInteractor: dagger.Lazy<SceneInteractor>,
    authenticationInteractor: dagger.Lazy<AuthenticationInteractor>,
    primaryBouncerInteractor: PrimaryBouncerInteractor,
) {
    /**
     * Considered to be occluded if in OCCLUDED, DREAMING, GLANCEABLE_HUB/Communal, or transitioning
@@ -70,4 +82,53 @@ constructor(
                ),
            )
            .any()

    /**
     * Whether bouncer is currently showing or not.
     *
     * Applicable only when either [SceneContainerFlag] or [ComposeBouncerFlags] are enabled,
     * otherwise it throws an error.
     */
    val isBouncerShowing: Flow<Boolean> =
        when {
            SceneContainerFlag.isEnabled -> {
                sceneInteractor.get().transitionState.map { it.isIdle(Scenes.Bouncer) }
            }
            ComposeBouncerFlags.isOnlyComposeBouncerEnabled() -> primaryBouncerInteractor.isShowing
            else ->
                flow {
                    error(
                        "Consume this flow only when SceneContainerFlag " +
                            "or ComposeBouncerFlags are enabled"
                    )
                }
        }.distinctUntilChanged()

    /**
     * Whether the bouncer currently require IME for device entry.
     *
     * This emits true when the authentication method is set to password and the bouncer is
     * currently showing. Throws an error when this is used without either [SceneContainerFlag] or
     * [ComposeBouncerFlags]
     */
    val doesBouncerRequireIme: Flow<Boolean> =
        if (ComposeBouncerFlags.isComposeBouncerOrSceneContainerEnabled()) {
                // This is required to make the window, where the bouncer resides,
                // focusable. InputMethodManager allows IME to be shown only for views
                // in windows that do not have the FLAG_NOT_FOCUSABLE flag.

                isBouncerShowing
                    .sample(authenticationInteractor.get().authenticationMethod, ::Pair)
                    .map { (showing, authMethod) ->
                        showing && authMethod == AuthenticationMethodModel.Password
                    }
            } else {
                flow {
                    error(
                        "Consume this flow only when SceneContainerFlag " +
                            "or ComposeBouncerFlags are enabled"
                    )
                }
            }
            .distinctUntilChanged()
}
Loading