[DO NOT MERGE] OCCLUDED->LOCKSCREEN transition
Follow the same path as DREAMING->LOCKSCREEN, do a subtle fade and y translation upon unoccluding. Allow coroutinecontext override for collectFlow, to optimize path for transition steps. Gated behind existing flag (223). Test: atest OccludedToLockscreenTransitionViewModelTest NotificationPanelViewControllerTest Bug: 195430376 Change-Id: I3e031d7b2f72c43f5d9a93954e983c7ff6c34270
This commit is contained in:
@@ -1275,6 +1275,9 @@
|
|||||||
<!-- DREAMING -> LOCKSCREEN transition: Amount to shift lockscreen content on entering -->
|
<!-- DREAMING -> LOCKSCREEN transition: Amount to shift lockscreen content on entering -->
|
||||||
<dimen name="dreaming_to_lockscreen_transition_lockscreen_translation_y">40dp</dimen>
|
<dimen name="dreaming_to_lockscreen_transition_lockscreen_translation_y">40dp</dimen>
|
||||||
|
|
||||||
|
<!-- OCCLUDED -> LOCKSCREEN transition: Amount to shift lockscreen content on entering -->
|
||||||
|
<dimen name="occluded_to_lockscreen_transition_lockscreen_translation_y">40dp</dimen>
|
||||||
|
|
||||||
<!-- The amount of vertical offset for the keyguard during the full shade transition. -->
|
<!-- The amount of vertical offset for the keyguard during the full shade transition. -->
|
||||||
<dimen name="lockscreen_shade_keyguard_transition_vertical_offset">0dp</dimen>
|
<dimen name="lockscreen_shade_keyguard_transition_vertical_offset">0dp</dimen>
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ constructor(
|
|||||||
name,
|
name,
|
||||||
KeyguardState.OCCLUDED,
|
KeyguardState.OCCLUDED,
|
||||||
KeyguardState.LOCKSCREEN,
|
KeyguardState.LOCKSCREEN,
|
||||||
getAnimator(),
|
getAnimator(TO_LOCKSCREEN_DURATION),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -97,6 +97,6 @@ constructor(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val DEFAULT_DURATION = 500.milliseconds
|
private val DEFAULT_DURATION = 500.milliseconds
|
||||||
val TO_LOCKSCREEN_DURATION = 1183.milliseconds
|
val TO_LOCKSCREEN_DURATION = 933.milliseconds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ 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.AOD
|
||||||
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
|
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
|
||||||
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
|
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
|
import com.android.systemui.keyguard.shared.model.TransitionState
|
||||||
|
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
|
||||||
import com.android.systemui.keyguard.shared.model.TransitionStep
|
import com.android.systemui.keyguard.shared.model.TransitionStep
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
@@ -50,6 +52,10 @@ constructor(
|
|||||||
val dreamingToLockscreenTransition: Flow<TransitionStep> =
|
val dreamingToLockscreenTransition: Flow<TransitionStep> =
|
||||||
repository.transition(DREAMING, LOCKSCREEN)
|
repository.transition(DREAMING, LOCKSCREEN)
|
||||||
|
|
||||||
|
/** OCCLUDED->LOCKSCREEN transition information. */
|
||||||
|
val occludedToLockscreenTransition: Flow<TransitionStep> =
|
||||||
|
repository.transition(OCCLUDED, LOCKSCREEN)
|
||||||
|
|
||||||
/** (any)->AOD transition information */
|
/** (any)->AOD transition information */
|
||||||
val anyStateToAodTransition: Flow<TransitionStep> =
|
val anyStateToAodTransition: Flow<TransitionStep> =
|
||||||
repository.transitions.filter { step -> step.to == KeyguardState.AOD }
|
repository.transitions.filter { step -> step.to == KeyguardState.AOD }
|
||||||
@@ -93,7 +99,14 @@ constructor(
|
|||||||
val start = (params.startTime / totalDuration).toFloat()
|
val start = (params.startTime / totalDuration).toFloat()
|
||||||
val chunks = (totalDuration / params.duration).toFloat()
|
val chunks = (totalDuration / params.duration).toFloat()
|
||||||
return flow
|
return flow
|
||||||
.map { step -> (step.value - start) * chunks }
|
// When starting, emit a value of 0f to give animations a chance to set initial state
|
||||||
|
.map { step ->
|
||||||
|
if (step.transitionState == STARTED) {
|
||||||
|
0f
|
||||||
|
} else {
|
||||||
|
(step.value - start) * chunks
|
||||||
|
}
|
||||||
|
}
|
||||||
.filter { value -> value >= 0f && value <= 1f }
|
.filter { value -> value >= 0f && value <= 1f }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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_DECELERATE
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
|
||||||
|
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||||
|
import com.android.systemui.keyguard.shared.model.AnimationParams
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Breaks down OCCLUDED->LOCKSCREEN transition into discrete steps for corresponding views to
|
||||||
|
* consume.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class OccludedToLockscreenTransitionViewModel
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val interactor: KeyguardTransitionInteractor,
|
||||||
|
) {
|
||||||
|
/** Lockscreen views y-translation */
|
||||||
|
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
|
||||||
|
return flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { value ->
|
||||||
|
-translatePx + (EMPHASIZED_DECELERATE.getInterpolation(value) * translatePx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Lockscreen views alpha */
|
||||||
|
val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA)
|
||||||
|
|
||||||
|
private fun flowForAnimation(params: AnimationParams): Flow<Float> {
|
||||||
|
return interactor.transitionStepAnimation(
|
||||||
|
interactor.occludedToLockscreenTransition,
|
||||||
|
params,
|
||||||
|
totalDuration = TO_LOCKSCREEN_DURATION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmField val LOCKSCREEN_ANIMATION_DURATION_MS = TO_LOCKSCREEN_DURATION.inWholeMilliseconds
|
||||||
|
val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = TO_LOCKSCREEN_DURATION)
|
||||||
|
val LOCKSCREEN_ALPHA =
|
||||||
|
AnimationParams(startTime = 233.milliseconds, duration = 250.milliseconds)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -144,6 +144,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState;
|
|||||||
import com.android.systemui.keyguard.shared.model.TransitionStep;
|
import com.android.systemui.keyguard.shared.model.TransitionStep;
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
|
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
|
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
|
||||||
|
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
|
||||||
import com.android.systemui.media.controls.pipeline.MediaDataManager;
|
import com.android.systemui.media.controls.pipeline.MediaDataManager;
|
||||||
import com.android.systemui.media.controls.ui.KeyguardMediaController;
|
import com.android.systemui.media.controls.ui.KeyguardMediaController;
|
||||||
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
|
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
|
||||||
@@ -238,6 +239,8 @@ import java.util.function.Consumer;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher;
|
||||||
|
|
||||||
@CentralSurfacesComponent.CentralSurfacesScope
|
@CentralSurfacesComponent.CentralSurfacesScope
|
||||||
public final class NotificationPanelViewController implements Dumpable {
|
public final class NotificationPanelViewController implements Dumpable {
|
||||||
|
|
||||||
@@ -683,10 +686,13 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
private boolean mIgnoreXTouchSlop;
|
private boolean mIgnoreXTouchSlop;
|
||||||
private boolean mExpandLatencyTracking;
|
private boolean mExpandLatencyTracking;
|
||||||
private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
|
private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
|
||||||
|
private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
|
||||||
|
|
||||||
private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
|
private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
|
||||||
private boolean mIsDreamToLockscreenTransitionRunning = false;
|
private CoroutineDispatcher mMainDispatcher;
|
||||||
|
private boolean mIsToLockscreenTransitionRunning = false;
|
||||||
private int mDreamingToLockscreenTransitionTranslationY;
|
private int mDreamingToLockscreenTransitionTranslationY;
|
||||||
|
private int mOccludedToLockscreenTransitionTranslationY;
|
||||||
private boolean mUnocclusionTransitionFlagEnabled = false;
|
private boolean mUnocclusionTransitionFlagEnabled = false;
|
||||||
|
|
||||||
private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */,
|
private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */,
|
||||||
@@ -705,7 +711,13 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
|
|
||||||
private final Consumer<TransitionStep> mDreamingToLockscreenTransition =
|
private final Consumer<TransitionStep> mDreamingToLockscreenTransition =
|
||||||
(TransitionStep step) -> {
|
(TransitionStep step) -> {
|
||||||
mIsDreamToLockscreenTransitionRunning =
|
mIsToLockscreenTransitionRunning =
|
||||||
|
step.getTransitionState() == TransitionState.RUNNING;
|
||||||
|
};
|
||||||
|
|
||||||
|
private final Consumer<TransitionStep> mOccludedToLockscreenTransition =
|
||||||
|
(TransitionStep step) -> {
|
||||||
|
mIsToLockscreenTransitionRunning =
|
||||||
step.getTransitionState() == TransitionState.RUNNING;
|
step.getTransitionState() == TransitionState.RUNNING;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -778,6 +790,8 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
KeyguardBottomAreaViewModel keyguardBottomAreaViewModel,
|
KeyguardBottomAreaViewModel keyguardBottomAreaViewModel,
|
||||||
KeyguardBottomAreaInteractor keyguardBottomAreaInteractor,
|
KeyguardBottomAreaInteractor keyguardBottomAreaInteractor,
|
||||||
DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel,
|
DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel,
|
||||||
|
OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel,
|
||||||
|
@Main CoroutineDispatcher mainDispatcher,
|
||||||
KeyguardTransitionInteractor keyguardTransitionInteractor,
|
KeyguardTransitionInteractor keyguardTransitionInteractor,
|
||||||
DumpManager dumpManager) {
|
DumpManager dumpManager) {
|
||||||
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
|
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
|
||||||
@@ -795,6 +809,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
mShadeHeightLogger = shadeHeightLogger;
|
mShadeHeightLogger = shadeHeightLogger;
|
||||||
mGutsManager = gutsManager;
|
mGutsManager = gutsManager;
|
||||||
mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
|
mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
|
||||||
|
mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel;
|
||||||
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
|
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
|
||||||
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
|
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -873,6 +888,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
mFalsingCollector = falsingCollector;
|
mFalsingCollector = falsingCollector;
|
||||||
mPowerManager = powerManager;
|
mPowerManager = powerManager;
|
||||||
mWakeUpCoordinator = coordinator;
|
mWakeUpCoordinator = coordinator;
|
||||||
|
mMainDispatcher = mainDispatcher;
|
||||||
mAccessibilityManager = accessibilityManager;
|
mAccessibilityManager = accessibilityManager;
|
||||||
mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
|
mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
|
||||||
setPanelAlpha(255, false /* animate */);
|
setPanelAlpha(255, false /* animate */);
|
||||||
@@ -1097,15 +1113,27 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
controller.setup(mNotificationContainerParent));
|
controller.setup(mNotificationContainerParent));
|
||||||
|
|
||||||
if (mUnocclusionTransitionFlagEnabled) {
|
if (mUnocclusionTransitionFlagEnabled) {
|
||||||
|
// Dreaming->Lockscreen
|
||||||
|
collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(),
|
||||||
|
mDreamingToLockscreenTransition, mMainDispatcher);
|
||||||
collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(),
|
collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(),
|
||||||
dreamingToLockscreenTransitionAlpha(mNotificationStackScrollLayoutController));
|
toLockscreenTransitionAlpha(mNotificationStackScrollLayoutController),
|
||||||
|
mMainDispatcher);
|
||||||
collectFlow(mView, mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY(
|
collectFlow(mView, mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY(
|
||||||
mDreamingToLockscreenTransitionTranslationY),
|
mDreamingToLockscreenTransitionTranslationY),
|
||||||
dreamingToLockscreenTransitionY(mNotificationStackScrollLayoutController));
|
toLockscreenTransitionY(mNotificationStackScrollLayoutController),
|
||||||
|
mMainDispatcher);
|
||||||
|
|
||||||
collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(),
|
// Occluded->Lockscreen
|
||||||
mDreamingToLockscreenTransition);
|
collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
|
||||||
|
mOccludedToLockscreenTransition, mMainDispatcher);
|
||||||
|
collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
|
||||||
|
toLockscreenTransitionAlpha(mNotificationStackScrollLayoutController),
|
||||||
|
mMainDispatcher);
|
||||||
|
collectFlow(mView, mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY(
|
||||||
|
mOccludedToLockscreenTransitionTranslationY),
|
||||||
|
toLockscreenTransitionY(mNotificationStackScrollLayoutController),
|
||||||
|
mMainDispatcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1143,6 +1171,8 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
R.dimen.split_shade_scrim_transition_distance);
|
R.dimen.split_shade_scrim_transition_distance);
|
||||||
mDreamingToLockscreenTransitionTranslationY = mResources.getDimensionPixelSize(
|
mDreamingToLockscreenTransitionTranslationY = mResources.getDimensionPixelSize(
|
||||||
R.dimen.dreaming_to_lockscreen_transition_lockscreen_translation_y);
|
R.dimen.dreaming_to_lockscreen_transition_lockscreen_translation_y);
|
||||||
|
mOccludedToLockscreenTransitionTranslationY = mResources.getDimensionPixelSize(
|
||||||
|
R.dimen.occluded_to_lockscreen_transition_lockscreen_translation_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
|
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
|
||||||
@@ -1806,7 +1836,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateClock() {
|
private void updateClock() {
|
||||||
if (mIsDreamToLockscreenTransitionRunning) {
|
if (mIsToLockscreenTransitionRunning) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha;
|
float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha;
|
||||||
@@ -2697,7 +2727,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
} else if (statusBarState == KEYGUARD
|
} else if (statusBarState == KEYGUARD
|
||||||
|| statusBarState == StatusBarState.SHADE_LOCKED) {
|
|| statusBarState == StatusBarState.SHADE_LOCKED) {
|
||||||
mKeyguardBottomArea.setVisibility(View.VISIBLE);
|
mKeyguardBottomArea.setVisibility(View.VISIBLE);
|
||||||
if (!mIsDreamToLockscreenTransitionRunning) {
|
if (!mIsToLockscreenTransitionRunning) {
|
||||||
mKeyguardBottomArea.setAlpha(1f);
|
mKeyguardBottomArea.setAlpha(1f);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -3566,7 +3596,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateNotificationTranslucency() {
|
private void updateNotificationTranslucency() {
|
||||||
if (mIsDreamToLockscreenTransitionRunning) {
|
if (mIsToLockscreenTransitionRunning) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float alpha = 1f;
|
float alpha = 1f;
|
||||||
@@ -3624,7 +3654,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateKeyguardBottomAreaAlpha() {
|
private void updateKeyguardBottomAreaAlpha() {
|
||||||
if (mIsDreamToLockscreenTransitionRunning) {
|
if (mIsToLockscreenTransitionRunning) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// There are two possible panel expansion behaviors:
|
// There are two possible panel expansion behaviors:
|
||||||
@@ -5856,7 +5886,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
mCurrentPanelState = state;
|
mCurrentPanelState = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Consumer<Float> dreamingToLockscreenTransitionAlpha(
|
private Consumer<Float> toLockscreenTransitionAlpha(
|
||||||
NotificationStackScrollLayoutController stackScroller) {
|
NotificationStackScrollLayoutController stackScroller) {
|
||||||
return (Float alpha) -> {
|
return (Float alpha) -> {
|
||||||
mKeyguardStatusViewController.setAlpha(alpha);
|
mKeyguardStatusViewController.setAlpha(alpha);
|
||||||
@@ -5874,7 +5904,7 @@ public final class NotificationPanelViewController implements Dumpable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Consumer<Float> dreamingToLockscreenTransitionY(
|
private Consumer<Float> toLockscreenTransitionY(
|
||||||
NotificationStackScrollLayoutController stackScroller) {
|
NotificationStackScrollLayoutController stackScroller) {
|
||||||
return (Float translationY) -> {
|
return (Float translationY) -> {
|
||||||
mKeyguardStatusViewController.setTranslationY(translationY, /* excludeMedia= */false);
|
mKeyguardStatusViewController.setTranslationY(translationY, /* excludeMedia= */false);
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import androidx.lifecycle.Lifecycle
|
|||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
|
|
||||||
@@ -34,7 +36,10 @@ fun <T> collectFlow(
|
|||||||
view: View,
|
view: View,
|
||||||
flow: Flow<T>,
|
flow: Flow<T>,
|
||||||
consumer: Consumer<T>,
|
consumer: Consumer<T>,
|
||||||
|
coroutineContext: CoroutineContext = EmptyCoroutineContext,
|
||||||
state: Lifecycle.State = Lifecycle.State.CREATED,
|
state: Lifecycle.State = Lifecycle.State.CREATED,
|
||||||
) {
|
) {
|
||||||
view.repeatWhenAttached { repeatOnLifecycle(state) { flow.collect { consumer.accept(it) } } }
|
view.repeatWhenAttached(coroutineContext) {
|
||||||
|
repeatOnLifecycle(state) { flow.collect { consumer.accept(it) } }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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_DECELERATE
|
||||||
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
|
||||||
|
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_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.OccludedToLockscreenTransitionViewModel.Companion.LOCKSCREEN_ALPHA
|
||||||
|
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel.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 OccludedToLockscreenTransitionViewModelTest : SysuiTestCase() {
|
||||||
|
private lateinit var underTest: OccludedToLockscreenTransitionViewModel
|
||||||
|
private lateinit var repository: FakeKeyguardTransitionRepository
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
repository = FakeKeyguardTransitionRepository()
|
||||||
|
val interactor = KeyguardTransitionInteractor(repository)
|
||||||
|
underTest = OccludedToLockscreenTransitionViewModel(interactor)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun lockscreenFadeIn() =
|
||||||
|
runTest(UnconfinedTestDispatcher()) {
|
||||||
|
val values = mutableListOf<Float>()
|
||||||
|
|
||||||
|
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
|
||||||
|
|
||||||
|
repository.sendTransitionStep(step(0f))
|
||||||
|
// Should start running here...
|
||||||
|
repository.sendTransitionStep(step(0.3f))
|
||||||
|
repository.sendTransitionStep(step(0.4f))
|
||||||
|
repository.sendTransitionStep(step(0.5f))
|
||||||
|
// ...up to here
|
||||||
|
repository.sendTransitionStep(step(0.6f))
|
||||||
|
repository.sendTransitionStep(step(1f))
|
||||||
|
|
||||||
|
// Only two 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(animValue(0.3f, LOCKSCREEN_ALPHA))
|
||||||
|
assertThat(values[1]).isEqualTo(animValue(0.4f, LOCKSCREEN_ALPHA))
|
||||||
|
assertThat(values[2]).isEqualTo(animValue(0.5f, 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)
|
||||||
|
|
||||||
|
repository.sendTransitionStep(step(0f))
|
||||||
|
repository.sendTransitionStep(step(0.3f))
|
||||||
|
repository.sendTransitionStep(step(0.5f))
|
||||||
|
repository.sendTransitionStep(step(1f))
|
||||||
|
|
||||||
|
assertThat(values.size).isEqualTo(4)
|
||||||
|
assertThat(values[0])
|
||||||
|
.isEqualTo(
|
||||||
|
-pixels +
|
||||||
|
EMPHASIZED_DECELERATE.getInterpolation(
|
||||||
|
animValue(0f, LOCKSCREEN_TRANSLATION_Y)
|
||||||
|
) * pixels
|
||||||
|
)
|
||||||
|
assertThat(values[1])
|
||||||
|
.isEqualTo(
|
||||||
|
-pixels +
|
||||||
|
EMPHASIZED_DECELERATE.getInterpolation(
|
||||||
|
animValue(0.3f, LOCKSCREEN_TRANSLATION_Y)
|
||||||
|
) * pixels
|
||||||
|
)
|
||||||
|
assertThat(values[2])
|
||||||
|
.isEqualTo(
|
||||||
|
-pixels +
|
||||||
|
EMPHASIZED_DECELERATE.getInterpolation(
|
||||||
|
animValue(0.5f, LOCKSCREEN_TRANSLATION_Y)
|
||||||
|
) * pixels
|
||||||
|
)
|
||||||
|
assertThat(values[3])
|
||||||
|
.isEqualTo(
|
||||||
|
-pixels +
|
||||||
|
EMPHASIZED_DECELERATE.getInterpolation(
|
||||||
|
animValue(1f, LOCKSCREEN_TRANSLATION_Y)
|
||||||
|
) * pixels
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun animValue(stepValue: Float, params: AnimationParams): Float {
|
||||||
|
val totalDuration = TO_LOCKSCREEN_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.OCCLUDED,
|
||||||
|
to = KeyguardState.LOCKSCREEN,
|
||||||
|
value = value,
|
||||||
|
transitionState = TransitionState.RUNNING,
|
||||||
|
ownerName = "OccludedToLockscreenTransitionViewModelTest"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -107,6 +107,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInterac
|
|||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
|
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
|
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
|
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
|
||||||
|
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
|
||||||
import com.android.systemui.media.controls.pipeline.MediaDataManager;
|
import com.android.systemui.media.controls.pipeline.MediaDataManager;
|
||||||
import com.android.systemui.media.controls.ui.KeyguardMediaController;
|
import com.android.systemui.media.controls.ui.KeyguardMediaController;
|
||||||
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
|
import com.android.systemui.media.controls.ui.MediaHierarchyManager;
|
||||||
@@ -188,6 +189,8 @@ import org.mockito.stubbing.Answer;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher;
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidTestingRunner.class)
|
@RunWith(AndroidTestingRunner.class)
|
||||||
@TestableLooper.RunWithLooper
|
@TestableLooper.RunWithLooper
|
||||||
@@ -289,7 +292,9 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
|
|||||||
@Mock private KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel;
|
@Mock private KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel;
|
||||||
@Mock private KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
|
@Mock private KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
|
||||||
@Mock private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
|
@Mock private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
|
||||||
|
@Mock private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
|
||||||
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
|
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
|
||||||
|
@Mock private CoroutineDispatcher mMainDispatcher;
|
||||||
@Mock private MotionEvent mDownMotionEvent;
|
@Mock private MotionEvent mDownMotionEvent;
|
||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener>
|
private ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener>
|
||||||
@@ -507,6 +512,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
|
|||||||
mKeyguardBottomAreaViewModel,
|
mKeyguardBottomAreaViewModel,
|
||||||
mKeyguardBottomAreaInteractor,
|
mKeyguardBottomAreaInteractor,
|
||||||
mDreamingToLockscreenTransitionViewModel,
|
mDreamingToLockscreenTransitionViewModel,
|
||||||
|
mOccludedToLockscreenTransitionViewModel,
|
||||||
|
mMainDispatcher,
|
||||||
mKeyguardTransitionInteractor,
|
mKeyguardTransitionInteractor,
|
||||||
mDumpManager);
|
mDumpManager);
|
||||||
mNotificationPanelViewController.initDependencies(
|
mNotificationPanelViewController.initDependencies(
|
||||||
|
|||||||
Reference in New Issue
Block a user