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

Commit 35235e6c authored by Josh Tsuji's avatar Josh Tsuji Committed by Android (Google) Code Review
Browse files

Merge "Adds swipe to dismiss to the keyguard refactor." into main

parents d7c959d7 ab6be9da
Loading
Loading
Loading
Loading
+10 −46
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
@@ -36,7 +35,6 @@ import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import dagger.Lazy
import java.util.UUID
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -64,7 +62,7 @@ constructor(
    private val shadeRepository: ShadeRepository,
    private val powerInteractor: PowerInteractor,
    private val glanceableHubTransitions: GlanceableHubTransitions,
    inWindowLauncherUnlockAnimationInteractor: Lazy<InWindowLauncherUnlockAnimationInteractor>,
    private val swipeToDismissInteractor: SwipeToDismissInteractor,
) :
    TransitionInteractor(
        fromState = KeyguardState.LOCKSCREEN,
@@ -102,49 +100,7 @@ constructor(
                    return@map null
                }

                true // TODO(b/278086361): Implement continuous swipe to unlock.
            }
            .onStart {
                // Default to null ("don't care, use a reasonable default").
                emit(null)
            }
            .distinctUntilChanged()

    /**
     * The surface behind view params to use for the transition from LOCKSCREEN, or null if we don't
     * care and should use a reasonable default.
     */
    val surfaceBehindModel: Flow<KeyguardSurfaceBehindModel?> =
        combine(
                transitionInteractor.startedKeyguardTransitionStep,
                transitionInteractor.transitionStepsFromState(KeyguardState.LOCKSCREEN),
                inWindowLauncherUnlockAnimationInteractor
                    .get()
                    .transitioningToGoneWithInWindowAnimation,
            ) { startedStep, fromLockscreenStep, transitioningToGoneWithInWindowAnimation ->
                if (startedStep.to != KeyguardState.GONE) {
                    // Only LOCKSCREEN -> GONE has specific surface params (for the unlock
                    // animation).
                    return@combine null
                } else if (transitioningToGoneWithInWindowAnimation) {
                    // If we're prepared for the in-window unlock, we're going to play an animation
                    // in the window. Make it fully visible.
                    KeyguardSurfaceBehindModel(
                        alpha = 1f,
                    )
                } else if (fromLockscreenStep.value > 0.5f) {
                    // Start the animation once we're 50% transitioned to GONE.
                    KeyguardSurfaceBehindModel(
                        animateFromAlpha = 0f,
                        alpha = 1f,
                        animateFromTranslationY = 500f,
                        translationY = 0f
                    )
                } else {
                    KeyguardSurfaceBehindModel(
                        alpha = 0f,
                    )
                }
                true // Make the surface visible during LS -> GONE transitions.
            }
            .onStart {
                // Default to null ("don't care, use a reasonable default").
@@ -325,6 +281,13 @@ constructor(

    private fun listenForLockscreenToGoneDragging() {
        if (KeyguardWmStateRefactor.isEnabled) {
            // When the refactor is enabled, we no longer use isKeyguardGoingAway.
            scope.launch {
                swipeToDismissInteractor.dismissFling.collect { _ ->
                    startTransitionTo(KeyguardState.GONE)
                }
            }

            return
        }

@@ -332,6 +295,7 @@ constructor(
            keyguardInteractor.isKeyguardGoingAway
                .sample(startedKeyguardTransitionStep, ::Pair)
                .collect { pair ->
                    KeyguardWmStateRefactor.assertInLegacyMode()
                    val (isKeyguardGoingAway, lastStartedStep) = pair
                    if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) {
                        startTransitionTo(KeyguardState.GONE)
+5 −9
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindReposi
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.shared.system.smartspace.SmartspaceState
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -52,10 +51,7 @@ constructor(
    val transitioningToGoneWithInWindowAnimation: StateFlow<Boolean> =
        transitionInteractor
            .isInTransitionToState(KeyguardState.GONE)
            .sample(repository.launcherActivityClass, ::Pair)
            .map { (isTransitioningToGone, launcherActivityClass) ->
                isTransitioningToGone && isActivityClassUnderneath(launcherActivityClass)
            }
            .map { transitioningToGone -> transitioningToGone && isLauncherUnderneath() }
            .stateIn(scope, SharingStarted.Eagerly, false)

    /**
@@ -91,11 +87,11 @@ constructor(
    }

    /**
     * Whether an activity with the given [activityClass] name is currently underneath the
     * lockscreen (it's at the top of the activity task stack).
     * Whether the Launcher is currently underneath the lockscreen (it's at the top of the activity
     * task stack).
     */
    private fun isActivityClassUnderneath(activityClass: String?): Boolean {
        return activityClass?.let {
    fun isLauncherUnderneath(): Boolean {
        return repository.launcherActivityClass.value?.let {
            activityManager.runningTask?.topActivity?.className?.equals(it)
        }
            ?: false
+45 −45
Original line number Diff line number Diff line
@@ -16,75 +16,75 @@

package com.android.systemui.keyguard.domain.interactor

import android.content.Context
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindRepository
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.isSurfaceVisible
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
import com.android.systemui.util.kotlin.toPx
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map

/**
 * Distance over which the surface behind the keyguard is animated in during a Y-translation
 * animation.
 */
const val SURFACE_TRANSLATION_Y_DISTANCE_DP = 250

@SysUISingleton
class KeyguardSurfaceBehindInteractor
@Inject
constructor(
    private val repository: KeyguardSurfaceBehindRepository,
    private val fromLockscreenInteractor: FromLockscreenTransitionInteractor,
    private val fromPrimaryBouncerInteractor: FromPrimaryBouncerTransitionInteractor,
    context: Context,
    transitionInteractor: KeyguardTransitionInteractor,
    inWindowLauncherUnlockAnimationInteractor: Lazy<InWindowLauncherUnlockAnimationInteractor>,
    swipeToDismissInteractor: SwipeToDismissInteractor,
) {

    @OptIn(ExperimentalCoroutinesApi::class)
    /**
     * The view params to use for the surface. These params describe the alpha/translation values to
     * apply, as well as animation parameters if necessary.
     */
    val viewParams: Flow<KeyguardSurfaceBehindModel> =
        transitionInteractor.isInTransitionToAnyState
            .flatMapLatest { isInTransition ->
                if (!isInTransition) {
                    defaultParams
                } else {
        combine(
                        transitionSpecificViewParams,
                        defaultParams,
                    ) { transitionParams, defaultParams ->
                        transitionParams ?: defaultParams
                transitionInteractor.startedKeyguardTransitionStep,
                transitionInteractor.currentKeyguardState,
            ) { startedStep, currentState ->
                // If we're in transition to GONE, special unlock animation params apply.
                if (startedStep.to == KeyguardState.GONE && currentState != KeyguardState.GONE) {
                    if (inWindowLauncherUnlockAnimationInteractor.get().isLauncherUnderneath()) {
                        // The Launcher icons have their own translation/alpha animations during the
                        // in-window animation. We'll just make the surface visible and let Launcher
                        // do its thing.
                        return@combine KeyguardSurfaceBehindModel(
                            alpha = 1f,
                        )
                    } else {
                        // Otherwise, animate a surface in via alpha/translation, and apply the
                        // swipe velocity (if available) to the translation spring.
                        return@combine KeyguardSurfaceBehindModel(
                            animateFromAlpha = 0f,
                            alpha = 1f,
                            animateFromTranslationY =
                                SURFACE_TRANSLATION_Y_DISTANCE_DP.toPx(context).toFloat(),
                            translationY = 0f,
                            startVelocity = swipeToDismissInteractor.dismissFling.value?.velocity
                                    ?: 0f,
                        )
                    }
                }

                // Default to the visibility of the current state, with no animations.
                KeyguardSurfaceBehindModel(alpha = if (isSurfaceVisible(currentState)) 1f else 0f)
            }
            .distinctUntilChanged()

    val isAnimatingSurface = repository.isAnimatingSurface

    private val defaultParams =
        transitionInteractor.finishedKeyguardState.map { state ->
            KeyguardSurfaceBehindModel(
                alpha =
                    if (WindowManagerLockscreenVisibilityInteractor.isSurfaceVisible(state)) 1f
                    else 0f
            )
        }

    /**
     * View params provided by the transition interactor for the most recently STARTED transition.
     * This is used to run transition-specific animations on the surface.
     *
     * If null, there are no transition-specific view params needed for this transition and we will
     * use a reasonable default.
     */
    @OptIn(ExperimentalCoroutinesApi::class)
    private val transitionSpecificViewParams: Flow<KeyguardSurfaceBehindModel?> =
        transitionInteractor.startedKeyguardTransitionStep.flatMapLatest { startedStep ->
            when (startedStep.from) {
                KeyguardState.LOCKSCREEN -> fromLockscreenInteractor.surfaceBehindModel
                KeyguardState.PRIMARY_BOUNCER -> fromPrimaryBouncerInteractor.surfaceBehindModel
                // Return null for other states, where no transition specific params are needed.
                else -> flowOf(null)
            }
        }

    fun setAnimatingSurface(animating: Boolean) {
        repository.setAnimatingSurface(animating)
    }
+66 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.keyguard.domain.interactor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

/**
 * Handles logic around the swipe to dismiss gesture, where the user swipes up on the dismissable
 * lockscreen to unlock the device.
 */
@SysUISingleton
class SwipeToDismissInteractor
@Inject
constructor(
    @Background backgroundScope: CoroutineScope,
    shadeRepository: ShadeRepository,
    transitionInteractor: KeyguardTransitionInteractor,
    keyguardInteractor: KeyguardInteractor,
) {
    /**
     * Emits a [FlingInfo] whenever a swipe to dismiss gesture has started a fling animation on the
     * lockscreen while it's dismissable.
     *
     * This value is collected by [FromLockscreenTransitionInteractor] to start a transition from
     * LOCKSCREEN -> GONE, and by [KeyguardSurfaceBehindInteractor] to match the surface remote
     * animation's velocity to the fling velocity, if applicable.
     */
    val dismissFling =
        shadeRepository.currentFling
            .sample(
                transitionInteractor.startedKeyguardState,
                keyguardInteractor.isKeyguardDismissible
            )
            .filter { (flingInfo, startedState, keyguardDismissable) ->
                flingInfo != null &&
                    !flingInfo.expand &&
                    startedState == KeyguardState.LOCKSCREEN &&
                    keyguardDismissable
            }
            .map { (flingInfo, _) -> flingInfo }
            .stateIn(backgroundScope, SharingStarted.Eagerly, null)
}
+3 −0
Original line number Diff line number Diff line
@@ -44,6 +44,9 @@ data class KeyguardSurfaceBehindModel(
     * running, in which case we'll animate from the current value to [translationY].
     */
    val animateFromTranslationY: Float = translationY,

    /** Velocity with which to start the Y-translation spring animation. */
    val startVelocity: Float = 0f,
) {
    fun willAnimateAlpha(): Boolean {
        return animateFromAlpha != alpha
Loading