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

Commit e71e3427 authored by Matt Pietal's avatar Matt Pietal
Browse files

Transitions: Gone->Dreaming

Explicitly support gone to dreaming. At the moment, the lockscreen
briefly shows when the power button is pushed. This will move lockscreen
elements out of the way.

Bug: 260637747
Test: atest GoneToDreamingTransitionViewModelTest
NotificationPanelViewControllerTest KeyguardScenariosTest

Change-Id: I0e3adb6799a8144eaf810b9ee20e54b29d7d505b
parent 6195a5c2
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1282,6 +1282,9 @@
    <!-- LOCKSCREEN -> DREAMING transition: Amount to shift lockscreen content on entering -->
    <dimen name="lockscreen_to_dreaming_transition_lockscreen_translation_y">-40dp</dimen>

    <!-- GONE -> DREAMING transition: Amount to shift lockscreen content on entering -->
    <dimen name="gone_to_dreaming_transition_lockscreen_translation_y">-40dp</dimen>

    <!-- LOCKSCREEN -> OCCLUDED transition: Amount to shift lockscreen content on entering -->
    <dimen name="lockscreen_to_occluded_transition_lockscreen_translation_y">-40dp</dimen>

+19 −3
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import com.android.systemui.keyguard.shared.model.AnimationParams
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -30,6 +31,8 @@ import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import javax.inject.Inject
import kotlin.time.Duration
import kotlin.math.max
import kotlin.math.min
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
@@ -53,6 +56,9 @@ constructor(
    val dreamingToLockscreenTransition: Flow<TransitionStep> =
        repository.transition(DREAMING, LOCKSCREEN)

    /** GONE->DREAMING transition information. */
    val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)

    /** LOCKSCREEN->AOD transition information. */
    val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)

