Merge changes from topic "low_light_animations" into tm-qpr-dev am: b85665b316
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21456149 Change-Id: I148cf37ec79c3deda9c6d411bff4b8498e204cd3 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -72,6 +72,7 @@ public final class LowLightDreamManager {
|
||||
public static final int AMBIENT_LIGHT_MODE_LOW_LIGHT = 2;
|
||||
|
||||
private final DreamManager mDreamManager;
|
||||
private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
|
||||
|
||||
@Nullable
|
||||
private final ComponentName mLowLightDreamComponent;
|
||||
@@ -81,8 +82,10 @@ public final class LowLightDreamManager {
|
||||
@Inject
|
||||
public LowLightDreamManager(
|
||||
DreamManager dreamManager,
|
||||
LowLightTransitionCoordinator lowLightTransitionCoordinator,
|
||||
@Named(LOW_LIGHT_DREAM_COMPONENT) @Nullable ComponentName lowLightDreamComponent) {
|
||||
mDreamManager = dreamManager;
|
||||
mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
|
||||
mLowLightDreamComponent = lowLightDreamComponent;
|
||||
}
|
||||
|
||||
@@ -111,7 +114,9 @@ public final class LowLightDreamManager {
|
||||
|
||||
mAmbientLightMode = ambientLightMode;
|
||||
|
||||
mDreamManager.setSystemDreamComponent(mAmbientLightMode == AMBIENT_LIGHT_MODE_LOW_LIGHT
|
||||
? mLowLightDreamComponent : null);
|
||||
boolean shouldEnterLowLight = mAmbientLightMode == AMBIENT_LIGHT_MODE_LOW_LIGHT;
|
||||
mLowLightTransitionCoordinator.notifyBeforeLowLightTransition(shouldEnterLowLight,
|
||||
() -> mDreamManager.setSystemDreamComponent(
|
||||
shouldEnterLowLight ? mLowLightDreamComponent : null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.dream.lowlight;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Helper class that allows listening and running animations before entering or exiting low light.
|
||||
*/
|
||||
@Singleton
|
||||
public class LowLightTransitionCoordinator {
|
||||
/**
|
||||
* Listener that is notified before low light entry.
|
||||
*/
|
||||
public interface LowLightEnterListener {
|
||||
/**
|
||||
* Callback that is notified before the device enters low light.
|
||||
*
|
||||
* @return an optional animator that will be waited upon before entering low light.
|
||||
*/
|
||||
Animator onBeforeEnterLowLight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener that is notified before low light exit.
|
||||
*/
|
||||
public interface LowLightExitListener {
|
||||
/**
|
||||
* Callback that is notified before the device exits low light.
|
||||
*
|
||||
* @return an optional animator that will be waited upon before exiting low light.
|
||||
*/
|
||||
Animator onBeforeExitLowLight();
|
||||
}
|
||||
|
||||
private LowLightEnterListener mLowLightEnterListener;
|
||||
private LowLightExitListener mLowLightExitListener;
|
||||
|
||||
@Inject
|
||||
public LowLightTransitionCoordinator() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener for the low light enter event.
|
||||
*
|
||||
* Only one listener can be set at a time. This method will overwrite any previously set
|
||||
* listener. Null can be used to unset the listener.
|
||||
*/
|
||||
public void setLowLightEnterListener(@Nullable LowLightEnterListener lowLightEnterListener) {
|
||||
mLowLightEnterListener = lowLightEnterListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener for the low light exit event.
|
||||
*
|
||||
* Only one listener can be set at a time. This method will overwrite any previously set
|
||||
* listener. Null can be used to unset the listener.
|
||||
*/
|
||||
public void setLowLightExitListener(@Nullable LowLightExitListener lowLightExitListener) {
|
||||
mLowLightExitListener = lowLightExitListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies listeners that the device is about to enter or exit low light.
|
||||
*
|
||||
* @param entering true if listeners should be notified before entering low light, false if this
|
||||
* is notifying before exiting.
|
||||
* @param callback callback that will be run after listeners complete.
|
||||
*/
|
||||
void notifyBeforeLowLightTransition(boolean entering, Runnable callback) {
|
||||
Animator animator = null;
|
||||
|
||||
if (entering && mLowLightEnterListener != null) {
|
||||
animator = mLowLightEnterListener.onBeforeEnterLowLight();
|
||||
} else if (!entering && mLowLightExitListener != null) {
|
||||
animator = mLowLightExitListener.onBeforeExitLowLight();
|
||||
}
|
||||
|
||||
// If the listener returned an animator to indicate it was running an animation, run the
|
||||
// callback after the animation completes, otherwise call the callback directly.
|
||||
if (animator != null) {
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
callback.run();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,10 @@ import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE
|
||||
import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_UNKNOWN;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@@ -43,45 +46,53 @@ public class LowLightDreamManagerTest {
|
||||
@Mock
|
||||
private DreamManager mDreamManager;
|
||||
|
||||
@Mock
|
||||
private LowLightTransitionCoordinator mTransitionCoordinator;
|
||||
|
||||
@Mock
|
||||
private ComponentName mDreamComponent;
|
||||
|
||||
LowLightDreamManager mLowLightDreamManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
// Automatically run any provided Runnable to mTransitionCoordinator to simplify testing.
|
||||
doAnswer(invocation -> {
|
||||
((Runnable) invocation.getArgument(1)).run();
|
||||
return null;
|
||||
}).when(mTransitionCoordinator).notifyBeforeLowLightTransition(anyBoolean(),
|
||||
any(Runnable.class));
|
||||
|
||||
mLowLightDreamManager = new LowLightDreamManager(mDreamManager, mTransitionCoordinator,
|
||||
mDreamComponent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAmbientLightMode_lowLight_setSystemDream() {
|
||||
final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
|
||||
mDreamComponent);
|
||||
|
||||
lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
|
||||
mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
|
||||
|
||||
verify(mTransitionCoordinator).notifyBeforeLowLightTransition(eq(true), any());
|
||||
verify(mDreamManager).setSystemDreamComponent(mDreamComponent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAmbientLightMode_regularLight_clearSystemDream() {
|
||||
final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
|
||||
mDreamComponent);
|
||||
|
||||
lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_REGULAR);
|
||||
mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_REGULAR);
|
||||
|
||||
verify(mTransitionCoordinator).notifyBeforeLowLightTransition(eq(false), any());
|
||||
verify(mDreamManager).setSystemDreamComponent(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAmbientLightMode_defaultUnknownMode_clearSystemDream() {
|
||||
final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
|
||||
mDreamComponent);
|
||||
|
||||
// Set to low light first.
|
||||
lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
|
||||
mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
|
||||
clearInvocations(mDreamManager);
|
||||
|
||||
// Return to default unknown mode.
|
||||
lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_UNKNOWN);
|
||||
mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_UNKNOWN);
|
||||
|
||||
verify(mDreamManager).setSystemDreamComponent(null);
|
||||
}
|
||||
@@ -89,7 +100,7 @@ public class LowLightDreamManagerTest {
|
||||
@Test
|
||||
public void setAmbientLightMode_dreamComponentNotSet_doNothing() {
|
||||
final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
|
||||
null /*dream component*/);
|
||||
mTransitionCoordinator, null /*dream component*/);
|
||||
|
||||
lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.dream.lowlight;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
public class LowLightTransitionCoordinatorTest {
|
||||
@Mock
|
||||
private LowLightTransitionCoordinator.LowLightEnterListener mEnterListener;
|
||||
|
||||
@Mock
|
||||
private LowLightTransitionCoordinator.LowLightExitListener mExitListener;
|
||||
|
||||
@Mock
|
||||
private Animator mAnimator;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<Animator.AnimatorListener> mAnimatorListenerCaptor;
|
||||
|
||||
@Mock
|
||||
private Runnable mRunnable;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onEnterCalledOnListeners() {
|
||||
LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
|
||||
|
||||
coordinator.setLowLightEnterListener(mEnterListener);
|
||||
|
||||
coordinator.notifyBeforeLowLightTransition(true, mRunnable);
|
||||
|
||||
verify(mEnterListener).onBeforeEnterLowLight();
|
||||
verify(mRunnable).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onExitCalledOnListeners() {
|
||||
LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
|
||||
|
||||
coordinator.setLowLightExitListener(mExitListener);
|
||||
|
||||
coordinator.notifyBeforeLowLightTransition(false, mRunnable);
|
||||
|
||||
verify(mExitListener).onBeforeExitLowLight();
|
||||
verify(mRunnable).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listenerNotCalledAfterRemoval() {
|
||||
LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
|
||||
|
||||
coordinator.setLowLightEnterListener(mEnterListener);
|
||||
coordinator.setLowLightEnterListener(null);
|
||||
|
||||
coordinator.notifyBeforeLowLightTransition(true, mRunnable);
|
||||
|
||||
verifyZeroInteractions(mEnterListener);
|
||||
verify(mRunnable).run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runnableCalledAfterAnimationEnds() {
|
||||
when(mEnterListener.onBeforeEnterLowLight()).thenReturn(mAnimator);
|
||||
|
||||
LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
|
||||
coordinator.setLowLightEnterListener(mEnterListener);
|
||||
|
||||
coordinator.notifyBeforeLowLightTransition(true, mRunnable);
|
||||
|
||||
// Animator listener is added and the runnable is not run yet.
|
||||
verify(mAnimator).addListener(mAnimatorListenerCaptor.capture());
|
||||
verifyZeroInteractions(mRunnable);
|
||||
|
||||
// Runnable is run once the animation ends.
|
||||
mAnimatorListenerCaptor.getValue().onAnimationEnd(null);
|
||||
verify(mRunnable).run();
|
||||
}
|
||||
}
|
||||
@@ -774,7 +774,7 @@
|
||||
<!-- Duration in milliseconds of the dream in complications fade-in animation. -->
|
||||
<integer name="config_dreamOverlayInComplicationsDurationMs">250</integer>
|
||||
<!-- Duration in milliseconds of the y-translation animation when entering a dream -->
|
||||
<integer name="config_dreamOverlayInTranslationYDurationMs">917</integer>
|
||||
<integer name="config_dreamOverlayInTranslationYDurationMs">1167</integer>
|
||||
|
||||
<!-- Delay in milliseconds before switching to the dock user and dreaming if a secondary user is
|
||||
active when the device is locked and docked. 0 indicates disabled. Default is 1 minute. -->
|
||||
|
||||
@@ -43,7 +43,6 @@ import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -131,9 +130,17 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts the dream content and dream overlay entry animations. */
|
||||
/**
|
||||
* Starts the dream content and dream overlay entry animations.
|
||||
*
|
||||
* @param downwards if true, the entry animation translations downwards into position rather
|
||||
* than upwards.
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun startEntryAnimations(animatorBuilder: () -> AnimatorSet = { AnimatorSet() }) {
|
||||
fun startEntryAnimations(
|
||||
downwards: Boolean,
|
||||
animatorBuilder: () -> AnimatorSet = { AnimatorSet() }
|
||||
) {
|
||||
cancelAnimations()
|
||||
|
||||
mAnimator =
|
||||
@@ -153,7 +160,7 @@ constructor(
|
||||
interpolator = Interpolators.LINEAR
|
||||
),
|
||||
translationYAnimator(
|
||||
from = mDreamInTranslationYDistance.toFloat(),
|
||||
from = mDreamInTranslationYDistance.toFloat() * (if (downwards) -1 else 1),
|
||||
to = 0f,
|
||||
durationMs = mDreamInTranslationYDurationMs,
|
||||
interpolator = Interpolators.EMPHASIZED_DECELERATE
|
||||
@@ -167,6 +174,71 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the dream content and dream overlay exit animations.
|
||||
*
|
||||
* This should only be used when the low light dream is entering, animations to/from other SysUI
|
||||
* views is controlled by `transitionViewModel`.
|
||||
*/
|
||||
// TODO(b/256916668): integrate with the keyguard transition model once dream surfaces work is
|
||||
// done.
|
||||
@JvmOverloads
|
||||
fun startExitAnimations(animatorBuilder: () -> AnimatorSet = { AnimatorSet() }): Animator {
|
||||
cancelAnimations()
|
||||
|
||||
mAnimator =
|
||||
animatorBuilder().apply {
|
||||
playTogether(
|
||||
translationYAnimator(
|
||||
from = 0f,
|
||||
to = -mDreamInTranslationYDistance.toFloat(),
|
||||
durationMs = mDreamInTranslationYDurationMs,
|
||||
delayMs = 0,
|
||||
interpolator = Interpolators.EMPHASIZED
|
||||
),
|
||||
alphaAnimator(
|
||||
from =
|
||||
mCurrentAlphaAtPosition.getOrDefault(
|
||||
key = POSITION_BOTTOM,
|
||||
defaultValue = 1f
|
||||
),
|
||||
to = 0f,
|
||||
durationMs = mDreamInComplicationsAnimDurationMs,
|
||||
delayMs = 0,
|
||||
positions = POSITION_BOTTOM
|
||||
)
|
||||
.apply {
|
||||
doOnEnd {
|
||||
// The logical end of the animation is once the alpha and blur
|
||||
// animations finish, end the animation so that any listeners are
|
||||
// notified. The Y translation animation is much longer than all of
|
||||
// the other animations due to how the spec is defined, but is not
|
||||
// expected to run to completion.
|
||||
mAnimator?.end()
|
||||
}
|
||||
},
|
||||
alphaAnimator(
|
||||
from =
|
||||
mCurrentAlphaAtPosition.getOrDefault(
|
||||
key = POSITION_TOP,
|
||||
defaultValue = 1f
|
||||
),
|
||||
to = 0f,
|
||||
durationMs = mDreamInComplicationsAnimDurationMs,
|
||||
delayMs = 0,
|
||||
positions = POSITION_TOP
|
||||
)
|
||||
)
|
||||
doOnEnd {
|
||||
mAnimator = null
|
||||
mOverlayStateController.setExitAnimationsRunning(false)
|
||||
}
|
||||
start()
|
||||
}
|
||||
mOverlayStateController.setExitAnimationsRunning(true)
|
||||
return mAnimator as AnimatorSet
|
||||
}
|
||||
|
||||
/** Starts the dream content and dream overlay exit animations. */
|
||||
fun wakeUp(doneCallback: Runnable, executor: DelayableExecutor) {
|
||||
cancelAnimations()
|
||||
@@ -182,19 +254,6 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the dream content and dream overlay animations, if they're currently running.
|
||||
*
|
||||
* @see [AnimatorSet.end]
|
||||
*/
|
||||
fun endAnimations() {
|
||||
mAnimator =
|
||||
mAnimator?.let {
|
||||
it.end()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun blurAnimator(
|
||||
view: View,
|
||||
fromBlurRadius: Float,
|
||||
|
||||
@@ -23,6 +23,7 @@ import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
|
||||
import static com.android.systemui.dreams.complication.ComplicationLayoutParams.POSITION_BOTTOM;
|
||||
import static com.android.systemui.dreams.complication.ComplicationLayoutParams.POSITION_TOP;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Handler;
|
||||
import android.util.MathUtils;
|
||||
@@ -31,6 +32,7 @@ import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.dream.lowlight.LowLightTransitionCoordinator;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.animation.Interpolators;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
@@ -54,11 +56,14 @@ import javax.inject.Named;
|
||||
* View controller for {@link DreamOverlayContainerView}.
|
||||
*/
|
||||
@DreamOverlayComponent.DreamOverlayScope
|
||||
public class DreamOverlayContainerViewController extends ViewController<DreamOverlayContainerView> {
|
||||
public class DreamOverlayContainerViewController extends
|
||||
ViewController<DreamOverlayContainerView> implements
|
||||
LowLightTransitionCoordinator.LowLightEnterListener {
|
||||
private final DreamOverlayStatusBarViewController mStatusBarViewController;
|
||||
private final BlurUtils mBlurUtils;
|
||||
private final DreamOverlayAnimationsController mDreamOverlayAnimationsController;
|
||||
private final DreamOverlayStateController mStateController;
|
||||
private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
|
||||
|
||||
private final ComplicationHostViewController mComplicationHostViewController;
|
||||
|
||||
@@ -143,19 +148,18 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
};
|
||||
|
||||
/**
|
||||
* If true, overlay entry animations should be skipped once.
|
||||
*
|
||||
* This is turned on when exiting low light and should be turned off once the entry animations
|
||||
* are skipped once.
|
||||
* If {@code true}, the dream has just transitioned from the low light dream back to the user
|
||||
* dream and we should play an entry animation where the overlay slides in downwards from the
|
||||
* top instead of the typicla slide in upwards from the bottom.
|
||||
*/
|
||||
private boolean mSkipEntryAnimations;
|
||||
private boolean mExitingLowLight;
|
||||
|
||||
private final DreamOverlayStateController.Callback
|
||||
mDreamOverlayStateCallback =
|
||||
new DreamOverlayStateController.Callback() {
|
||||
@Override
|
||||
public void onExitLowLight() {
|
||||
mSkipEntryAnimations = true;
|
||||
mExitingLowLight = true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -165,6 +169,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
ComplicationHostViewController complicationHostViewController,
|
||||
@Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
|
||||
DreamOverlayStatusBarViewController statusBarViewController,
|
||||
LowLightTransitionCoordinator lowLightTransitionCoordinator,
|
||||
BlurUtils blurUtils,
|
||||
@Main Handler handler,
|
||||
@Main Resources resources,
|
||||
@@ -182,6 +187,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
mBlurUtils = blurUtils;
|
||||
mDreamOverlayAnimationsController = animationsController;
|
||||
mStateController = stateController;
|
||||
mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
|
||||
|
||||
mBouncerlessScrimController = bouncerlessScrimController;
|
||||
mBouncerlessScrimController.addCallback(mBouncerlessExpansionCallback);
|
||||
@@ -208,6 +214,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
mStatusBarViewController.init();
|
||||
mComplicationHostViewController.init();
|
||||
mDreamOverlayAnimationsController.init(mView);
|
||||
mLowLightTransitionCoordinator.setLowLightEnterListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -219,14 +226,10 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
|
||||
// Start dream entry animations. Skip animations for low light clock.
|
||||
if (!mStateController.isLowLightActive()) {
|
||||
mDreamOverlayAnimationsController.startEntryAnimations();
|
||||
|
||||
if (mSkipEntryAnimations) {
|
||||
// If we're transitioning from the low light dream back to the user dream, skip the
|
||||
// overlay animations and show immediately.
|
||||
mDreamOverlayAnimationsController.endAnimations();
|
||||
mSkipEntryAnimations = false;
|
||||
}
|
||||
// If this is transitioning from the low light dream to the user dream, the overlay
|
||||
// should translate in downwards instead of upwards.
|
||||
mDreamOverlayAnimationsController.startEntryAnimations(mExitingLowLight);
|
||||
mExitingLowLight = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,4 +313,12 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
|
||||
|
||||
mDreamOverlayAnimationsController.wakeUp(onAnimationEnd, callbackExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animator onBeforeEnterLowLight() {
|
||||
// Return the animator so that the transition coordinator waits for the overlay exit
|
||||
// animations to finish before entering low light, as otherwise the default DreamActivity
|
||||
// animation plays immediately and there's no time for this animation to play.
|
||||
return mDreamOverlayAnimationsController.startExitAnimations();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.android.systemui.dreams
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ValueAnimator
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.view.View
|
||||
import androidx.test.filters.SmallTest
|
||||
@@ -10,13 +12,16 @@ import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransition
|
||||
import com.android.systemui.statusbar.BlurUtils
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import com.android.systemui.util.mockito.argumentCaptor
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.anyLong
|
||||
import org.mockito.Mockito.eq
|
||||
@@ -70,6 +75,19 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
|
||||
runBlocking(Dispatchers.Main.immediate) { controller.init(mockView) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExitAnimationUpdatesState() {
|
||||
controller.startExitAnimations(animatorBuilder = { mockAnimator })
|
||||
|
||||
verify(stateController).setExitAnimationsRunning(true)
|
||||
|
||||
val captor = argumentCaptor<Animator.AnimatorListener>()
|
||||
verify(mockAnimator).addListener(captor.capture())
|
||||
|
||||
captor.value.onAnimationEnd(mockAnimator)
|
||||
verify(stateController).setExitAnimationsRunning(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testWakeUpCallsExecutor() {
|
||||
val mockExecutor: DelayableExecutor = mock()
|
||||
@@ -87,7 +105,7 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
|
||||
fun testWakeUpAfterStartWillCancel() {
|
||||
val mockStartAnimator: AnimatorSet = mock()
|
||||
|
||||
controller.startEntryAnimations(animatorBuilder = { mockStartAnimator })
|
||||
controller.startEntryAnimations(false, animatorBuilder = { mockStartAnimator })
|
||||
|
||||
verify(mockStartAnimator, never()).cancel()
|
||||
|
||||
@@ -100,4 +118,50 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
|
||||
// animator.
|
||||
verify(mockStartAnimator, times(1)).cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEntryAnimations_translatesUpwards() {
|
||||
val mockStartAnimator: AnimatorSet = mock()
|
||||
|
||||
controller.startEntryAnimations(
|
||||
/* downwards= */ false,
|
||||
animatorBuilder = { mockStartAnimator }
|
||||
)
|
||||
|
||||
val animatorCaptor = ArgumentCaptor.forClass(Animator::class.java)
|
||||
verify(mockStartAnimator).playTogether(animatorCaptor.capture())
|
||||
|
||||
// Check if there's a ValueAnimator starting at the expected Y distance.
|
||||
val animators: List<ValueAnimator> = animatorCaptor.allValues as List<ValueAnimator>
|
||||
assertTrue(
|
||||
animators.any {
|
||||
// Call setCurrentFraction so the animated value jumps to the initial value.
|
||||
it.setCurrentFraction(0f)
|
||||
it.animatedValue == DREAM_IN_TRANSLATION_Y_DISTANCE.toFloat()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEntryAnimations_translatesDownwards() {
|
||||
val mockStartAnimator: AnimatorSet = mock()
|
||||
|
||||
controller.startEntryAnimations(
|
||||
/* downwards= */ true,
|
||||
animatorBuilder = { mockStartAnimator }
|
||||
)
|
||||
|
||||
val animatorCaptor = ArgumentCaptor.forClass(Animator::class.java)
|
||||
verify(mockStartAnimator).playTogether(animatorCaptor.capture())
|
||||
|
||||
// Check if there's a ValueAnimator starting at the expected Y distance.
|
||||
val animators: List<ValueAnimator> = animatorCaptor.allValues as List<ValueAnimator>
|
||||
assertTrue(
|
||||
animators.any {
|
||||
// Call setCurrentFraction so the animated value jumps to the initial value.
|
||||
it.setCurrentFraction(0f)
|
||||
it.animatedValue == -DREAM_IN_TRANSLATION_Y_DISTANCE.toFloat()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.dreams;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyFloat;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
@@ -33,6 +34,7 @@ import android.view.ViewTreeObserver;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.dream.lowlight.LowLightTransitionCoordinator;
|
||||
import com.android.keyguard.BouncerPanelExpansionCalculator;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.dreams.complication.ComplicationHostViewController;
|
||||
@@ -64,6 +66,9 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
||||
@Mock
|
||||
DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController;
|
||||
|
||||
@Mock
|
||||
LowLightTransitionCoordinator mLowLightTransitionCoordinator;
|
||||
|
||||
@Mock
|
||||
DreamOverlayContainerView mDreamOverlayContainerView;
|
||||
|
||||
@@ -109,6 +114,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
||||
mComplicationHostViewController,
|
||||
mDreamOverlayContentView,
|
||||
mDreamOverlayStatusBarViewController,
|
||||
mLowLightTransitionCoordinator,
|
||||
mBlurUtils,
|
||||
mHandler,
|
||||
mResources,
|
||||
@@ -200,7 +206,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
||||
|
||||
mController.onViewAttached();
|
||||
|
||||
verify(mAnimationsController).startEntryAnimations();
|
||||
verify(mAnimationsController).startEntryAnimations(false);
|
||||
verify(mAnimationsController, never()).cancelAnimations();
|
||||
}
|
||||
|
||||
@@ -210,11 +216,11 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
||||
|
||||
mController.onViewAttached();
|
||||
|
||||
verify(mAnimationsController, never()).startEntryAnimations();
|
||||
verify(mAnimationsController, never()).startEntryAnimations(anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipEntryAnimationsWhenExitingLowLight() {
|
||||
public void testDownwardEntryAnimationsWhenExitingLowLight() {
|
||||
ArgumentCaptor<DreamOverlayStateController.Callback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
|
||||
when(mStateController.isLowLightActive()).thenReturn(false);
|
||||
@@ -230,8 +236,14 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
|
||||
mController.onViewAttached();
|
||||
|
||||
// Entry animations should be started then immediately ended to skip to the end.
|
||||
verify(mAnimationsController).startEntryAnimations();
|
||||
verify(mAnimationsController).endAnimations();
|
||||
verify(mAnimationsController).startEntryAnimations(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartsExitAnimationsBeforeEnteringLowLight() {
|
||||
mController.onBeforeEnterLowLight();
|
||||
|
||||
verify(mAnimationsController).startExitAnimations();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user