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

Commit dd64bf8d authored by Chandru S's avatar Chandru S
Browse files

Add predictive back animation to the compose bouncer

This is not required for flexiglass as the back animation is wired up through STL transitions for flexiglass.

Fixes: 375206941
Test: verified manually,
 1. Enable gesture nav
 2. Open bouncer
 3. Use gesture nav to go back to the lockscreen
 4. bouncer should shrink in size
Flag: com.android.systemui.compose_bouncer
Change-Id: I762186ec1503ef7a3975a7745f1f9fdc78a29811
parent c64ab0a3
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.key.onKeyEvent
@@ -138,6 +139,7 @@ fun BouncerContent(
    dialogFactory: BouncerDialogFactory,
    modifier: Modifier,
) {
    val scale by viewModel.scale.collectAsStateWithLifecycle()
    Box(
        // Allows the content within each of the layouts to react to the appearance and
        // disappearance of the IME, which is also known as the software keyboard.
@@ -145,7 +147,7 @@ fun BouncerContent(
        // Despite the keyboard only being part of the password bouncer, adding it at this level is
        // both necessary to properly handle the keyboard in all layouts and harmless in cases when
        // the keyboard isn't used (like the PIN or pattern auth methods).
        modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent)
        modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent).scale(scale)
    ) {
        when (layout) {
            BouncerSceneLayout.STANDARD_BOUNCER -> StandardLayout(viewModel = viewModel)
+7 −3
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
@@ -82,8 +83,8 @@ import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.DisableSceneContainer;
import com.android.systemui.flags.EnableSceneContainer;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionState;
@@ -170,6 +171,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
    @Mock private DeviceEntryInteractor mDeviceEntryInteractor;
    @Mock private SceneInteractor mSceneInteractor;
    @Mock private DismissCallbackRegistry mDismissCallbackRegistry;
    @Mock private BouncerInteractor mBouncerInteractor;

    private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
    private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
@@ -241,7 +243,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
                        mock(StatusBarKeyguardViewManagerInteractor.class),
                        mExecutor,
                        () -> mDeviceEntryInteractor,
                        mDismissCallbackRegistry) {
                        mDismissCallbackRegistry,
                        () -> mBouncerInteractor) {
                    @Override
                    public ViewRootImpl getViewRootImpl() {
                        return mViewRootImpl;
@@ -748,7 +751,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
                        mock(StatusBarKeyguardViewManagerInteractor.class),
                        mExecutor,
                        () -> mDeviceEntryInteractor,
                        mDismissCallbackRegistry) {
                        mDismissCallbackRegistry,
                        () -> mBouncerInteractor) {
                    @Override
                    public ViewRootImpl getViewRootImpl() {
                        return mViewRootImpl;
+2 −0
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@ constructor(
    private val globalSettings: GlobalSettings,
    private val flags: FeatureFlagsClassic,
) {
    val scale: MutableStateFlow<Float> = MutableStateFlow(1.0f)

    /** Whether the user switcher should be displayed within the bouncer UI on large screens. */
    val isUserSwitcherEnabledInConfig: Boolean
        get() =
+32 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInt
import com.android.systemui.log.SessionTracker
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneBackInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -45,6 +46,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
@@ -139,6 +141,9 @@ constructor(
    /** X coordinate of the last recorded touch position on the lockscreen. */
    val lastRecordedLockscreenTouchPosition = repository.lastRecordedLockscreenTouchPosition

    /** Value between 0-1 that specifies by how much the bouncer UI should be scaled down. */
    val scale: StateFlow<Float> = repository.scale.asStateFlow()

    /** The scene to show when bouncer is dismissed. */
    val dismissDestination: Flow<SceneKey> =
        sceneBackInteractor.backScene
@@ -190,6 +195,22 @@ constructor(
        repository.recordLockscreenTouchPosition(x)
    }

    fun onBackEventProgressed(progress: Float) {
        // this is applicable only for compose bouncer without flexiglass
        SceneContainerFlag.assertInLegacyMode()
        repository.scale.value = (mapBackEventProgressToScale(progress))
    }

    fun onBackEventCancelled() {
        // this is applicable only for compose bouncer without flexiglass
        SceneContainerFlag.assertInLegacyMode()
        repository.scale.value = DEFAULT_SCALE
    }

    fun resetScale() {
        repository.scale.value = DEFAULT_SCALE
    }

    /**
     * Attempts to authenticate based on the given user input.
     *
@@ -253,4 +274,15 @@ constructor(
    suspend fun onImeHiddenByUser() {
        _onImeHiddenByUser.emit(Unit)
    }

    private fun mapBackEventProgressToScale(progress: Float): Float {
        // TODO(b/263819310): Update the interpolator to match spec.
        return MIN_BACK_SCALE + (1 - MIN_BACK_SCALE) * (1 - progress)
    }

    companion object {
        // How much the view scales down to during back gestures.
        private const val MIN_BACK_SCALE: Float = 0.9f
        private const val DEFAULT_SCALE: Float = 1.0f
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -140,11 +140,15 @@ constructor(
     */
    val isFoldSplitRequired: StateFlow<Boolean> = _isFoldSplitRequired.asStateFlow()

    /** How much the bouncer UI should be scaled. */
    val scale: StateFlow<Float> = bouncerInteractor.scale

    private val _isInputEnabled =
        MutableStateFlow(authenticationInteractor.lockoutEndTimestamp == null)
    private val isInputEnabled: StateFlow<Boolean> = _isInputEnabled.asStateFlow()

    override suspend fun onActivated(): Nothing {
        bouncerInteractor.resetScale()
        coroutineScope {
            launch { message.activate() }
            launch {
Loading