@@ -106,13 +112,23 @@ constructor(
    ): Flow<Float> {
        val start = (params.startTime / totalDuration).toFloat()
        val chunks = (totalDuration / params.duration).toFloat()
        var isRunning = false
        return flow
            // When starting, emit a value of 0f to give animations a chance to set initial state
            .map { step ->
                val value = (step.value - start) * chunks
                if (step.transitionState == STARTED) {
                    0f
                    // When starting, make sure to always emit. If a transition is started from the
                    // middle, it is possible this animation is being skipped but we need to inform
                    // the ViewModels of the last update
                    isRunning = true
                    max(0f, min(1f, value))
                } else if (isRunning && value >= 1f) {
                    // Always send a final value of 1. Because of rounding, [value] may never be
                    // exactly 1.
                    isRunning = false
                    1f
                } else {
                    (step.value - start) * chunks
                    value
                }
            }
            .filter { value -> value >= 0f && value <= 1f }
+68 −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.ui.viewmodel

import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AnimationParams
import com.android.systemui.keyguard.shared.model.TransitionState
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge

/** Breaks down GONE->DREAMING transition into discrete steps for corresponding views to consume. */
@SysUISingleton
class GoneToDreamingTransitionViewModel
@Inject
constructor(
    private val interactor: KeyguardTransitionInteractor,
) {

    /** Lockscreen views y-translation */
    fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
        return merge(
            flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
                (EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx)
            },
            // On end, reset the translation to 0
            interactor.goneToDreamingTransition
                .filter { step -> step.transitionState == TransitionState.FINISHED }
                .map { 0f }
        )
    }

    /** Lockscreen views alpha */
    val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA).map { 1f - it }

    private fun flowForAnimation(params: AnimationParams): Flow<Float> {
        return interactor.transitionStepAnimation(
            interactor.goneToDreamingTransition,
            params,
            totalDuration = TO_DREAMING_DURATION
        )
    }

    companion object {
        val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = 500.milliseconds)
        val LOCKSCREEN_ALPHA = AnimationParams(duration = 250.milliseconds)
    }
}
+24 −0
Original line number Diff line number Diff line
@@ -144,6 +144,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
@@ -692,6 +693,7 @@ public final class NotificationPanelViewController implements Dumpable {
    private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
    private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
    private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
    private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
    private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;

    private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -700,6 +702,7 @@ public final class NotificationPanelViewController implements Dumpable {
    private int mDreamingToLockscreenTransitionTranslationY;
    private int mOccludedToLockscreenTransitionTranslationY;
    private int mLockscreenToDreamingTransitionTranslationY;
    private int mGoneToDreamingTransitionTranslationY;
    private int mLockscreenToOccludedTransitionTranslationY;
    private boolean mUnocclusionTransitionFlagEnabled = false;

@@ -735,6 +738,12 @@ public final class NotificationPanelViewController implements Dumpable {
                    step.getTransitionState() == TransitionState.RUNNING;
            };

    private final Consumer<TransitionStep> mGoneToDreamingTransition =
            (TransitionStep step) -> {
                mIsOcclusionTransitionRunning =
                    step.getTransitionState() == TransitionState.RUNNING;
            };

    private final Consumer<TransitionStep> mLockscreenToOccludedTransition =
            (TransitionStep step) -> {
                mIsOcclusionTransitionRunning =
@@ -813,6 +822,7 @@ public final class NotificationPanelViewController implements Dumpable {
            DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel,
            OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel,
            LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel,
            GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel,
            LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
            @Main CoroutineDispatcher mainDispatcher,
            KeyguardTransitionInteractor keyguardTransitionInteractor,
@@ -834,6 +844,7 @@ public final class NotificationPanelViewController implements Dumpable {
        mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
        mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel;
        mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel;
        mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
        mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
        mKeyguardTransitionInteractor = keyguardTransitionInteractor;
        mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@@ -1172,6 +1183,17 @@ public final class NotificationPanelViewController implements Dumpable {
                    setTransitionY(mNotificationStackScrollLayoutController),
                    mMainDispatcher);

            // Gone->Dreaming
            collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(),
                    mGoneToDreamingTransition, mMainDispatcher);
            collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(),
                    setTransitionAlpha(mNotificationStackScrollLayoutController),
                    mMainDispatcher);
            collectFlow(mView, mGoneToDreamingTransitionViewModel.lockscreenTranslationY(
                    mGoneToDreamingTransitionTranslationY),
                    setTransitionY(mNotificationStackScrollLayoutController),
                    mMainDispatcher);

            // Lockscreen->Occluded
            collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(),
                    mLockscreenToOccludedTransition, mMainDispatcher);
@@ -1223,6 +1245,8 @@ public final class NotificationPanelViewController implements Dumpable {
                R.dimen.occluded_to_lockscreen_transition_lockscreen_translation_y);
        mLockscreenToDreamingTransitionTranslationY = mResources.getDimensionPixelSize(
                R.dimen.lockscreen_to_dreaming_transition_lockscreen_translation_y);
        mGoneToDreamingTransitionTranslationY = mResources.getDimensionPixelSize(
                R.dimen.gone_to_dreaming_transition_lockscreen_translation_y);
        mLockscreenToOccludedTransitionTranslationY = mResources.getDimensionPixelSize(
                R.dimen.lockscreen_to_occluded_transition_lockscreen_translation_y);
    }
+45 −1
Original line number Diff line number Diff line
@@ -506,7 +506,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
                withArgCaptor<TransitionInfo> {
                    verify(mockTransitionRepository).startTransition(capture())
                }
            // THEN a transition to DOZING should occur
            // THEN a transition to AOD should occur
            assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
            assertThat(info.from).isEqualTo(KeyguardState.GONE)
            assertThat(info.to).isEqualTo(KeyguardState.AOD)
@@ -515,6 +515,50 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
            coroutineContext.cancelChildren()
        }

    @Test
    fun `GONE to DREAMING`() =
        testScope.runTest {
            // GIVEN a device that is not dreaming or dozing
            keyguardRepository.setDreamingWithOverlay(false)
            keyguardRepository.setDozeTransitionModel(
                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
            )
            runCurrent()

            // GIVEN a prior transition has run to GONE
            runner.startTransition(
                testScope,
                TransitionInfo(
                    ownerName = "",
                    from = KeyguardState.LOCKSCREEN,
                    to = KeyguardState.GONE,
                    animator =
                        ValueAnimator().apply {
                            duration = 10
                            interpolator = Interpolators.LINEAR
                        },
                )
            )
            runCurrent()
            reset(mockTransitionRepository)

            // WHEN the device begins to dream
            keyguardRepository.setDreamingWithOverlay(true)
            runCurrent()

            val info =
                withArgCaptor<TransitionInfo> {
                    verify(mockTransitionRepository).startTransition(capture())
                }
            // THEN a transition to DREAMING should occur
            assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
            assertThat(info.from).isEqualTo(KeyguardState.GONE)
            assertThat(info.to).isEqualTo(KeyguardState.DREAMING)
            assertThat(info.animator).isNotNull()

            coroutineContext.cancelChildren()
        }

    private fun startingToWake() =
        WakefulnessModel(
            WakefulnessState.STARTING_TO_WAKE,
Loading