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
This commit is contained in:
Matt Pietal
2023-01-03 14:03:18 +00:00
parent 6195a5c22c
commit e71e3427e6
7 changed files with 297 additions and 4 deletions

View File

@@ -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>

View File

@@ -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 }

View File

@@ -0,0 +1,68 @@
/*
* 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)
}
}

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -0,0 +1,135 @@
/*
* 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 androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
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.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel.Companion.LOCKSCREEN_ALPHA
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel.Companion.LOCKSCREEN_TRANSLATION_Y
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@SmallTest
@RunWith(JUnit4::class)
class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: GoneToDreamingTransitionViewModel
private lateinit var repository: FakeKeyguardTransitionRepository
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
val interactor = KeyguardTransitionInteractor(repository)
underTest = GoneToDreamingTransitionViewModel(interactor)
}
@Test
fun lockscreenFadeOut() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
// Should start running here...
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.1f))
repository.sendTransitionStep(step(0.2f))
// ...up to here
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(1f))
// Only three values should be present, since the dream overlay runs for a small
// fraction
// of the overall animation time
assertThat(values.size).isEqualTo(3)
assertThat(values[0]).isEqualTo(1f - animValue(0f, LOCKSCREEN_ALPHA))
assertThat(values[1]).isEqualTo(1f - animValue(0.1f, LOCKSCREEN_ALPHA))
assertThat(values[2]).isEqualTo(1f - animValue(0.2f, LOCKSCREEN_ALPHA))
job.cancel()
}
@Test
fun lockscreenTranslationY() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
val pixels = 100
val job =
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
// Should start running here...
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.5f))
// ...up to here
repository.sendTransitionStep(step(1f))
assertThat(values.size).isEqualTo(3)
assertThat(values[0])
.isEqualTo(
EMPHASIZED_ACCELERATE.getInterpolation(
animValue(0f, LOCKSCREEN_TRANSLATION_Y)
) * pixels
)
assertThat(values[1])
.isEqualTo(
EMPHASIZED_ACCELERATE.getInterpolation(
animValue(0.3f, LOCKSCREEN_TRANSLATION_Y)
) * pixels
)
assertThat(values[2])
.isEqualTo(
EMPHASIZED_ACCELERATE.getInterpolation(
animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
) * pixels
)
job.cancel()
}
private fun animValue(stepValue: Float, params: AnimationParams): Float {
val totalDuration = TO_DREAMING_DURATION
val startValue = (params.startTime / totalDuration).toFloat()
val multiplier = (totalDuration / params.duration).toFloat()
return (stepValue - startValue) * multiplier
}
private fun step(value: Float): TransitionStep {
return TransitionStep(
from = KeyguardState.GONE,
to = KeyguardState.DREAMING,
value = value,
transitionState = TransitionState.RUNNING,
ownerName = "GoneToDreamingTransitionViewModelTest"
)
}
}

View File

@@ -107,6 +107,7 @@ import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteracto
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
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;
@@ -298,6 +299,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
@Mock private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
@Mock private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
@Mock private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock private CoroutineDispatcher mMainDispatcher;
@@ -522,6 +524,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mDreamingToLockscreenTransitionViewModel,
mOccludedToLockscreenTransitionViewModel,
mLockscreenToDreamingTransitionViewModel,
mGoneToDreamingTransitionViewModel,
mLockscreenToOccludedTransitionViewModel,
mMainDispatcher,
mKeyguardTransitionInteractor,