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

Commit 4b0a55e4 authored by Coco Duan's avatar Coco Duan
Browse files

Add transitions for the lockscreen hosted dream

Add a new KeyguardState for the lockscreen hosted dream, as
the transition animations to/from lockscreen/gone/bouncer are
different from the existing dreaming state. We may also add
animations for the lockscreen hosted dream<->lowlight dream
transition in later changes.
Add Gone->DreamingLockscreenHosted transition ViewModel and set
keyguard alpha to 0 immediately and don't animate the clock when
updating its position during the transition.
When dreaming active, the clock will always be centered.

Bug: b/285062308
Test: atest KeyguardTransitionScenariosTest
Test: atest NotificationPanelViewControllerTest
Change-Id: I6678318b9cbd89d8e47cdd04855c1a592dea5d71
parent 5b5df31e
Loading
Loading
Loading
Loading
+169 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 android.animation.ValueAnimator
import com.android.app.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch

@SysUISingleton
class FromDreamingLockscreenHostedTransitionInteractor
@Inject
constructor(
    override val transitionRepository: KeyguardTransitionRepository,
    override val transitionInteractor: KeyguardTransitionInteractor,
    @Application private val scope: CoroutineScope,
    private val keyguardInteractor: KeyguardInteractor,
) :
    TransitionInteractor(
        fromState = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
    ) {

    override fun start() {
        listenForDreamingLockscreenHostedToLockscreen()
        listenForDreamingLockscreenHostedToGone()
        listenForDreamingLockscreenHostedToDozing()
        listenForDreamingLockscreenHostedToOccluded()
        listenForDreamingLockscreenHostedToPrimaryBouncer()
    }

    private fun listenForDreamingLockscreenHostedToLockscreen() {
        scope.launch {
            keyguardInteractor.isActiveDreamLockscreenHosted
                // Add a slight delay to prevent transitioning to lockscreen from happening too soon
                // as dozing can arrive in a slight gap after the lockscreen hosted dream stops.
                .onEach { delay(50) }
                .sample(
                    combine(
                        keyguardInteractor.dozeTransitionModel,
                        transitionInteractor.startedKeyguardTransitionStep,
                        ::Pair
                    ),
                    ::toTriple
                )
                .collect {
                    (isActiveDreamLockscreenHosted, dozeTransitionModel, lastStartedTransition) ->
                    if (
                        !isActiveDreamLockscreenHosted &&
                            DozeStateModel.isDozeOff(dozeTransitionModel.to) &&
                            lastStartedTransition.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
                    ) {
                        startTransitionTo(KeyguardState.LOCKSCREEN)
                    }
                }
        }
    }

    private fun listenForDreamingLockscreenHostedToOccluded() {
        scope.launch {
            keyguardInteractor.isActiveDreamLockscreenHosted
                .sample(
                    combine(
                        keyguardInteractor.isKeyguardOccluded,
                        transitionInteractor.startedKeyguardTransitionStep,
                        ::Pair,
                    ),
                    ::toTriple
                )
                .collect { (isActiveDreamLockscreenHosted, isOccluded, lastStartedTransition) ->
                    if (
                        isOccluded &&
                            !isActiveDreamLockscreenHosted &&
                            lastStartedTransition.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
                    ) {
                        startTransitionTo(KeyguardState.OCCLUDED)
                    }
                }
        }
    }

    private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
        scope.launch {
            keyguardInteractor.primaryBouncerShowing
                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
                .collect { (isBouncerShowing, lastStartedTransitionStep) ->
                    if (
                        isBouncerShowing &&
                            lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
                    ) {
                        startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
                    }
                }
        }
    }

    private fun listenForDreamingLockscreenHostedToGone() {
        scope.launch {
            keyguardInteractor.biometricUnlockState
                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
                .collect { (biometricUnlockState, lastStartedTransitionStep) ->
                    if (
                        lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED &&
                            biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
                    ) {
                        startTransitionTo(KeyguardState.GONE)
                    }
                }
        }
    }

    private fun listenForDreamingLockscreenHostedToDozing() {
        scope.launch {
            combine(
                    keyguardInteractor.dozeTransitionModel,
                    transitionInteractor.startedKeyguardTransitionStep,
                    ::Pair
                )
                .collect { (dozeTransitionModel, lastStartedTransitionStep) ->
                    if (
                        dozeTransitionModel.to == DozeStateModel.DOZE &&
                            lastStartedTransitionStep.to == KeyguardState.DREAMING_LOCKSCREEN_HOSTED
                    ) {
                        startTransitionTo(KeyguardState.DOZING)
                    }
                }
        }
    }

    override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
        return ValueAnimator().apply {
            interpolator = Interpolators.LINEAR
            duration =
                if (toState == KeyguardState.LOCKSCREEN) TO_LOCKSCREEN_DURATION.inWholeMilliseconds
                else DEFAULT_DURATION.inWholeMilliseconds
        }
    }

    companion object {
        private val DEFAULT_DURATION = 500.milliseconds
        val TO_LOCKSCREEN_DURATION = 1167.milliseconds
    }
}
+9 −4
Original line number Diff line number Diff line
@@ -94,8 +94,13 @@ constructor(

    private fun listenForDreamingToGone() {
        scope.launch {
            keyguardInteractor.biometricUnlockState.collect { biometricUnlockState ->
                if (biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM) {
            keyguardInteractor.biometricUnlockState
                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
                .collect { (biometricUnlockState, lastStartedTransitionStep) ->
                    if (
                        lastStartedTransitionStep.to == KeyguardState.DREAMING &&
                            biometricUnlockState == BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
                    ) {
                        startTransitionTo(KeyguardState.GONE)
                    }
                }
+27 −3
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ constructor(
        listenForGoneToAodOrDozing()
        listenForGoneToDreaming()
        listenForGoneToLockscreen()
        listenForGoneToDreamingLockscreenHosted()
    }

    // Primarily for when the user chooses to lock down the device
@@ -63,12 +64,35 @@ constructor(
        }
    }

    private fun listenForGoneToDreamingLockscreenHosted() {
        scope.launch {
            keyguardInteractor.isActiveDreamLockscreenHosted
                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
                .collect { (isActiveDreamLockscreenHosted, lastStartedStep) ->
                    if (isActiveDreamLockscreenHosted && lastStartedStep.to == KeyguardState.GONE) {
                        startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
                    }
                }
        }
    }

    private fun listenForGoneToDreaming() {
        scope.launch {
            keyguardInteractor.isAbleToDream
                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
                .collect { (isAbleToDream, lastStartedStep) ->
                    if (isAbleToDream && lastStartedStep.to == KeyguardState.GONE) {
                .sample(
                    combine(
                        transitionInteractor.startedKeyguardTransitionStep,
                        keyguardInteractor.isActiveDreamLockscreenHosted,
                        ::Pair
                    ),
                    ::toTriple
                )
                .collect { (isAbleToDream, lastStartedStep, isActiveDreamLockscreenHosted) ->
                    if (
                        isAbleToDream &&
                            lastStartedStep.to == KeyguardState.GONE &&
                            !isActiveDreamLockscreenHosted
                    ) {
                        startTransitionTo(KeyguardState.DREAMING)
                    }
                }
+14 −4
Original line number Diff line number Diff line
@@ -70,21 +70,31 @@ constructor(
                    combine(
                        transitionInteractor.startedKeyguardTransitionStep,
                        transitionInteractor.finishedKeyguardState,
                        ::Pair
                        keyguardInteractor.isActiveDreamLockscreenHosted,
                        ::Triple
                    ),
                    ::toTriple
                    ::toQuad
                )
                .collect { (isAbleToDream, lastStartedTransition, finishedKeyguardState) ->
                .collect {
                    (
                        isAbleToDream,
                        lastStartedTransition,
                        finishedKeyguardState,
                        isActiveDreamLockscreenHosted) ->
                    val isOnLockscreen = finishedKeyguardState == KeyguardState.LOCKSCREEN
                    val isTransitionInterruptible =
                        lastStartedTransition.to == KeyguardState.LOCKSCREEN &&
                            !invalidFromStates.contains(lastStartedTransition.from)
                    if (isAbleToDream && (isOnLockscreen || isTransitionInterruptible)) {
                        if (isActiveDreamLockscreenHosted) {
                            startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
                        } else {
                            startTransitionTo(KeyguardState.DREAMING)
                        }
                    }
                }
        }
    }

    private fun listenForLockscreenToPrimaryBouncer() {
        scope.launch {
+39 −5
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.Utils.Companion.toQuint
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -52,6 +54,7 @@ constructor(
        listenForPrimaryBouncerToGone()
        listenForPrimaryBouncerToAodOrDozing()
        listenForPrimaryBouncerToLockscreenOrOccluded()
        listenForPrimaryBouncerToDreamingLockscreenHosted()
    }

    private fun listenForPrimaryBouncerToLockscreenOrOccluded() {
@@ -62,17 +65,24 @@ constructor(
                        keyguardInteractor.wakefulnessModel,
                        transitionInteractor.startedKeyguardTransitionStep,
                        keyguardInteractor.isKeyguardOccluded,
                        ::Triple
                    ),
                        keyguardInteractor.isActiveDreamLockscreenHosted,
                        ::toQuad
                    ),
                    ::toQuint
                )
                .collect { (isBouncerShowing, wakefulnessState, lastStartedTransitionStep, occluded)
                    ->
                .collect {
                    (
                        isBouncerShowing,
                        wakefulnessState,
                        lastStartedTransitionStep,
                        occluded,
                        isActiveDreamLockscreenHosted) ->
                    if (
                        !isBouncerShowing &&
                            lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER &&
                            (wakefulnessState.state == WakefulnessState.AWAKE ||
                                wakefulnessState.state == WakefulnessState.STARTING_TO_WAKE)
                                wakefulnessState.state == WakefulnessState.STARTING_TO_WAKE) &&
                            !isActiveDreamLockscreenHosted
                    ) {
                        startTransitionTo(
                            if (occluded) KeyguardState.OCCLUDED else KeyguardState.LOCKSCREEN
@@ -111,6 +121,30 @@ constructor(
        }
    }

    private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
        scope.launch {
            keyguardInteractor.primaryBouncerShowing
                .sample(
                    combine(
                        keyguardInteractor.isActiveDreamLockscreenHosted,
                        transitionInteractor.startedKeyguardTransitionStep,
                        ::Pair
                    ),
                    ::toTriple
                )
                .collect {
                    (isBouncerShowing, isActiveDreamLockscreenHosted, lastStartedTransitionStep) ->
                    if (
                        !isBouncerShowing &&
                            isActiveDreamLockscreenHosted &&
                            lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
                    ) {
                        startTransitionTo(KeyguardState.DREAMING_LOCKSCREEN_HOSTED)
                    }
                }
        }
    }

    private fun listenForPrimaryBouncerToGone() {
        scope.launch {
            keyguardInteractor.isKeyguardGoingAway
Loading